使用AI对话,给出基础需求
AI帮助提炼需求,并自动生成图片
实现菜单
新游戏
悔棋
人人对战
人机对战(算法优化)
Java Swing包实现,识别准确,渐变背景
package org.example.gobang;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Stack;
import java.awt.BorderLayout;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Gobang extends JPanel {
// 游戏参数
private static final int BOARD_SIZE = 15;
private static final int CELL_SIZE = 40;
private static final int MARGIN = 40;
// 游戏状态
private int[][] board = new int[BOARD_SIZE][BOARD_SIZE];
private int currentPlayer = 1; // 1: 黑棋 2: 白棋
private boolean gameOver = false;
private boolean vsAI = false; // 人机对战模式
private Stack<int[]> moveHistory = new Stack<>(); // 棋步历史
private final int MAX_UNDO = 3; // 最大悔棋次数
private int undoCount = 0; // 当前悔棋计数
private JPanel controlPanel;
// 属性变更支持
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
// 历史对局相关
private ArrayList<int[]> replayMoves = new ArrayList<>(); // 复盘时使用的棋步
private int replayIndex = 0; // 当前复盘索引
private boolean replayMode = false; // 是否处于复盘模式
private Timer replayTimer; // 复盘定时器
private JFrame parentFrame; // 父窗口引用
public Gobang() {
this(null);
}
public Gobang(JFrame parent) {
this.parentFrame = parent;
// 初始化控制面板 - 移至顶部
controlPanel = new JPanel();
controlPanel.setBackground(new Color(243, 224, 195));
controlPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
controlPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 20, 5));
// 创建顶部按钮
JButton restartBtn = createStyledButton("新游戏");
String playerType = (currentPlayer == 1) ? "黑方" : "白方";
JButton undoBtn = createStyledButton(playerType + "悔棋(剩余" + (MAX_UNDO - undoCount) + "次)");
JCheckBox aiModeCheck = createStyledCheckBox("人机对战");
// 事件监听器
restartBtn.addActionListener(e -> {
resetGame();
undoBtn.setText("黑方悔棋(剩余" + MAX_UNDO + "次)");
});
undoBtn.addActionListener(e -> {
if (undoCount < MAX_UNDO && !moveHistory.isEmpty()) {
int[] lastMove = moveHistory.pop();
board[lastMove[0]][lastMove[1]] = 0;
currentPlayer = lastMove[2]; // 恢复上一个玩家
propertyChangeSupport.firePropertyChange("currentPlayer", 0, currentPlayer);
undoCount++;
// 确保显示的是当前玩家类型
String playerType1 = (currentPlayer == 1) ? "黑方" : "白方";
undoBtn.setText(playerType1 + "悔棋(剩余" + (MAX_UNDO - undoCount) + "次)");
repaint();
}
});
aiModeCheck.addItemListener(e -> {
vsAI = (e.getStateChange() == ItemEvent.SELECTED);
resetGame();
});
// 添加按钮到控制面板
controlPanel.add(restartBtn);
controlPanel.add(undoBtn);
controlPanel.add(aiModeCheck);
setPreferredSize(new Dimension(
CELL_SIZE*(BOARD_SIZE-1) + 2*MARGIN,
CELL_SIZE*(BOARD_SIZE-1) + 2*MARGIN
));
// 初始化复盘定时器
replayTimer = new Timer(1000, e -> {
if (replayIndex < replayMoves.size()) {
int[] move = replayMoves.get(replayIndex++);
board[move[0]][move[1]] = move[2];
repaint();
} else {
replayTimer.stop();
JOptionPane.showMessageDialog(this, "复盘结束");
}
});
// 鼠标监听器
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (replayMode) {
return; // 复盘模式下不响应鼠标点击
}
if (!gameOver) {
int x = e.getX() - MARGIN;
int y = e.getY() - MARGIN;
int i = (x + CELL_SIZE/2) / CELL_SIZE;
int j = (y + CELL_SIZE/2) / CELL_SIZE;
if (i >= 0 && i < BOARD_SIZE &&
j >= 0 && j < BOARD_SIZE &&
board[i][j] == 0) {
moveHistory.push(new int[]{i, j, currentPlayer});
board[i][j] = currentPlayer;
if (checkWin(i, j)) {
gameOver = true;
repaint();
String winner = (currentPlayer == 1 ? "黑方" : "白方");
JOptionPane.showMessageDialog(Gobang.this, winner + "获胜!");
// 询问是否保存对局
int option = JOptionPane.showConfirmDialog(Gobang.this,
"是否保存本局对战记录?", "保存对局",
JOptionPane.YES_NO_OPTION);
if (option == JOptionPane.YES_OPTION) {
saveGame();
}
} else {
int oldPlayer = currentPlayer;
currentPlayer = currentPlayer == 1 ? 2 : 1;
propertyChangeSupport.firePropertyChange("currentPlayer", oldPlayer, currentPlayer);
repaint();
// AI回合处理
if (!gameOver && vsAI && currentPlayer == 2) {
makeAIMove();
}
}
}
}
}
});
}
// 添加属性变更监听器
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
// 添加特定属性变更监听器
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
// 移除属性变更监听器
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
// 移除特定属性变更监听器
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
}
private void resetGame() {
board = new int[BOARD_SIZE][BOARD_SIZE];
int oldPlayer = currentPlayer;
currentPlayer = 1;
gameOver = false;
moveHistory.clear();
undoCount = 0;
propertyChangeSupport.firePropertyChange("currentPlayer", oldPlayer, currentPlayer);
repaint();
}
private void makeAIMove() {
double bestScore = Integer.MIN_VALUE;
int bestI = -1, bestJ = -1;
boolean foundWinningMove = false;
// 首先检查是否有直接获胜的位置
for (int i = 0; i < BOARD_SIZE && !foundWinningMove; i++) {
for (int j = 0; j < BOARD_SIZE && !foundWinningMove; j++) {
if (board[i][j] == 0) {
// 临时落子检查是否获胜
board[i][j] = 2;
if (checkWin(i, j)) {
bestI = i;
bestJ = j;
foundWinningMove = true;
}
board[i][j] = 0; // 恢复
}
}
}
// 如果没有直接获胜的位置,再评估最佳位置
if (!foundWinningMove) {
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == 0) {
// 评估这个位置的分数
double score = evaluatePosition(i, j);
if (score > bestScore) {
bestScore = score;
bestI = i;
bestJ = j;
}
}
}
}
}
// 在最佳位置落子
if (bestI != -1 && bestJ != -1) {
moveHistory.push(new int[]{bestI, bestJ, currentPlayer});
board[bestI][bestJ] = currentPlayer;
if (checkWin(bestI, bestJ)) {
gameOver = true;
repaint();
JOptionPane.showMessageDialog(null, "白方获胜!");
} else {
int oldPlayer = currentPlayer;
currentPlayer = 1;
propertyChangeSupport.firePropertyChange("currentPlayer", oldPlayer, currentPlayer);
repaint();
}
}
}
private double evaluatePosition(int x, int y) {
int score = 0;
int[][] directions = {{1,0}, {0,1}, {1,1}, {1,-1}};
// 检查AI自己可以连成五子的机会 - 最高优先级
board[x][y] = 2; // 临时落子
if (checkWin(x, y)) {
board[x][y] = 0; // 恢复
return 1000000; // 极大提高必胜位置权重,确保AI选择获胜位置
}
// 检查是否需要防守(对手是否可以在这里连成五子)
board[x][y] = 1;
if (checkWin(x, y)) {
board[x][y] = 0;
return 195000; // 大幅提高防守权重,接近进攻权重
}
// 检查是否是中间位置的连子威胁(对手在中间位置形成连子)
boolean isCentralPosition = isCentralPosition(x, y);
if (isCentralPosition) {
// 检查中间位置的连子威胁
int centralThreatLevel = checkCentralThreat(x, y, 1);
if (centralThreatLevel > 0) {
board[x][y] = 0;
return centralThreatLevel * 7.0; // 提高中间位置连子的防守权重
}
}
// 检查对手的潜在威胁
int threatLevel = checkThreatLevel(x, y, 1);
if (threatLevel > 0) {
board[x][y] = 0;
// 对角线方向的威胁更危险,需要更高的防守权重
boolean isDiagonalThreat = isDiagonalThreat(x, y, 1);
double threatMultiplier = isDiagonalThreat ? 6.0 : 5.0; // 大幅提高防守权重
return threatLevel * threatMultiplier; // 提高威胁应对权重
}
// 检查AI的进攻机会
int aiThreatLevel = checkThreatLevel(x, y, 2);
if (aiThreatLevel > 0) {
board[x][y] = 0;
// 对角线方向的进攻更有效
boolean isDiagonalThreat = isDiagonalThreat(x, y, 2);
double aiThreatMultiplier = isDiagonalThreat ? 5.5 : 4.8;
// 检查是否靠近边界,如果是则降低进攻权重
if (isNearBoundary(x, y)) {
aiThreatMultiplier *= 0.7; // 降低边界附近的进攻权重
}
return aiThreatLevel * aiThreatMultiplier; // 更积极的进攻权重
}
board[x][y] = 0;
// 评估每个方向的连子情况
for (int[] dir : directions) {
int dx = dir[0], dy = dir[1];
boolean isDiagonal = Math.abs(dx) == Math.abs(dy);
// 提高对角线方向的权重
double defenseMultiplier = isDiagonal ? 6.5 : 4.5; // 大幅提高斜向防守权重
double attackMultiplier = isDiagonal ? 6.0 : 4.0; // 提高斜向进攻权重
// 平衡进攻和防守,但略微倾向防守
score += evaluateDirection(x, y, dx, dy, 2) * attackMultiplier;
score += evaluateDirection(x, y, dx, dy, 1) * defenseMultiplier;
}
// 优化中心位置和关键点位置权重
int centerWeight = 50 - (Math.abs(x - BOARD_SIZE/2) + Math.abs(y - BOARD_SIZE/2));
score += centerWeight * 6; // 进一步提高中心控制的重要性
// 考虑周围棋子的影响
score += evaluateSurroundings(x, y) * 2.5; // 提高周围环境评估权重
return score;
}
// 检查是否是对角线方向的威胁
private boolean isDiagonalThreat(int x, int y, int player) {
int[][] diagonalDirections = {{1,1}, {1,-1}};
for (int[] dir : diagonalDirections) {
int dx = dir[0], dy = dir[1];
int threat = checkDirectionalThreat(x, y, dx, dy, player);
if (threat > 0) {
return true;
}
}
return false;
}
// 检查是否是中间位置
private boolean isCentralPosition(int x, int y) {
// 定义中间区域范围(棋盘的中间区域,大约是3-11的范围)
int minCentral = 3;
int maxCentral = BOARD_SIZE - 4; // 11 for 15x15 board
return x >= minCentral && x <= maxCentral && y >= minCentral && y <= maxCentral;
}
// 检查是否靠近边界
private boolean isNearBoundary(int x, int y) {
// 定义边界范围(距离边缘2格以内)
int boundaryThreshold = 2;
return x <= boundaryThreshold || x >= BOARD_SIZE - 1 - boundaryThreshold ||
y <= boundaryThreshold || y >= BOARD_SIZE - 1 - boundaryThreshold;
}
// 检查中间位置的连子威胁
private int checkCentralThreat(int x, int y, int player) {
int maxThreat = 0;
int[][] directions = {{1,0}, {0,1}, {1,1}, {1,-1}};
// 检查是否在中间位置形成连子
for (int[] dir : directions) {
int dx = dir[0], dy = dir[1];
int centralCount = countCentralPieces(x, y, dx, dy, player);
// 如果在中间位置有3个或更多连子,这是一个高威胁
if (centralCount >= 3) {
int threatScore = centralCount * 25000; // 中间连子威胁基础分数
maxThreat = Math.max(maxThreat, threatScore);
}
}
return maxThreat;
}
// 计算中间位置的连子数量
private int countCentralPieces(int x, int y, int dx, int dy, int player) {
int count = 1; // 包括当前位置
// 正向检查
int tx = x + dx, ty = y + dy;
while (tx >= 0 && tx < BOARD_SIZE && ty >= 0 && ty < BOARD_SIZE && isCentralPosition(tx, ty)) {
if (board[tx][ty] == player) {
count++;
} else {
break;
}
tx += dx;
ty += dy;
}
// 反向检查
tx = x - dx;
ty = y - dy;
while (tx >= 0 && tx < BOARD_SIZE && ty >= 0 && ty < BOARD_SIZE && isCentralPosition(tx, ty)) {
if (board[tx][ty] == player) {
count++;
} else {
break;
}
tx -= dx;
ty -= dy;
}
return count;
}
private int evaluateSurroundings(int x, int y) {
int score = 0;
int[][] directions = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1}};
int aiCount = 0;
int playerCount = 0;
int emptyCount = 0;
int diagonalAiCount = 0;
int diagonalPlayerCount = 0;
// 检查周围8个方向的棋子分布
for (int[] dir : directions) {
int nx = x + dir[0];
int ny = y + dir[1];
boolean isDiagonal = Math.abs(dir[0]) == 1 && Math.abs(dir[1]) == 1;
if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE) {
if (board[nx][ny] == 1) {
playerCount++;
if (isDiagonal) diagonalPlayerCount++;
} else if (board[nx][ny] == 2) {
aiCount++;
if (isDiagonal) diagonalAiCount++;
} else {
emptyCount++;
}
}
}
// 根据周围棋子分布评分
// 如果周围有己方棋子,增加权重
if (aiCount > 0) {
score += aiCount * 250; // 提高己方棋子权重
// 对角线方向的己方棋子额外加分
score += diagonalAiCount * 100;
}
// 如果周围有对方棋子,增加防守权重
if (playerCount > 0) {
score += playerCount * 300; // 大幅提高防守权重
// 对角线方向的对方棋子额外加分
score += diagonalPlayerCount * 180; // 提高对角线防守权重
}
// 如果周围有空位,增加灵活性权重
score += emptyCount * 60;
// 如果周围同时有己方和对方棋子,这是一个交汇点,增加权重
if (aiCount > 0 && playerCount > 0) {
score += 400; // 提高交汇点权重
// 如果对角线上有交汇,额外加分
if (diagonalAiCount > 0 && diagonalPlayerCount > 0) {
score += 200;
}
}
return score;
}
private int evaluateDirection(int x, int y, int dx, int dy, int player) {
int count = 1;
int space = 0;
int score = 0;
int maxSpace = 2;
boolean isDiagonal = Math.abs(dx) == 1 && Math.abs(dy) == 1;
int consecutiveCount = 1;
boolean isLive = true;
boolean hitBoundary = false;
// 正向检查
int tx = x + dx, ty = y + dy;
while (tx >= 0 && tx < BOARD_SIZE && ty >= 0 && ty < BOARD_SIZE && space <= maxSpace) {
if (board[tx][ty] == player) {
count++;
consecutiveCount++;
} else if (board[tx][ty] == 0) {
space++;
consecutiveCount = 0;
} else {
isLive = false;
break;
}
tx += dx;
ty += dy;
}
// 检查反向是否碰到边界
if (tx < 0 || tx >= BOARD_SIZE || ty < 0 || ty >= BOARD_SIZE) {
hitBoundary = true;
}
// 反向检查
tx = x - dx;
ty = y - dy;
int reverseConsecutive = 1; // 反向连续计数
while (tx >= 0 && tx < BOARD_SIZE && ty >= 0 && ty < BOARD_SIZE && space <= maxSpace) {
if (board[tx][ty] == player) {
count++;
reverseConsecutive++;
} else if (board[tx][ty] == 0) {
space++;
consecutiveCount = 0;
} else {
isLive = false;
break;
}
tx -= dx;
ty -= dy;
}
// 取最大连续值
consecutiveCount = Math.max(consecutiveCount, reverseConsecutive);
// 根据连子数量、空位和活度评分
if (count >= 4) {
score = isLive ? 20000 : 10000; // 进一步提高活四和冲四权重
} else if (count == 3) {
if (space >= 2) { // 活三
score = isLive ? 8000 : 4000; // 大幅提高活三权重
} else { // 眠三
score = 2000; // 提高眠三权重
}
} else if (count == 2) {
if (space >= 2) { // 活二
score = isLive ? 1500 : 600; // 提高活二权重
} else { // 眠二
score = 300; // 提高眠二权重
}
} else {
score = space >= 2 ? 150 : 30; // 提高基础权重
}
// 斜向加权
if (isDiagonal) {
score = (int)(score * 1.8); // 进一步增加斜向权重
if ((x == 0 || x == BOARD_SIZE-1) && (y == 0 || y == BOARD_SIZE-1)) {
score = (int)(score * 1.5); // 提高斜角权重
}
}
// 如果碰到边界,降低评分权重
if (hitBoundary) {
// 如果是进攻方(AI),边界情况下降低权重更多
if (player == 2) {
score = (int)(score * 0.6); // 边界情况下大幅降低AI进攻权重
} else {
score = (int)(score * 0.8); // 边界情况下适当降低防守权重
}
}
// 根据连续性额外加分
if (consecutiveCount >= 3) {
score += consecutiveCount * 500; // 连续棋子额外加分
}
return score;
}
private int checkThreatLevel(int x, int y, int player) {
int maxThreat = 0;
int[][] directions = {{1,0}, {0,1}, {1,1}, {1,-1}};
// 检查每个方向的威胁
for (int[] dir : directions) {
int dx = dir[0], dy = dir[1];
int threat = checkDirectionalThreat(x, y, dx, dy, player);
maxThreat = Math.max(maxThreat, threat);
}
return maxThreat;
}
private int checkDirectionalThreat(int x, int y, int dx, int dy, int player) {
int count = 1;
int emptyBefore = 0;
int emptyAfter = 0;
boolean isBlocked = false;
boolean hitBoundary = false;
int consecutiveCount = 1; // 连续棋子计数
// 向一个方向检查
int tx = x + dx, ty = y + dy;
while (tx >= 0 && tx < BOARD_SIZE && ty >= 0 && ty < BOARD_SIZE) {
if (board[tx][ty] == player) {
count++;
consecutiveCount++;
} else if (board[tx][ty] == 0) {
emptyAfter++;
consecutiveCount = 0;
break;
} else {
isBlocked = true;
break;
}
tx += dx;
ty += dy;
}
// 检查是否碰到边界
if (tx < 0 || tx >= BOARD_SIZE || ty < 0 || ty >= BOARD_SIZE) {
hitBoundary = true;
}
// 向相反方向检查
tx = x - dx;
ty = y - dy;
int reverseConsecutive = 1; // 反向连续计数
while (tx >= 0 && tx < BOARD_SIZE && ty >= 0 && ty < BOARD_SIZE) {
if (board[tx][ty] == player) {
count++;
reverseConsecutive++;
} else if (board[tx][ty] == 0) {
emptyBefore++;
break;
} else {
isBlocked = true;
break;
}
tx -= dx;
ty -= dy;
}
// 取最大连续值
consecutiveCount = Math.max(consecutiveCount, reverseConsecutive);
// 评估威胁等级
if (count >= 4) {
return hitBoundary ? 50000 : 70000; // 边界情况下降低连四威胁权重
} else if (count == 3) {
// 区分活三和眠三
if (emptyBefore + emptyAfter >= 2) {
// 活三威胁
boolean isDiagonal = Math.abs(dx) == Math.abs(dy);
int baseScore = isBlocked ? 25000 : 40000; // 提高活三威胁权重
// 对角线方向的活三威胁更危险
// 如果碰到边界,降低威胁评估
if (hitBoundary) {
baseScore = (int)(baseScore * 0.6);
}
return isDiagonal ? (int)(baseScore * 1.5) : baseScore;
} else {
// 眠三威胁
return hitBoundary ? 9000 : 15000; // 边界情况下降低眠三威胁权重
}
} else if (count == 2 && emptyBefore + emptyAfter >= 2) {
// 活二威胁
boolean isDiagonal = Math.abs(dx) == Math.abs(dy);
int baseScore = isBlocked ? 8000 : 12000; // 提高活二威胁权重
// 连续的活二比不连续的更危险
if (consecutiveCount >= 2) {
baseScore = (int)(baseScore * 1.3);
}
// 如果碰到边界,降低威胁评估
if (hitBoundary) {
baseScore = (int)(baseScore * 0.5);
}
return isDiagonal ? (int)(baseScore * 1.4) : baseScore;
}
return 0;
}
private boolean checkWin(int x, int y) {
int[][] directions = {{1,0}, {0,1}, {1,1}, {1,-1}};
for (int[] dir : directions) {
int count = 1;
int dx = dir[0], dy = dir[1];
// 正向检查
int tx = x + dx, ty = y + dy;
while (tx >= 0 && tx < BOARD_SIZE &&
ty >= 0 && ty < BOARD_SIZE &&
board[tx][ty] == currentPlayer) {
count++;
tx += dx;
ty += dy;
}
// 反向检查
tx = x - dx; ty = y - dy;
while (tx >= 0 && tx < BOARD_SIZE &&
ty >= 0 && ty < BOARD_SIZE &&
board[tx][ty] == currentPlayer) {
count++;
tx -= dx;
ty -= dy;
}
if (count >= 5) return true;
}
return false;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制渐变背景
GradientPaint gradient = new GradientPaint(
0, 0, new Color(243, 224, 195),
getWidth(), getHeight(), new Color(212, 167, 106)
);
g2d.setPaint(gradient);
g2d.fillRect(0, 0, getWidth(), getHeight());
// 绘制网格线
g2d.setColor(new Color(139, 69, 19, 180));
g2d.setStroke(new BasicStroke(1.0f));
for (int i = 0; i < BOARD_SIZE; i++) {
int pos = MARGIN + i * CELL_SIZE;
g2d.drawLine(MARGIN, pos, MARGIN + (BOARD_SIZE-1)*CELL_SIZE, pos);
g2d.drawLine(pos, MARGIN, pos, MARGIN + (BOARD_SIZE-1)*CELL_SIZE);
}
// 绘制棋子
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] != 0) {
int x = MARGIN + i*CELL_SIZE - CELL_SIZE/2 + 2;
int y = MARGIN + j*CELL_SIZE - CELL_SIZE/2 + 2;
int size = CELL_SIZE - 4;
// 绘制棋子阴影
g2d.setColor(new Color(0, 0, 0, 50));
g2d.fillOval(x + 2, y + 2, size, size);
// 绘制棋子本体
Color pieceColor = board[i][j] == 1 ? Color.BLACK : Color.WHITE;
GradientPaint pieceGradient = new GradientPaint(
x, y, pieceColor,
x + size, y + size,
board[i][j] == 1 ? new Color(40, 40, 40) : new Color(220, 220, 220)
);
g2d.setPaint(pieceGradient);
g2d.fillOval(x, y, size, size);
// 绘制棋子高光
if (board[i][j] == 2) {
g2d.setColor(new Color(255, 255, 255, 100));
g2d.fillOval(x + size/4, y + size/4, size/4, size/4);
}
}
}
}
}
// 创建样式化按钮
private JButton createStyledButton(String text) {
JButton button = new JButton(text);
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
button.setBackground(new Color(205, 133, 63));
button.setForeground(Color.WHITE);
button.setFocusPainted(false);
button.setBorder(BorderFactory.createRaisedBevelBorder());
return button;
}
// 创建样式化复选框
private JCheckBox createStyledCheckBox(String text) {
JCheckBox checkBox = new JCheckBox(text);
checkBox.setFont(new Font("微软雅黑", Font.PLAIN, 14));
checkBox.setBackground(new Color(243, 224, 195));
checkBox.setFocusPainted(false);
return checkBox;
}
// 保存游戏记录到文件
private void saveGame() {
try {
// 创建保存对话框
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("保存对局记录");
// 设置默认文件名(使用当前时间)
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
String defaultFileName = "gobang_" + sdf.format(new Date()) + ".txt";
fileChooser.setSelectedFile(new File(defaultFileName));
// 显示保存对话框
int userSelection = fileChooser.showSaveDialog(parentFrame);
if (userSelection == JFileChooser.APPROVE_OPTION) {
File fileToSave = fileChooser.getSelectedFile();
// 如果文件已存在,询问是否覆盖
if (fileToSave.exists()) {
int response = JOptionPane.showConfirmDialog(parentFrame,
"文件已存在,是否覆盖?", "确认覆盖",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (response != JOptionPane.YES_OPTION) {
return;
}
}
// 写入文件
try (PrintWriter writer = new PrintWriter(new FileWriter(fileToSave))) {
// 写入游戏信息头
writer.println("五子棋对局记录");
writer.println("时间: " + sdf.format(new Date()));
writer.println("模式: " + (vsAI ? "人机对战" : "双人对战"));
writer.println("胜方: " + (currentPlayer == 1 ? "黑方" : "白方"));
writer.println("棋谱:");
// 写入棋谱(将moveHistory转换为ArrayList以便按顺序访问)
ArrayList<int[]> moves = new ArrayList<>(moveHistory);
for (int i = 0; i < moves.size(); i++) {
int[] move = moves.get(i);
writer.println((i+1) + ". " + (char)('A' + move[0]) + (move[1] + 1) +
" " + (move[2] == 1 ? "黑" : "白"));
}
JOptionPane.showMessageDialog(parentFrame,
"对局记录已保存至: " + fileToSave.getAbsolutePath(),
"保存成功", JOptionPane.INFORMATION_MESSAGE);
}
}
} catch (IOException e) {
JOptionPane.showMessageDialog(parentFrame,
"保存失败: " + e.getMessage(),
"错误", JOptionPane.ERROR_MESSAGE);
}
}
// 添加主方法,作为程序入口点
public static void main(String[] args) {
JFrame frame = new JFrame("五子棋");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 创建菜单栏
JMenuBar menuBar = new JMenuBar();
// 游戏菜单
JMenu gameMenu = new JMenu("游戏");
gameMenu.setFont(new Font("微软雅黑", Font.PLAIN, 14));
JMenuItem newGameItem = new JMenuItem("新游戏");
JMenuItem exitItem = new JMenuItem("退出");
gameMenu.add(newGameItem);
gameMenu.addSeparator();
gameMenu.add(exitItem);
// 设置菜单
JMenu settingsMenu = new JMenu("设置");
settingsMenu.setFont(new Font("微软雅黑", Font.PLAIN, 14));
JCheckBoxMenuItem aiModeItem = new JCheckBoxMenuItem("人机对战");
settingsMenu.add(aiModeItem);
// 历史菜单
JMenu historyMenu = new JMenu("历史对局");
historyMenu.setFont(new Font("微软雅黑", Font.PLAIN, 14));
JMenuItem loadGameItem = new JMenuItem("加载对局");
JMenuItem historyListItem = new JMenuItem("历史记录");
historyMenu.add(loadGameItem);
historyMenu.add(historyListItem);
// 添加菜单到菜单栏
menuBar.add(gameMenu);
menuBar.add(settingsMenu);
menuBar.add(historyMenu);
// 设置菜单事件
newGameItem.addActionListener(e -> {
Gobang gobang = (Gobang)frame.getContentPane().getComponent(0);
gobang.resetGame();
});
exitItem.addActionListener(e -> System.exit(0));
aiModeItem.addActionListener(e -> {
Gobang gobang = (Gobang)frame.getContentPane().getComponent(0);
gobang.vsAI = aiModeItem.isSelected();
gobang.resetGame();
});
loadGameItem.addActionListener(e -> {
JOptionPane.showMessageDialog(frame, "加载对局功能即将推出", "提示", JOptionPane.INFORMATION_MESSAGE);
});
historyListItem.addActionListener(e -> {
JOptionPane.showMessageDialog(frame, "历史记录功能即将推出", "提示", JOptionPane.INFORMATION_MESSAGE);
});
// 设置菜单栏
frame.setJMenuBar(menuBar);
Gobang gobang = new Gobang(frame);
frame.setLayout(new BorderLayout());
frame.add(gobang, BorderLayout.CENTER);
frame.add(gobang.controlPanel, BorderLayout.NORTH); // 控制面板移至顶部
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}