Java-GUI编程-五子棋游戏

news/2025/2/2 22:07:38/文章来源:https://www.cnblogs.com/CaiDingChao/p/18697146

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);
}
}

代码结构说明:

  1. 成员变量:定义了游戏所需的变量,如棋盘状态、时间限制等。

  2. 构造函数:初始化UI、按钮处理器和背景图片。

  3. UI初始化:设置窗口属性并加载背景图片。

  4. 按钮处理器:通过Map管理按钮点击事件。

  5. 绘制逻辑:使用双缓冲技术绘制棋盘、棋子和游戏信息。

  6. 事件处理:处理鼠标点击事件,包括按钮点击和棋盘点击。

  7. 辅助方法:如resetBoardupdateTimeMessages等,用于简化代码逻辑。

  8. 线程逻辑:实现倒计时功能。

  9. 内部类ButtonArea用于定义按钮区域,ButtonClickHandler用于定义按钮点击处理器接口。


使用方法:

  1. 将代码复制到 FiveChessFrame.java 文件中。

  2. 确保资源文件(如背景图片)路径正确。

  3. 运行程序即可体验优化后的五子棋游戏。



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/878116.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【模拟电子技术】18-多级放大电路的构成与动态分析

【模拟电子技术】18-多级放大电路的构成与动态分析 单极放大电路往往不能满足我们的需求,比如有一个电压型的信号源,其内阻还不能忽略,输出端要求输出稳定的电压,要求设计尽可能放大的放大电路。 即Ri大,R0小例如这样就可以实现,接下来看直接耦合放大电路的“进化”图(a…

AvaloniaUI 的甘特图组件.

https://github.com/xiejiang2014/XieJiang.Gantt.Avalonia Gantt chart component for AvaloniaUI(UNDER CONSTRUCTION).

快速入门 DeepSeek-R1 大模型

国内最新的神级人工智能模型已经正式发布,没错,它就是备受瞩目的DeepSeek-R1大模型。今天,我们将对DeepSeek进行一个简单的了解,并探索如何快速使用和部署这个强大的工具。值得一提的是,DeepSeek已经开源,您可以随意下载和使用它。 DeepSeek的官方网站地址如下:https://…

CompletableFuture 超时功能有大坑!使用不当直接生产事故!

CompletableFuture 超时功能有大坑!使用不当直接生产事故! 本文未经允许禁止转载! 上一篇文章《如何实现超时功能(以CompletableFuture为例)》中我们讨论了 CompletableFuture 超时功能的具体实现,从整体实现来说,JDK21前的版本有着内存泄露的bug,不过很少对实际生产有影…

2024.2.2 鲜花

P2305 [NOI2014] 购票aLIEz 決めつけばかり 自惚れを着たチープな hokori で 音荒げても 棚に隠した哀れな 恥に濡れた鏡の中 都合の傷だけひけらかして 手軽な強さで勝取る術を どれだけ磨いでも気はやつれる ふらついた思想通りだ 愛-same-CRIER 愛撫-save-LIAR Eid-聖-Risin…

昆明理工大学25考研冶金工程预计调剂145人

冶金工程考研809冶金物理化学有色冶金学有色金属冶金冶金过程及设备F002钢铁冶金学

8.数据结构

空气在他的呼吸间化作赤红烈焰,烈火在他掌中咆哮翻涌,如同猛兽般肆虐纵横,每一缕火舌都在嘶吼着征服与毁灭。他以战神的姿态掌控炽炎,以焚天煮海之势,在这场杀戮盛宴中肆意狂舞!数据结构 开题顺序: \(WHABCEI\) \(A\) CF2042D Recommendations扫描线维护 \(\le l\) 的最…

ollama mac使用

教程地址:https://www.youtube.com/watch?v=SRroLOci0CA 安装完成后,常用命令。 启动服务:ollama run deepseek-r1:8B 使用:停止服务:本文来自博客园,作者:NeverLateThanBetter,转载请注明原文链接:https://www.cnblogs.com/do-it-520/p/18697037韶华易逝,不能虚度年…

07. 文件操作

一、文件的查找我们可以使用 find 命令 从指定目录向下递归地遍历其各个子目录,将满足的文件显示在终端中。 find [搜索范围] [选项]其中,选项的可选值如下:-name 文件名:按照指定的文件名查找文件,如果不知道文件的全名,可以使用 * 进行模糊匹配。 -user 用户名:查找属…

《计算机网络》笔记——第五章 运输层

计算机网络(第7版)谢希仁目录第5章 运输层概述运输层的两个主要协议端口用户数据报协议UDPUDP的首部格式传输控制协议TCPTCP的连接可靠传输的工作原理停止等待协议连续ARQ协议TCP报文段的首部格式TCP可靠传输的实现滑动窗口超时重传时间(RTO)的选择选择确认SACKTCP的流量控制…

[Paper Reading] DeepSeek-V3 Technical Report

目录DeepSeek-V3 Technical Report解读TL;DR优势训练数据参数量Method架构MLA(Multi-Head Latent Attention)DeepSeekMoEMoEDeepSeekMoEMTP(Multi-Token Prediction)基建FP8训练部署PrefillingDecodingPre-TrainingDataLong Context ExtensionPost-TrainingSFTReinforcement Le…