Java GUI编程实践1:五子棋
前言
五子棋是一种两人对弈的纯策略性棋类游戏,棋具与围棋同用,是起源于中国古代的传统黑白棋种之一1。五子棋游戏规则简单易懂,老少皆宜,趣味横生,引人入胜。在这里用Java swing制作一个五子棋游戏。
系统总体设计
游戏开始时,由白方先开局,将一枚棋子落在棋盘上的一个交叉点上,然后由红方落子,如此轮流下子,直到某一方首先在棋盘的横向、纵向、或对角线上五子连成一条直线,则该方该局获胜。在一局中的任意时刻双方都可以认输,也可以重新开局。
项目解析
这个小游戏是用IntelliJ IDEA编写的,在Main.java中建立了4个类:
* public class Main # 提供进入程序的main()方法
* class MyFrame extends JFrame # GUI的基础框架
* class MainScreen extends JPanel implements ActionListener, MouseListener # 各种组件的容器
* class MapPanel extends JPanel # 内有paint()方法,专门用来画棋盘
棋盘界面开发
关于棋盘界面的具体约定如下:
- 棋盘总体大小为19 x 19(纵向、横向各19条线)
- 棋盘总宽度为360像素,分成18份,每份20像素。
- 棋盘总高度为360像素,分成18份,每份20像素。
具体实现代码如下:
public void paint(Graphics g) {
// 双缓冲技术防止屏幕闪烁
BufferedImage bi = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
Graphics g1 = bi.createGraphics();
// 绘制标题、双方信息
g1.drawString("游戏信息:" + Main.xinxi, 130, 45);
g1.drawString("白方时间:" + Main.black_msg, 80, 480);
g1.drawString("红方时间:" + Main.white_msg, 310, 480);
// 绘制棋盘
for(int i = 0; i < 19; i++) {
g1.drawLine(60, 80+20*i, 420, 80+20*i);
g1.drawLine(60+20*i, 80, 60+20*i, 440);
}
// 标注点位
g1.fillOval(98, 138, 4, 4);
g1.fillOval(98, 258, 4, 4);
g1.fillOval(98, 378, 4, 4);
g1.fillOval(218, 138, 4, 4);
g1.fillOval(218, 258, 4, 4);
g1.fillOval(218, 378, 4, 4);
g1.fillOval(338, 138, 4, 4);
g1.fillOval(338, 258, 4, 4);
g1.fillOval(338, 378, 4, 4);
// 绘制全部棋子
draw_allwzq(g1);
// 将缓冲区的图片显示出来
g.drawImage(bi, 0, 0, this);
}
保存棋局数组
通过一个全局二维数组变量保存已经下过的所有棋子的位置:
public class Main {
public static int[][] allwzq = new int[19][19];
...
}
}
处理落子事件
当鼠标单击棋盘上的某个纵横线交叉点时,在该点上显示一个棋子。需要在public void mousePressed(MouseEvent e)
中编写代码。只需要改变全局二维数组的值,然后MainScreen
调用repaint()方法即可。
public void mousePressed(MouseEvent e) {
// 如果对局已经结束,则不响应
if(Main.is_finished) {
return;
}
// 获取鼠标点击位置在棋盘上的映射
int x = (e.getX() - 65 + 10) / 20;
int y = (e.getY() - 85 + 10) / 20;
System.out.println(e.getX() + "," + e.getY() + "=(" + x + "," + y + ")");
// 棋盘四方最边上的线不能落子
if(x >= 1 && x <= 17 && y >= 1 && y <= 17) {
if(Main.is_black) {
// 白方落子
Main.allwzq[x][y] = 1;
// 统计白方是否胜利
boolean flag1 = is_win(1, x, y);
if(flag1) {
Main.is_finished = true;
JOptionPane.showMessageDialog(this, "游戏结束,白方胜利!");
}
} else {
// 红方落子
Main.allwzq[x][y] = 2;
// 统计红方是否胜利
boolean flag2 = is_win(2, x, y);
if(flag2) {
Main.is_finished = true;
JOptionPane.showMessageDialog(this, "游戏结束,红方胜利!");
}
}
this.repaint();
}
}
判断胜负
一方落子之后,要判断横向、纵向、或对角线上是否有5个连续的同色棋子。算法是追溯到落子所在的行、列、或对角线,每当碰到需要的颜色的棋子,计数器加一,每当碰到其它颜色的棋子,计数器归零。
public boolean is_win(int color, int x, int y) {
// 判断横向
int count = 0;
for(int i = 1; i <= 17; i++) {
if(Main.allwzq[i][y] == color) {
count++;
if(count == 5) {
return true;
}
}
if(Main.allwzq[i][y] != color) {
count = 0;
}
}
...
}
功能按钮的实现
【 开始 】按钮
单击【 开始 】按钮,则开始新的游戏,如下:
if(e.getSource() == btn_start) {
int result = JOptionPane.showConfirmDialog(this, "是否重新开始游戏?");
if(result == 0) {
// 重新开始游戏
// (1)把棋盘清空
// (2)把游戏参数复位到初始值
intial_parameters();
}
}
【 认输 】按钮
单击【 认输 】按钮,表示一方放弃游戏,投子认输。
if(e.getSource() == btn_giveUp) {
int result = JOptionPane.showConfirmDialog(this, "是否确认认输?");
if(result == 0) {
if(Main.is_black) {
JOptionPane.showMessageDialog(this, "白方已经认输,游戏结束!");
} else {
JOptionPane.showMessageDialog(this, "红方已经认输,游戏结束!");
}
// 棋局结束,单击【 开始 】可重新开始游戏
Main.is_finished = true;
}
}
【 退出 】按钮
单击【 退出 】按钮后弹出确认对话框,确认后退出程序,否则游戏继续。
if(e.getSource() == btn_exit) {
int result = JOptionPane.showConfirmDialog(this, "要退出游戏吗?");
if(result == 0) {
System.exit(0);
}
}
【 游戏设置 】按钮
这里将实现游戏倒计时的设置。如果设置了倒计时,当一方时间用完,系统将自动判定该方为输。
if(e.getSource() == btn_setting) {
String input = JOptionPane.showInputDialog(this, "请输入游戏的最大时间(单位:分钟):");
try {
Main.max_time = Integer.parseInt(input) * 60;
// 提示输入每方每次落子的最大时限,0表示无限制
intial_parameters_with_dialog();
} catch (NumberFormatException e2) {
JOptionPane.showMessageDialog(this, "请正确输入时间!");
}
}
【 关于 】按钮
【 关于 】按钮用来显示程序作者的相关信息。
if(e.getSource() == btn_about) {
JOptionPane.showMessageDialog(this, "制作者:下唐人\n制作时间:2023年9月");
}
源代码
完整的源代码请在GitCode中下载查看:项目首页 - wuziqi - GitCode
运行
该项目已经编译好可执行JAR包,可直接运行。方法是打开终端,然后输入:
cd java1_jar/
java -jar java1.jar
运行结果展示
聚慕课教育研发中心 编著. Java从入门到项目实践. 清华大学出版社. 2018 ↩︎