ADD file via upload

main
pg26wfsba 4 months ago
parent a2ae9f645c
commit 7deb4b23ce

@ -0,0 +1,712 @@
package fivepieces;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.sound.sampled.*;
import java.util.Stack;
public class GomokuGame extends JFrame{
private static final int BOARD_SIZE = 15;
private static final int CELL_SIZE = 40;
private static final int MARGIN = 30;
private static final int PIECE_SIZE = 36;
private int[][] board = new int[BOARD_SIZE][BOARD_SIZE]; // 0: empty, 1: black, 2: white
private boolean blackTurn = true;
private boolean gameOver = false;
private boolean aiMode = false;
private boolean playerIsBlack = true; // 玩家执黑先手
private Clip backgroundMusic;
private Stack<int[][]> history = new Stack<>();
private Stack<int[][]> redoStack = new Stack<>();
private static final int AI_SEARCH_DEPTH = 3; // 搜索深度
private static final int WIN_SCORE = 100000; // 获胜分数
private static final int BLOCK_WIN_SCORE = 50000; // 阻止获胜分数
public GomokuGame() {
setTitle("五子棋游戏");
setSize(BOARD_SIZE * CELL_SIZE + 2 * MARGIN, BOARD_SIZE * CELL_SIZE + 2 * MARGIN + 100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// 加载背景音乐
loadBackgroundMusic();
// 添加游戏面板
add(new GamePanel(), BorderLayout.CENTER);
// 添加控制面板
JPanel controlPanel = new JPanel(new GridLayout(2, 3, 5, 5));
JButton restartButton = new JButton("重新开始");
restartButton.addActionListener(e -> restartGame());
JButton musicButton = new JButton("背景音乐 开/关");
musicButton.addActionListener(e -> toggleMusic());
JButton aiButton = new JButton("AI对战 开/关");
aiButton.addActionListener(e -> toggleAIMode());
JButton saveButton = new JButton("存档");
saveButton.addActionListener(e -> saveGame());
JButton loadButton = new JButton("读档");
loadButton.addActionListener(e -> loadGame());
JButton undoButton = new JButton("悔棋");
undoButton.addActionListener(e -> undoMove());
controlPanel.add(restartButton);
controlPanel.add(musicButton);
controlPanel.add(aiButton);
controlPanel.add(saveButton);
controlPanel.add(loadButton);
controlPanel.add(undoButton);
add(controlPanel, BorderLayout.SOUTH);
setVisible(true);
}
private void loadBackgroundMusic() {
try {
// 注意你需要准备一个background.wav文件放在项目根目录下
// 或者修改为你的音乐文件路径
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(
new File("one.wav").getAbsoluteFile());
backgroundMusic = AudioSystem.getClip();
backgroundMusic.open(audioInputStream);
backgroundMusic.loop(Clip.LOOP_CONTINUOUSLY);
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "无法加载背景音乐: " + e.getMessage());
}
}
private void toggleMusic() {
if (backgroundMusic != null) {
if (backgroundMusic.isRunning()) {
backgroundMusic.stop();
} else {
backgroundMusic.start();
}
}
}
private void toggleAIMode() {
aiMode = !aiMode;
if (aiMode) {
int option = JOptionPane.showOptionDialog(this,
"选择执子颜色", "AI对战",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
null, new String[]{"执黑先手", "执白后手"}, "执黑先手");
playerIsBlack = (option == 0);
// 如果AI先手让它先走一步
if (!playerIsBlack && aiMode) {
makeAIMove();
}
}
restartGame();
}
private void saveGameState() {
// 深拷贝当前棋盘状态
int[][] currentState = new int[BOARD_SIZE][BOARD_SIZE];
for (int i = 0; i < BOARD_SIZE; i++) {
System.arraycopy(board[i], 0, currentState[i], 0, BOARD_SIZE);
}
history.push(currentState);
redoStack.clear(); // 新的操作后,重做栈清空
}
private void undoMove() {
if (history.size() > 1) { // 保留初始空棋盘状态
redoStack.push(history.pop());
board = history.peek();
blackTurn = !blackTurn;
gameOver = false;
repaint();
} else {
JOptionPane.showMessageDialog(this, "无法再悔棋了");
}
}
private void redoMove() {
if (!redoStack.isEmpty()) {
history.push(redoStack.pop());
board = history.peek();
blackTurn = !blackTurn;
repaint();
}
}
private void saveGame() {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("gomoku_save.dat"))) {
SaveData data = new SaveData(board, blackTurn, gameOver, aiMode, playerIsBlack);
oos.writeObject(data);
JOptionPane.showMessageDialog(this, "游戏已保存");
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "保存游戏失败: " + e.getMessage());
}
}
private void loadGame() {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("gomoku_save.dat"))) {
SaveData data = (SaveData) ois.readObject();
board = data.getBoard();
blackTurn = data.isBlackTurn();
gameOver = data.isGameOver();
aiMode = data.isAiMode();
playerIsBlack = data.isPlayerIsBlack();
// 重置历史栈
history.clear();
redoStack.clear();
saveGameState(); // 保存当前状态到历史
repaint();
JOptionPane.showMessageDialog(this, "游戏已加载");
} catch (IOException | ClassNotFoundException e) {
JOptionPane.showMessageDialog(this, "加载游戏失败: " + e.getMessage());
}
}
private void restartGame() {
board = new int[BOARD_SIZE][BOARD_SIZE];
blackTurn = true;
gameOver = false;
// 重置历史栈
history.clear();
redoStack.clear();
saveGameState(); // 保存初始状态
// 如果AI先手让它先走一步
if (aiMode && !playerIsBlack) {
makeAIMove();
}
repaint();
}
private void makeMove(int x, int y) {
if (gameOver) return;
if (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE && board[x][y] == 0) {
saveGameState();
board[x][y] = blackTurn ? 1 : 2;
if (checkWin(x, y)) {
gameOver = true;
JOptionPane.showMessageDialog(GomokuGame.this,
(blackTurn ? "黑方" : "白方") + "获胜!");
}
blackTurn = !blackTurn;
repaint();
// AI模式且轮到AI下棋
if (aiMode && !gameOver && ((playerIsBlack && !blackTurn) || (!playerIsBlack && blackTurn))) {
Timer timer = new Timer(500, e -> {
makeAIMove();
((Timer)e.getSource()).stop();
});
timer.setRepeats(false);
timer.start();
}
}
}
private void makeAIMove() {
// 使用MiniMax算法寻找最佳移动
int[] bestMove = findBestMoveWithMiniMax();
if (bestMove != null) {
makeMove(bestMove[0], bestMove[1]);
}
}
private int[] findBestMoveWithMiniMax() {
int player = playerIsBlack ? 2 : 1; // AI的棋子颜色
int opponent = playerIsBlack ? 1 : 2; // 玩家的棋子颜色
int bestScore = Integer.MIN_VALUE;
int[] bestMove = null;
// 获取所有可能的移动
java.util.List<int[]> possibleMoves = generatePossibleMoves();
for (int[] move : possibleMoves) {
int x = move[0], y = move[1];
// 尝试这个移动
board[x][y] = player;
// 计算这个移动的分数
int score = miniMax(AI_SEARCH_DEPTH - 1, false, player, opponent,
Integer.MIN_VALUE, Integer.MAX_VALUE);
// 撤销移动
board[x][y] = 0;
// 更新最佳移动
if (score > bestScore) {
bestScore = score;
bestMove = new int[]{x, y};
}
}
return bestMove;
}
private int miniMax(int depth, boolean isMaximizing, int player, int opponent,
int alpha, int beta) {
// 检查游戏是否结束或达到最大深度
if (depth == 0 || isGameOver()) {
return evaluateBoard(player, opponent);
}
// 生成可能的移动
java.util.List<int[]> possibleMoves = generatePossibleMoves();
if (isMaximizing) {
int bestScore = Integer.MIN_VALUE;
for (int[] move : possibleMoves) {
int x = move[0], y = move[1];
// 尝试这个移动
board[x][y] = player;
// 递归调用
int score = miniMax(depth - 1, false, player, opponent, alpha, beta);
// 撤销移动
board[x][y] = 0;
// 更新最佳分数
bestScore = Math.max(bestScore, score);
alpha = Math.max(alpha, bestScore);
// Alpha-Beta剪枝
if (beta <= alpha) {
break;
}
}
return bestScore;
} else {
int bestScore = Integer.MAX_VALUE;
for (int[] move : possibleMoves) {
int x = move[0], y = move[1];
// 尝试这个移动
board[x][y] = opponent;
// 递归调用
int score = miniMax(depth - 1, true, player, opponent, alpha, beta);
// 撤销移动
board[x][y] = 0;
// 更新最佳分数
bestScore = Math.min(bestScore, score);
beta = Math.min(beta, bestScore);
// Alpha-Beta剪枝
if (beta <= alpha) {
break;
}
}
return bestScore;
}
}
private java.util.List<int[]> generatePossibleMoves() {
java.util.List<int[]> moves = new java.util.ArrayList<>();
// 首先检查是否有紧急情况(立即获胜或阻止对手获胜)
int player = playerIsBlack ? 2 : 1;
int opponent = playerIsBlack ? 1 : 2;
// 1. 检查AI是否可以立即获胜
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == 0) {
board[i][j] = player;
if (checkWin(i, j)) {
board[i][j] = 0;
moves.clear();
moves.add(new int[]{i, j});
return moves; // 直接返回获胜位置
}
board[i][j] = 0;
}
}
}
// 2. 检查是否需要阻止玩家获胜
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == 0) {
board[i][j] = opponent;
if (checkWin(i, j)) {
board[i][j] = 0;
moves.clear();
moves.add(new int[]{i, j});
return moves; // 直接返回阻止位置
}
board[i][j] = 0;
}
}
}
// 3. 生成所有可能的移动,但优先考虑靠近已有棋子的位置
boolean[][] visited = new boolean[BOARD_SIZE][BOARD_SIZE];
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] != 0) {
// 检查周围的空位
for (int dx = -2; dx <= 2; dx++) {
for (int dy = -2; dy <= 2; dy++) {
int x = i + dx;
int y = j + dy;
if (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE
&& board[x][y] == 0 && !visited[x][y]) {
moves.add(new int[]{x, y});
visited[x][y] = true;
}
}
}
}
}
}
// 如果没有找到附近的空位(游戏刚开始),返回中心区域的位置
if (moves.isEmpty()) {
int center = BOARD_SIZE / 2;
for (int i = center - 2; i <= center + 2; i++) {
for (int j = center - 2; j <= center + 2; j++) {
if (i >= 0 && i < BOARD_SIZE && j >= 0 && j < BOARD_SIZE
&& board[i][j] == 0) {
moves.add(new int[]{i, j});
}
}
}
}
return moves;
}
private int evaluateBoard(int player, int opponent) {
int score = 0;
// 评估所有可能的五元组
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == player) {
score += evaluatePosition(i, j, player, opponent);
} else if (board[i][j] == opponent) {
score -= evaluatePosition(i, j, opponent, player);
}
}
}
return score;
}
private int evaluatePosition(int x, int y, int player, int opponent) {
int score = 0;
int[][] directions = {{1, 0}, {0, 1}, {1, 1}, {1, -1}};
for (int[] dir : directions) {
// 检查这个方向的棋型
int playerCount = 1; // 当前棋子
int openEnds = 0;
boolean blocked1 = false, blocked2 = false;
// 正向检查
for (int i = 1; i <= 4; i++) {
int nx = x + i * dir[0];
int ny = y + i * dir[1];
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) {
blocked1 = true;
break;
}
if (board[nx][ny] == player) {
playerCount++;
} else if (board[nx][ny] == opponent) {
blocked1 = true;
break;
} else {
openEnds++;
break;
}
}
// 反向检查
for (int i = 1; i <= 4; i++) {
int nx = x - i * dir[0];
int ny = y - i * dir[1];
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) {
blocked2 = true;
break;
}
if (board[nx][ny] == player) {
playerCount++;
} else if (board[nx][ny] == opponent) {
blocked2 = true;
break;
} else {
openEnds++;
break;
}
}
// 根据棋型评分
score += evaluatePattern(playerCount, openEnds);
}
return score;
}
private int evaluatePattern(int playerCount, int openEnds) {
if (playerCount >= 5) return WIN_SCORE; // 五连
if (openEnds == 0) return 0; // 被阻挡的两端
switch (playerCount) {
case 4:
return openEnds == 2 ? BLOCK_WIN_SCORE : 10000; // 活四或冲四
case 3:
return openEnds == 2 ? 5000 : 1000; // 活三或眠三
case 2:
return openEnds == 2 ? 200 : 50; // 活二或眠二
case 1:
return openEnds == 2 ? 10 : 1; // 活一或眠一
default:
return 0;
}
}
private boolean isGameOver() {
// 检查棋盘是否已满
boolean boardFull = true;
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == 0) {
boardFull = false;
break;
}
}
if (!boardFull) break;
}
return boardFull;
}
private int[] findBestMove() {
int player = playerIsBlack ? 2 : 1; // AI的棋子颜色
// 1. 检查是否有可以立即获胜的位置
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == 0) {
board[i][j] = player;
if (checkWin(i, j)) {
board[i][j] = 0;
return new int[]{i, j};
}
board[i][j] = 0;
}
}
}
// 2. 检查是否需要阻止玩家获胜
int opponent = playerIsBlack ? 1 : 2;
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == 0) {
board[i][j] = opponent;
if (checkWin(i, j)) {
board[i][j] = 0;
return new int[]{i, j};
}
board[i][j] = 0;
}
}
}
// 3. 随机选择一个空位置
java.util.List<int[]> emptyCells = new java.util.ArrayList<>();
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == 0) {
emptyCells.add(new int[]{i, j});
}
}
}
if (!emptyCells.isEmpty()) {
return emptyCells.get((int)(Math.random() * emptyCells.size()));
}
return null;
}
private boolean checkWin(int x, int y) {
int player = board[x][y];
int[][] directions = {{1, 0}, {0, 1}, {1, 1}, {1, -1}};
for (int[] dir : directions) {
int count = 1;
// 正向检查
for (int i = 1; i <= 4; i++) {
int nx = x + i * dir[0];
int ny = y + i * dir[1];
if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] == player) {
count++;
} else {
break;
}
}
// 反向检查
for (int i = 1; i <= 4; i++) {
int nx = x - i * dir[0];
int ny = y - i * dir[1];
if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] == player) {
count++;
} else {
break;
}
}
if (count >= 5) {
return true;
}
}
return false;
}
class GamePanel extends JPanel {
public GamePanel() {
setPreferredSize(new Dimension(
BOARD_SIZE * CELL_SIZE + 2 * MARGIN,
BOARD_SIZE * CELL_SIZE + 2 * MARGIN));
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (gameOver) return;
if (aiMode && ((playerIsBlack && !blackTurn) || (!playerIsBlack && blackTurn))) {
return; // AI回合忽略玩家点击
}
int x = (e.getX() - MARGIN + CELL_SIZE / 2) / CELL_SIZE;
int y = (e.getY() - MARGIN + CELL_SIZE / 2) / CELL_SIZE;
makeMove(x, y);
}
});
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// 绘制棋盘背景
g.setColor(new Color(220, 179, 92));
g.fillRect(MARGIN, MARGIN, BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE);
// 绘制网格线
g.setColor(Color.BLACK);
for (int i = 0; i < BOARD_SIZE; i++) {
g.drawLine(MARGIN + i * CELL_SIZE, MARGIN,
MARGIN + i * CELL_SIZE, MARGIN + (BOARD_SIZE - 1) * CELL_SIZE);
g.drawLine(MARGIN, MARGIN + i * CELL_SIZE,
MARGIN + (BOARD_SIZE - 1) * CELL_SIZE, MARGIN + i * CELL_SIZE);
}
// 绘制棋子
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == 1) {
g.setColor(Color.BLACK);
g.fillOval(MARGIN + i * CELL_SIZE - PIECE_SIZE / 2,
MARGIN + j * CELL_SIZE - PIECE_SIZE / 2,
PIECE_SIZE, PIECE_SIZE);
} else if (board[i][j] == 2) {
g.setColor(Color.WHITE);
g.fillOval(MARGIN + i * CELL_SIZE - PIECE_SIZE / 2,
MARGIN + j * CELL_SIZE - PIECE_SIZE / 2,
PIECE_SIZE, PIECE_SIZE);
}
}
}
// 显示当前回合和模式
g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.BOLD, 16));
String modeText = aiMode ? (playerIsBlack ? "AI模式(玩家执黑)" : "AI模式(玩家执白)") : "双人对战模式";
g.drawString("模式: " + modeText, MARGIN, MARGIN + BOARD_SIZE * CELL_SIZE + 20);
g.drawString("当前回合: " + (blackTurn ? "黑方" : "白方"),
MARGIN, MARGIN + BOARD_SIZE * CELL_SIZE + 40);
}
}
// 用于游戏存档的序列化类
static class SaveData implements Serializable {
private static final long serialVersionUID = 1L;
private int[][] board;
private boolean blackTurn;
private boolean gameOver;
private boolean aiMode;
private boolean playerIsBlack;
public SaveData(int[][] board, boolean blackTurn, boolean gameOver, boolean aiMode, boolean playerIsBlack) {
this.board = new int[BOARD_SIZE][BOARD_SIZE];
for (int i = 0; i < BOARD_SIZE; i++) {
System.arraycopy(board[i], 0, this.board[i], 0, BOARD_SIZE);
}
this.blackTurn = blackTurn;
this.gameOver = gameOver;
this.aiMode = aiMode;
this.playerIsBlack = playerIsBlack;
}
public int[][] getBoard() {
return board;
}
public boolean isBlackTurn() {
return blackTurn;
}
public boolean isGameOver() {
return gameOver;
}
public boolean isAiMode() {
return aiMode;
}
public boolean isPlayerIsBlack() {
return playerIsBlack;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new GomokuGame());
}
}
Loading…
Cancel
Save