java-gui编程-五子棋小游戏
文件目录如下:
棋盘背景图如下:
运行效果如下:
FiveChess类代码:
package com.gui.fivechess;
public class FiveChess {
//主类,运行该类即可启动游戏
public static void main(String[] args) {
FiveChessFrame ff = new FiveChessFrame();
}
}
FiveChessFrame类代码:
package com.gui.fivechess;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.swing.*;
public class FiveChessFrame extends JFrame implements MouseListener, Runnable {
// 常量定义
private static final int BOARD_START_X = 150;
private static final int BOARD_START_Y = 125;
private static final int CELL_SIZE = 50;
private static final int BOARD_SIZE = 14;
// 成员变量
private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
private int height = Toolkit.getDefaultToolkit().getScreenSize().height;
private BufferedImage bgimage;
private int[][] allChess = new int[BOARD_SIZE + 1][BOARD_SIZE + 1];
private boolean isBlack = true;
private boolean canPlay = true;
private String message = "黑方先行";
private int maxTime = 0;
private Thread t = new Thread(this);
private int blackTime = 0;
private int whiteTime = 0;
private String blackMessage = "无限制";
private String whiteMessage = "无限制";
private BufferedImage bufferImage;
private boolean isAIMode = false; // 是否为 AI 模式
// 按钮点击处理器映射
private Map<ButtonArea, ButtonClickHandler> buttonHandlers = new HashMap<>();
// 构造函数
public FiveChessFrame() {
// 模式选择
String[] options = {"玩家对战", "人机对战"};
int choice = JOptionPane.showOptionDialog(this, "请选择对战模式", "模式选择",
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
if (choice == 0) {
isAIMode = false; // 玩家对战
} else if (choice == 1) {
isAIMode = true; // 人机对战
} else {
System.exit(0); // 用户取消选择,退出程序
}
initUI();
initButtonHandlers();
loadBackgroundImage();
startGame();
}
// 初始化UI
private void initUI() {
setTitle("五子棋");
setSize(1080, 970);
setLocation((width - 1000) / 2, (height - 1000) / 2);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addMouseListener(this);
setVisible(true);
}
// 初始化按钮处理器
private void initButtonHandlers() {
buttonHandlers.put(new ButtonArea(888, 130, 1020, 160), this::handleStartGame);
buttonHandlers.put(new ButtonArea(888, 255, 1020, 285), this::handleGameInstructions);
buttonHandlers.put(new ButtonArea(888, 380, 1020, 410), this::handleAbout);
buttonHandlers.put(new ButtonArea(888, 505, 1020, 535), this::handleSurrender);
buttonHandlers.put(new ButtonArea(888, 630, 1020, 660), this::handleExitGame);
buttonHandlers.put(new ButtonArea(888, 755, 1020, 785), this::handleGameSettings);
}
// 加载背景图片
private void loadBackgroundImage() {
try {
bgimage = ImageIO.read(Objects.requireNonNull(getClass().getResource("resource/beijing.jpg")));
} catch (IOException e) {
bgimage = new BufferedImage(1080, 970, BufferedImage.TYPE_INT_ARGB);
Graphics g = bgimage.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, 1080, 970);
g.dispose();
JOptionPane.showMessageDialog(this, "背景图片加载失败,使用默认背景。");
}
}
// 启动游戏
private void startGame() {
t.start();
t.suspend();
repaint();
}
// 绘制方法
@Override
public void paint(Graphics g) {
if (bufferImage == null || bufferImage.getWidth() != getWidth() || bufferImage.getHeight() != getHeight()) {
bufferImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Graphics g2 = bufferImage.createGraphics();
// 绘制背景
g2.drawImage(bgimage, 0, 0, this);
// 绘制游戏信息
drawGameInfo(g2);
// 绘制棋盘
drawChessBoard(g2);
// 绘制棋子
drawChessPieces(g2);
g.drawImage(bufferImage, 0, 0, this);
}
// 绘制游戏信息
private void drawGameInfo(Graphics g2) {
g2.setColor(Color.BLACK);
g2.setFont(new Font("楷体", Font.BOLD, 50));
g2.drawString("游戏信息:" + message, 70, 90);
g2.setFont(new Font("楷体", Font.PLAIN, 30));
g2.drawString("黑方时间:" + blackMessage, 75, 888);
g2.drawString("白方时间:" + whiteMessage, 440, 888);
g2.drawString("新的游戏",888,160);
g2.drawString("游戏规则",888,285);
g2.drawString("关于游戏",888,410);
g2.drawString("认输游戏",888,535);
g2.drawString("退出游戏",888,660);
g2.drawString("游戏设置",888,785);
}
// 绘制棋盘
private void drawChessBoard(Graphics g2) {
for (int i = 0; i <= BOARD_SIZE; i++) {
g2.drawLine(BOARD_START_X + i * CELL_SIZE, BOARD_START_Y, BOARD_START_X + i * CELL_SIZE, BOARD_START_Y + BOARD_SIZE * CELL_SIZE);
g2.drawLine(BOARD_START_X, BOARD_START_Y + i * CELL_SIZE, BOARD_START_X + BOARD_SIZE * CELL_SIZE, BOARD_START_Y + i * CELL_SIZE);
}
}
// 绘制棋子
private void drawChessPieces(Graphics g2) {
for (int i = 1; i <= BOARD_SIZE; i++) {
for (int j = 1; j <= BOARD_SIZE; j++) {
if (allChess[i][j] == 1) {
drawChessPiece(g2, i, j, Color.BLACK);
} else if (allChess[i][j] == 2) {
drawChessPiece(g2, i, j, Color.WHITE);
}
}
}
}
// 绘制单个棋子
private void drawChessPiece(Graphics g2, int x, int y, Color color) {
int tempx = (x - 1) * CELL_SIZE + BOARD_START_X;
int tempy = (y - 1) * CELL_SIZE + BOARD_START_Y;
g2.setColor(color);
g2.fillOval(tempx - 20, tempy - 20, 40, 40);
if (color == Color.WHITE) {
g2.setColor(Color.BLACK);
g2.drawOval(tempx - 20, tempy - 20, 40, 40);
}
}
// 鼠标点击事件处理
@Override
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
// 处理按钮点击
for (Map.Entry<ButtonArea, ButtonClickHandler> entry : buttonHandlers.entrySet()) {
if (entry.getKey().contains(x, y)) {
entry.getValue().handle(this);
return;
}
}
// 处理棋盘点击
if (canPlay && x >= BOARD_START_X && x <= BOARD_START_X + BOARD_SIZE * CELL_SIZE &&
y >= BOARD_START_Y && y <= BOARD_START_Y + BOARD_SIZE * CELL_SIZE) {
handleChessPlacement(x, y);
}
}
// 处理棋子放置
// 在 handleChessPlacement 方法中增加 AI 逻辑
private void handleChessPlacement(int x, int y) {
int chessX = Math.round((x - BOARD_START_X) / (float) CELL_SIZE) + 1;
int chessY = Math.round((y - BOARD_START_Y) / (float) CELL_SIZE) + 1;
if (allChess[chessX][chessY] == 0) {
allChess[chessX][chessY] = isBlack ? 1 : 2;
if (checkWin(chessX, chessY)) {
JOptionPane.showMessageDialog(this, "游戏结束," + (isBlack ? "黑方" : "白方") + "获胜!");
canPlay = false;
message = "游戏结束!";
} else {
isBlack = !isBlack;
message = isBlack ? "轮到黑方" : "轮到白方";
// 如果是 AI 模式且轮到电脑下棋
if (isAIMode && !isBlack) {
aiMove(); // AI 落子
}
}
repaint();
} else {
JOptionPane.showMessageDialog(this, "当前位置已经有棋子了!");
}
}
// AI 落子逻辑
private void aiMove() {
// 简单的随机落子 AI
int x, y;
do {
x = (int) (Math.random() * BOARD_SIZE) + 1;
y = (int) (Math.random() * BOARD_SIZE) + 1;
} while (allChess[x][y] != 0); // 找到一个空位置
allChess[x][y] = 2; // 白棋(AI)落子
if (checkWin(x, y)) {
JOptionPane.showMessageDialog(this, "游戏结束, 白方(AI)获胜!");
canPlay = false;
message = "游戏结束!";
} else {
isBlack = true; // 切换回玩家
message = "轮到黑方";
}
repaint();
}
// 判断是否胜利
private boolean checkWin(int x, int y) {
int[][] directions = {{1, 0}, {0, 1}, {1, 1}, {1, -1}};
for (int[] dir : directions) {
if (countConsecutive(x, y, dir[0], dir[1]) >= 5) {
return true;
}
}
return false;
}
// 计算连续棋子数量
private int countConsecutive(int x, int y, int dx, int dy) {
int count = 1;
int color = allChess[x][y];
for (int i = 1; i <= 4; i++) {
if (isValidPosition(x + i * dx, y + i * dy) && allChess[x + i * dx][y + i * dy] == color) {
count++;
} else {
break;
}
}
for (int i = 1; i <= 4; i++) {
if (isValidPosition(x - i * dx, y - i * dy) && allChess[x - i * dx][y - i * dy] == color) {
count++;
} else {
break;
}
}
return count;
}
// 判断坐标是否有效
private boolean isValidPosition(int x, int y) {
return x >= 1 && x <= BOARD_SIZE && y >= 1 && y <= BOARD_SIZE;
}
// 按钮事件处理方法
private void handleStartGame(FiveChessFrame fiveChessFrame) {
int result = JOptionPane.showConfirmDialog(this, "是否重新开始游戏?");
if (result == JOptionPane.YES_OPTION) {
resetBoard();
message = "黑方先行";
isBlack = true;
blackTime = maxTime;
whiteTime = maxTime;
updateTimeMessages();
canPlay = true;
repaint();
}
}
private void handleGameInstructions(FiveChessFrame fiveChessFrame) {
String instructions = "五子棋游戏规则:\n" +
" 1. 黑白双方轮流落子,黑方先行\n" +
" 2. 先在横/竖/斜方向连成五子者胜\n" +
" 3. 点击棋盘交叉点放置棋子\n" +
" 4. 支持时间限制模式(设置中启用)\n" +
" 5. 认输/超时自动判负"
;
JOptionPane.showMessageDialog(this, instructions, "游戏说明", JOptionPane.INFORMATION_MESSAGE);
}
private void handleAbout(FiveChessFrame fiveChessFrame) {
String aboutInfo = "五子棋游戏 v1.0\n" + "开发者:cdc\n";
JOptionPane.showMessageDialog(this, aboutInfo, "关于游戏", JOptionPane.INFORMATION_MESSAGE);
}
private void handleSurrender(FiveChessFrame fiveChessFrame) {
int result = JOptionPane.showConfirmDialog(this, "确认要认输吗?");
if (result == JOptionPane.YES_OPTION) {
String winner = isBlack ? "白方" : "黑方";
JOptionPane.showMessageDialog(this, winner + "获胜!");
canPlay = false;
message = "游戏结束 - " + winner + "胜利";
repaint();
}
}
private void handleExitGame(FiveChessFrame fiveChessFrame) {
int result = JOptionPane.showConfirmDialog(this, "确定要退出游戏吗?");
if (result == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
private void handleGameSettings(FiveChessFrame fiveChessFrame) {
String input = JOptionPane.showInputDialog("请输入时间限制(分钟,0表示无限制):");
try {
int minutes = Integer.parseInt(input);
if (minutes < 0) {
JOptionPane.showMessageDialog(this, "请输入非负数!");
return;
}
maxTime = minutes * 60;
if (maxTime == 0) {
blackMessage = whiteMessage = "无限制";
} else {
blackTime = whiteTime = maxTime;
updateTimeMessages();
}
int result = JOptionPane.showConfirmDialog(this, "需要重新开始游戏应用设置吗?");
if (result == JOptionPane.YES_OPTION) {
resetBoard();
canPlay = true;
t.resume();
repaint();
}
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(this, "请输入有效数字!");
}
}
// 辅助方法
private void resetBoard() {
for (int i = 1; i <= BOARD_SIZE; i++) {
for (int j = 1; j <= BOARD_SIZE; j++) {
allChess[i][j] = 0;
}
}
}
private void updateTimeMessages() {
if (maxTime > 0) {
blackMessage = formatTime(blackTime);
whiteMessage = formatTime(whiteTime);
} else {
blackMessage = whiteMessage = "无限制";
}
}
private String formatTime(int seconds) {
return String.format("%02d:%02d:%02d",
seconds / 3600,
(seconds % 3600) / 60,
seconds % 60);
}
// 线程运行逻辑
@Override
public void run() {
while (true) {
if (maxTime > 0 && canPlay) {
if (isBlack) {
blackTime--;
if (blackTime <= 0) {
handleTimeOut("黑方");
break;
}
} else {
whiteTime--;
if (whiteTime <= 0) {
handleTimeOut("白方");
break;
}
}
updateTimeMessages();
repaint();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void handleTimeOut(String player) {
canPlay = false;
JOptionPane.showMessageDialog(this, player + "超时,游戏结束!");
message = player + "超时失败";
repaint();
t.suspend();
}
// 其他MouseListener空实现
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent e) {}
@Override public void mouseExited(MouseEvent e) {}
@Override public void mouseReleased(MouseEvent e) {}
// 内部类:按钮区域定义
private static class ButtonArea {
private final int x1, y1, x2, y2;
public ButtonArea(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public boolean contains(int x, int y) {
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
}
}
// 按钮点击处理器接口
@FunctionalInterface
private interface ButtonClickHandler {
void handle(FiveChessFrame frame);
}
}
代码结构说明:
-
成员变量:定义了游戏所需的变量,如棋盘状态、时间限制等。
-
构造函数:初始化UI、按钮处理器和背景图片。
-
UI初始化:设置窗口属性并加载背景图片。
-
按钮处理器:通过
Map
管理按钮点击事件。 -
绘制逻辑:使用双缓冲技术绘制棋盘、棋子和游戏信息。
-
事件处理:处理鼠标点击事件,包括按钮点击和棋盘点击。
-
辅助方法:如
resetBoard
、updateTimeMessages
等,用于简化代码逻辑。 -
线程逻辑:实现倒计时功能。
-
内部类:
ButtonArea
用于定义按钮区域,ButtonClickHandler
用于定义按钮点击处理器接口。
使用方法:
-
将代码复制到
FiveChessFrame.java
文件中。 -
确保资源文件(如背景图片)路径正确。
-
运行程序即可体验优化后的五子棋游戏。