Java Swing实现五子棋程序
首先感谢08年MLDN出的这个培训视频,我把代码和文档整理了一下,发布出来给需要学习Swing的朋友。
源码地址:
https://gitee.com/indexman/gobang
一、知识点准备:
JFrame:窗体
JOptionPane:对话框
MouseListener:鼠标事件
Graphics:绘制二维图像
Thread:线程类
二、五子棋游戏的功能:

- 在点击鼠标时,可以在相应的位置显示棋子。
- 可以自动判断游戏是否结束,是否黑方或白方已经胜利
- 对游戏时间进行设置,判断是否超出规定时间
三、游戏开发步骤
1.开发游戏界面
计算棋盘中每一条线的间距:这里用的是19X19的围棋棋盘,
总宽度为360px, 分成18份,每份是20px
2.在棋盘上的鼠标点击位置,显示一个棋子
黑子:一个实心的黑圆表示
白子:一个空心的黑圆+一个实心的白圆表示
repaint() 方法表示重新执行一次paint()方法。
3.保存之前下过的棋子
通过一个二维数组来保存之前下过的所有棋子。
4.判断游戏胜负
依据 五子棋 的基本游戏规则,判断是否有同一颜色的棋子连城5个。
完成五子棋游戏的核心算法
这里可以把核心算法总结成一个灵活的方法。
提示信息的保存
5.处理屏幕闪烁问题
使用双缓冲技术
6.实现各个按钮的功能
开始游戏:重新开始游戏
游戏设置: 设置倒计时时间
游戏说明:用来说明游戏规则和操作
认输:某一方放弃游戏,投子认负
关于:显示游戏制作者
退出:退出程序
-----------------------------------------------------分割线------------------------------------------------------
四、代码部分
1.启动类
package com.laoxu.game;
import com.laoxu.game.view.GobangFrame;
import com.laoxu.game.view.MyFrame;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
GobangFrame frame = new GobangFrame();
//MyFrame myFrame = new MyFrame();
//JOptionPane
/*JOptionPane.showMessageDialog(myFrame,"我的信息");
int result = JOptionPane.showConfirmDialog(myFrame,"确定要开始游戏吗?","",0);
if(result==0){
JOptionPane.showMessageDialog(myFrame,"游戏开始");
}
if(result==1){
JOptionPane.showMessageDialog(myFrame,"游戏结束");
}
if(result==2){
JOptionPane.showMessageDialog(myFrame,"重新选择");
}
String username = JOptionPane.showInputDialog("请输入你的姓名:");
System.out.println(username);
JOptionPane.showMessageDialog(myFrame,"输入的姓名为:"+username);*/
//System.out.println(System.getProperty("user.dir")+"/src/image/background.jpg");
}
}
2.五子棋类
package com.laoxu.game.view;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* @description: 五子棋主界面
* @author: luohanye
* @create: 2019-03-21
**/
public class GobangFrame extends JFrame implements MouseListener,Runnable {
//获取当前平面分辨率,例如:1920x1080
int swidth = Toolkit.getDefaultToolkit().getScreenSize().width;
int sheight = Toolkit.getDefaultToolkit().getScreenSize().height;
BufferedImage image = null;
//保存坐标值
int x = 0;
int y = 0;
//保存下过的棋子 0-没有棋子 1-黑子 2-白子
int[][] allChess = new int[19][19];
// 标识当前应该是黑子还是白子
boolean isBlack = true;
// 控制游戏是否可以玩
boolean canPlay = true;
// 保存游戏信息
String message = "黑方先行";
// 保存最多拥有多少时间(秒)
int maxTime = 0;
// 做倒计时的线程类
Thread t = new Thread(this);
// 保存黑方与白方的剩余时间
int blackTime = 0;
int whiteTime = 0;
// 保存双方剩余时间的显示信息
String blackMessage = "无限制";
String whiteMessage = "无限制";
public GobangFrame() {
//设置标题
this.setTitle("五子棋");
int width = 500, height = 500;
this.setSize(width, height);
//设置窗体出现的位置
this.setLocation((swidth - width) / 2, (sheight - height) / 2);
//窗口大小不可变
this.setResizable(false);
//定义关闭动作
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//添加监听器
this.addMouseListener(this);
//窗体可见
this.setVisible(true);
//加载背景图片
try {
image = ImageIO.read(new File(System.getProperty("user.dir")+"/src/image/background.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
// 启动线程
t.start();
t.suspend();
// 刷新屏幕,防止开始游戏时出现无法显示的情况.
this.repaint();
}
public void paint(Graphics g) {
//双缓冲技术防止屏幕闪烁
BufferedImage bi = new BufferedImage(500,500,BufferedImage.TYPE_INT_RGB);
Graphics g2 = bi.createGraphics();
g2.setColor(Color.BLACK);
//绘制背景
g2.drawImage(image, 0, 20, this);
g2.setFont(new Font("黑体", Font.BOLD, 20));
g2.drawString("游戏信息:"+message, 130, 60);
//输出时间信息
g2.setFont(new Font("宋体", 0, 14));
g2.drawString("黑方时间:" + blackMessage, 30, 470);
g2.drawString("白方时间:" + whiteMessage, 260, 470);
//绘制棋盘
/**
* X=10 Y=70
* X=370 Y=430
* X=370 Y=70
* X=10 Y=430
*/
for (int i = 0; i < 19; i++) {
g2.drawLine(10, 70 + 20 * i, 370, 70 + 20 * i);
g2.drawLine(10 + 20 * i, 70, 10 + 20 * i, 430);
}
//标注点位
g2.fillOval(68, 128, 4, 4);
g2.fillOval(308, 128, 4, 4);
g2.fillOval(308, 368, 4, 4);
g2.fillOval(68, 368, 4, 4);
g2.fillOval(68, 248, 4, 4);
g2.fillOval(308, 248, 4, 4);
g2.fillOval(188, 128, 4, 4);
g2.fillOval(188, 368, 4, 4);
g2.fillOval(188, 248, 4, 4);
//绘制棋子
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
//黑子
if (allChess[i][j] == 1) {
int tempX = i * 20 + 10;
int tempY = j * 20 + 70;
g2.fillOval(tempX - 7, tempY - 7, 14, 14);
}
//白子
if (allChess[i][j] == 2) {
int tempX = i * 20 + 10;
int tempY = j * 20 + 70;
g2.setColor(Color.WHITE);
g2.fillOval(tempX - 7, tempY - 7, 14, 14);
g2.setColor(Color.BLACK);
g2.drawOval(tempX - 7, tempY - 7, 14, 14);
}
}
}
g.drawImage(bi,0,0,this);
}
//鼠标按键在组件上按下时调用。
@Override
public void mousePressed(MouseEvent e) {
System.out.println("X=" + e.getX() + " Y=" + e.getY());
if (canPlay) {
x = e.getX();
y = e.getY();
//判断点击是否在棋盘内
if (x >= 10 && x <= 370 && y >= 70 && y <= 430) {
x = (x - 10) / 20;
y = (y - 70) / 20;
if (allChess[x][y] == 0) {
if (isBlack) {
allChess[x][y] = 1;
isBlack = false;
message="轮到白方";
} else {
allChess[x][y] = 2;
isBlack = true;
message="轮到黑方";
}
//判断游戏是否结束
boolean isWin = this.checkWin();
if (isWin) {
JOptionPane.showMessageDialog(this, "游戏结束,"
+ (allChess[x][y] == 1 ? "黑方" : "白方") + "获胜!");
canPlay = false;
}
}
this.repaint();
}
}
// 点击 【开始游戏】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=70 && e.getY()<=100){
int result = JOptionPane.showConfirmDialog(this,"是否重新开始游戏?","",0);
if(result==0){
//1.清空棋盘
allChess = new int[19][19];
//另一种方式:
/*for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}*/
//2.重置游戏信息
message = "黑方先行";
//3.将下一步下棋的人改为黑方
isBlack = true;
//4.可以游戏标志改为true
canPlay = true;
//5.重置黑白双方时间限制
blackTime = maxTime;
whiteTime = maxTime;
if (maxTime > 0) {
blackMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
whiteMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
t.resume();
} else {
blackMessage = "无限制";
whiteMessage = "无限制";
}
//6.重新绘制窗体
this.repaint();
}
}
//点击 【游戏设置】 按钮
if (e.getX() >= 400 && e.getX() <= 470 && e.getY() >= 120
&& e.getY() <= 150) {
String input = JOptionPane
.showInputDialog("请输入游戏的最大时间(单位:分钟),如果输入0,表示没有时间限制:");
try {
maxTime = Integer.parseInt(input) * 60;
if (maxTime < 0) {
JOptionPane.showMessageDialog(this, "请输入正确信息,不允许输入负数!");
}
if (maxTime == 0) {
int result = JOptionPane.showConfirmDialog(this,
"设置完成,是否重新开始游戏?");
if (result == 0) {
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}
// 另一种方式 allChess = new int[19][19];
message = "黑方先行";
isBlack = true;
blackTime = maxTime;
whiteTime = maxTime;
blackMessage = "无限制";
whiteMessage = "无限制";
this.canPlay = true;
this.repaint();
}
}
if (maxTime > 0) {
int result = JOptionPane.showConfirmDialog(this,
"设置完成,是否重新开始游戏?");
if (result == 0) {
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}
// 另一种方式 allChess = new int[19][19];
message = "黑方先行";
isBlack = true;
blackTime = maxTime;
whiteTime = maxTime;
blackMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
whiteMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
t.resume();
this.canPlay = true;
this.repaint();
}
}
} catch (NumberFormatException e1) {
JOptionPane.showMessageDialog(this, "请正确输入信息!");
}
}
//点击 【游戏说明】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=170 && e.getY()<=200){
JOptionPane.showMessageDialog(this,"这是一个五子棋游戏程序,黑白双方轮流下棋,当某一方连到五子时游戏结束。");
}
//点击 【认输】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=270 && e.getY()<=300){
int result = JOptionPane.showConfirmDialog(this,"是否确认认输?","",0);
if(result==0){
if(isBlack){
JOptionPane.showMessageDialog(this,"黑方已经认输,游戏结束!");
}else{
JOptionPane.showMessageDialog(this,"白方已经认输,游戏结束!");
}
//停止游戏
canPlay = false;
}
}
//点击 【关于】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=320 && e.getY()<=350){
JOptionPane.showMessageDialog(this,"本游戏由老徐制作,有问题请联系老徐。");
}
//点击 【退出】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=370 && e.getY()<=400){
JOptionPane.showMessageDialog(this,"游戏结束!");
System.exit(0);
}
}
private boolean checkWin() {
boolean flag = false;
//统计相连棋子数
//横向
int count = 1;
int color = allChess[x][y];
/*int i = 1;
while (color == allChess[x + i][y]) {
count++;
i++;
}
i = 1;
while (color == allChess[x - i][y]) {
count++;
i++;
}
if (count >= 5) {
flag = true;
}
//竖向
int count2 = 1;
int i2 = 1;
while (color == allChess[x][y+i2]) {
count2++;
i2++;
}
i2 = 1;
while (color == allChess[x][y-i2]) {
count2++;
i2++;
}
if (count2 >= 5) {
flag = true;
}
// 右上+左下
int count3 = 1;
int i3 = 1;
while (color == allChess[x+i3][y-i3]) {
count3++;
i3++;
}
i3 = 1;
while (color == allChess[x-i3][y+i3]) {
count3++;
i3++;
}
if (count3 >= 5) {
flag = true;
}
// 左上+右下
int count4 = 1;
int i4 = 1;
while (color == allChess[x-i4][y-i4]) {
count4++;
i4++;
}
i4 = 1;
while (color == allChess[x+i4][y+i4]) {
count4++;
i4++;
}
if (count4 >= 5) {
flag = true;
}
*/ //判断横向
count = this.checkCount(1,0,color);
if(count>=5){
flag = true;
}else {
//判断纵向
count = this.checkCount(0,1,color);
if(count>=5){
flag = true;
}else {
//判断右上、左下
count = this.checkCount(1, -1, color);
if (count >= 5) {
flag = true;
} else {
//判断右下、左上
count = this.checkCount(1, 1, color);
if (count >= 5) {
flag = true;
}
}
}
}
return flag;
}
private int checkCount(int xChange, int yChange, int color){
int count = 1;
int tempX = xChange;
int tempY = yChange;
while ((x+xChange>=0 && x+xChange <= 18 && y+yChange>=0 && y+yChange <= 18 )&&color == allChess[x+xChange][y+yChange]){
count++;
if(xChange != 0){
xChange++;
}
if(yChange!=0){
if(yChange>0){
yChange++;
}else {
yChange--;
}
}
}
xChange = tempX;
yChange = tempY;
while ((x-xChange>=0 && x-xChange <= 18 && y-yChange>=0 && y-yChange <= 18 )&&color == allChess[x-xChange][y-yChange]){
count++;
if(xChange != 0){
xChange++;
}
if(yChange!=0){
if(yChange>0){
yChange++;
}else {
yChange--;
}
}
}
return count;
}
@Override
public void run() {
// 判断是否有时间限制
if (maxTime > 0) {
while (true) {
if (isBlack) {
blackTime--;
if (blackTime == 0) {
JOptionPane.showMessageDialog(this, "黑方超时,游戏结束!");
}
} else {
whiteTime--;
if (whiteTime == 0) {
JOptionPane.showMessageDialog(this, "白方超时,游戏结束!");
}
}
blackMessage = blackTime / 3600 + ":"
+ (blackTime / 60 - blackTime / 3600 * 60) + ":"
+ (blackTime - blackTime / 60 * 60);
whiteMessage = whiteTime / 3600 + ":"
+ (whiteTime / 60 - whiteTime / 3600 * 60) + ":"
+ (whiteTime - whiteTime / 60 * 60);
this.repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(blackTime + " -- " + whiteTime);
}
}
}
// 鼠标单击事件
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
Java Swing实现五子棋程序的更多相关文章
- java swing 双人五子棋源代码
import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Toolkit; impo ...
- Java Swing 第01记 Hello Word
首先来一个Java Swing的HelloWord程序. package cn.java.swing.chapter03; import javax.swing.JButton; import jav ...
- Java Swing快速构建窗体应用程序
以前接触java感觉其在桌面开发上,总是不太方便,没有一个好的拖拽界面布局工具,可以快速构建窗体. 最近学习了一下NetBeans IDE 8.1,感觉其窗体设计工具还是很不错的 , 就尝试一下做了一 ...
- 解决 GTK+/GNOME 3 环境下 Java Swing 程序使用本地 GTK+ 主题时菜单无边框 bug 的方法
在 GTK+/GNOME 3 环境下采用默认的 Adwaita 主题时,Java Swing 程序如果使用本地 GTK+ 主题会出现菜单无边框的 bug,这个问题也可能在其他常用的 GTK+ 主题中出 ...
- Java swing项目-图书管理系统(swing+mysql+jdbc) 总结
(一)java Swing的学习. (1)学习如何安装windowbuilder插件的安装. <1>在eclipse中点击help <2>在help的下拉选中选择install ...
- Java Swing的进化
摘 要:Swing已是一个比较老的工具集了,在美观的用户界面出来之前需要开发很长时间.它缺少一些你在开发富UI时所需的组件.幸运地是,像 Substance,SwingX及Java Look-and_ ...
- Java Swing事件处理机制
Java Swing的事件处理机制 Swing GUI启动后,Java虚拟机就启动三个线程,分别为主线程,事件派发线程(也是事件处理线程)和系统工具包线程. 主线程 :负责创建并显示该程序的初始界面: ...
- Java Swing界面编程(1)
写多了jsp,对于页面式的系统已经写烦了,本人也開始着手于java swing的学习,作为菜鸟,仅想用博客记录下我的swing学习的历程.话不多说,首先開始我的第一个窗体化程序. 下面给出源码: pa ...
- GUI编程笔记(java)02:java.awt和java.swing包的区别
1. java.awt和java.swing两者的概述 java.awt:(java的标准包) Abstract Window ToolKit (抽象窗口工具包),需要调用本地 ...
- Java Swing 使用总结(转载)
随笔转载自:此去经年ぢ 地址:http://www.cnblogs.com/FLFL/p/5369756.html 1. GUI编程引言 以前的学习当中,我们都使用的是命令交互方式: 例如:在 ...
随机推荐
- [转帖]一文读懂容器存储接口 CSI
https://zhuanlan.zhihu.com/p/470093908 作者 | 惠志来源 | 阿里巴巴云原生公众号 导读:在<一文读懂 K8s 持久化存储流程>一文我们重点介绍了 ...
- [转帖]《Linux性能优化实战》笔记(22)—— 网络丢包问题分析
所谓丢包,是指在网络数据的收发过程中,由于种种原因,数据包还没传输到应用程序中,就被丢弃了.这些被丢弃包的数量,除以总的传输包数,也就是我们常说的丢包率.丢包率是网络性能中最核心的指标之一.丢包通常会 ...
- 【转帖】3.JVM内存结构概述
目录 1.JVM内存结构 1.JVM内存结构 在JVM系列的第一篇文章中已经给出了JVM内存结构的简图,下面是JVM内存结构更加详细的图. 同样,JVM的内存结构可以分为上中下3层. 上层主要是类加载 ...
- 【转帖】【奇技淫巧】Linux | 安全保障防火墙-iptables
虽然说Linux在安全方面确实相当于windows要更加可靠一些,但一般使用其作为服务器的我们,也不能大意,也是需要严格限制网络传输过程中的出入规则.上篇文章我们有聊到统计网络的信息,这篇文章来学习一 ...
- 使用Grafana 监控 SQLSERVER数据库
使用Grafana 监控 SQLSERVER数据库 1.获取镜像信息以及启动镜像 docker pull awaragi/prometheus-mssql-exporter docker run -e ...
- MySQL新增数据,修改数据,删除数据
连接本地mysql语句 mysql -hlocalhost -uroot -proot DML-介绍 DML英文全称是:用来对数据库中表的数据记录进行 增 删 改 操作. 增加使用 insert 删除 ...
- vue3.2新增指令v-memo的使用
v-memo的讲解 vue3.2中新增了一个性能优化的指令: 这个指令就是v-memo; v-memo:可以做性能优化,v-memo中值若不发生变化,整个子树的更新会被跳过. <div v-me ...
- 手写Promise自定义封装 then 函数
Promise 自定义封装 then 函数 <script src="./Promise.js"></script> <script type=&qu ...
- js分钟转化为小时并且以某个数字进行递增
有些时候,我们需要将分钟转为小时: 并且还有以一个数字进行递增: 呈现出[3,6,9,12,15,18]这样的递增形式 // 因为是递增,所以是相加: // 在使用+号的时候: // 注意两边都是数字 ...
- 【小测试】fastcgo 调用 c 代码
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 找到了一个项目 https://github.com/pe ...