消球游戏
设计一个程序实现消球游戏:
在棋盘内,一开始随机初始化三个不同色小球,一次可移动一个小球至空白位置,当同色5个小球连成直线,横、竖、对角均可,则小球消除并得分。消除1个小球得1分, 当小球移动1次没有消除时,系统会自动随机产生三个小球。
基本要求:
(1)要求实现图形化界面,可视化显示棋盘布局和消球过程;
(2)要求实现棋谱的记录;
(3)要求实现人人对弈和人机对弈;
基本设置:游戏窗口包括一个用于显示游戏棋盘的画布,分数显示标签,当前玩家提示标签,和几个按钮(查看棋谱、结束游戏)以及一个下拉菜单用于选择游戏模式(单机训练、人人对弈、人机对弈)。
游戏棋盘:棋盘由9x9的网格组成,每个格子可以放置一个球。球的颜色有三种:红色、绿色和蓝色。
玩家交互:
点击棋盘时,根据选中的模式和当前玩家(玩家1、玩家2或AI),执行相应的动作。
玩家可以选择一个球然后点击另一个空白位置来移动球,如果移动合法(即通过空白格子连线可达),则完成移动。
游戏规则:
在每次移动后,系统会检查是否有五个或更多相连的同色球,如果有,则这些球会被消除,玩家得分增加。
如果玩家的移动没有导致消除,则会随机生成三个球在空白位置。
游戏中的AI会在其回合计算最佳移动策略,试图最大化消除或阻挠玩家。
得分和切换玩家:
游戏根据消除的球数给玩家计分。
玩家间交替操作,如果是人机对弈模式,会在玩家和AI之间交替。
游戏结束:
当没有足够空间生成新的球时,游戏结束。
玩家可以随时结束游戏,并查看当前得分。
结束时会提示玩家得分,并提供重新开始或结束游戏的选项。
记录和回放:
游戏的每次移动都会被记录到棋谱文件中。
玩家可以通过点击“查看棋谱”按钮来回看过去的游戏。
具体实现代码:
import javax.swing.*; import javax.swing.Timer; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.List; public class BallEliminationGame extends JFrame { private static final int BOARD_SIZE = 9; private static final String[] COLOR_NAMES = {"red", "green", "blue"}; private static final Color[] COLORS = {Color.RED, Color.GREEN, Color.BLUE}; private static final int BALL_SIZE = 50; private JPanel canvas; private JLabel scoreLabel1, scoreLabel2, currentPlayerLabel; private JButton recordButton, endGameButton; private JComboBox<String> modeMenu; private String currentPlayer; private String[][] board; private Map<String, Integer> playerScores; private List<String[]> moves; private List<List<String>> allGames; private Point selectedBall; private Timer aiTimer; private int currentGameIndex; public BallEliminationGame() { setTitle("消球游戏"); setSize(580, 580); // 设置初始大小 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new GridBagLayout()); setPreferredSize(new Dimension(580, 580)); // 设置首选大小 setMinimumSize(new Dimension(580, 580)); // 设置最小大小 // 初始化游戏变量 board = new String[BOARD_SIZE][BOARD_SIZE]; playerScores = new HashMap<>(); moves = new ArrayList<>(); allGames = new ArrayList<>(); selectedBall = null; currentPlayer = "玩家1"; currentGameIndex = 0; // 初始化游戏画布 canvas = new JPanel() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); drawBoard(g); // 绘制棋盘 } }; canvas.setPreferredSize(new Dimension(BOARD_SIZE * BALL_SIZE, BOARD_SIZE * BALL_SIZE)); canvas.setBackground(Color.WHITE); canvas.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { onCanvasClick(e); // 处理鼠标点击事件 } }); // 初始化UI组件 scoreLabel1 = new JLabel("玩家1得分: 0"); scoreLabel2 = new JLabel("玩家2得分: 0"); currentPlayerLabel = new JLabel("当前玩家: 玩家1"); recordButton = new JButton("查看棋谱"); recordButton.addActionListener(e -> showMoves()); // 查看棋谱按钮 endGameButton = new JButton("结束游戏"); endGameButton.addActionListener(e -> endGameManual()); // 结束游戏按钮 modeMenu = new JComboBox<>(new String[]{"训练模式", "人人对弈", "人机对弈"}); modeMenu.addActionListener(e -> resetGame()); // 模式选择菜单 aiTimer = new Timer(1000, e -> handleAIMove()); // AI移动定时器 GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 5; gbc.anchor = GridBagConstraints.CENTER; add(currentPlayerLabel, gbc); gbc.gridy = 1; add(canvas, gbc); gbc.gridwidth = 1; gbc.gridy = 2; gbc.anchor = GridBagConstraints.WEST; add(scoreLabel1, gbc); gbc.gridx = 1; add(scoreLabel2, gbc); JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); buttonPanel.add(recordButton); buttonPanel.add(endGameButton); buttonPanel.add(modeMenu); gbc.gridx = 2; gbc.gridwidth = 3; gbc.anchor = GridBagConstraints.EAST; add(buttonPanel, gbc); pack(); // 调整组件大小以适应首选尺寸 setLocationRelativeTo(null); // 窗口居中显示 initGame(); } // 初始化游戏 private void initGame() { for (int i = 0; i < BOARD_SIZE; i++) { Arrays.fill(board[i], null); // 清空棋盘 } playerScores.put("玩家1", 0); playerScores.put("玩家2", 0); playerScores.put("AI", 0); moves.clear(); selectedBall = null; currentPlayer = "玩家1"; generateBalls(3); // 生成初始小球 updateScores(); switchPlayer(); // 调整当前玩家标签的显示 loadAllGames(); // 加载所有棋谱记录 // 初始化 currentGameIndex 为最新一局记录的索引 currentGameIndex = allGames.size() - 1; canvas.repaint(); } // 重置游戏 private void resetGame() { initGame(); switchPlayer(); // 调整当前玩家标签的显示 canvas.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { onCanvasClick(e); // 重新添加鼠标点击事件 } }); if ("人机对弈".equals(modeMenu.getSelectedItem()) && "AI".equals(currentPlayer)) { aiTimer.stop(); // 停止AI计时器 } } // 生成小球 private void generateBalls(int count) { List<Point> emptyPositions = new ArrayList<>(); for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { if (board[i][j] == null) { emptyPositions.add(new Point(i, j)); // 记录空位置 } } } if (emptyPositions.size() < count) { if (!emptyPositions.isEmpty()) { Random rand = new Random(); Point pos = emptyPositions.remove(rand.nextInt(emptyPositions.size())); board[pos.x][pos.y] = COLOR_NAMES[rand.nextInt(COLOR_NAMES.length)]; } endGame(); // 如果空位置不足,则结束游戏 return; } Random rand = new Random(); for (int k = 0; k < count; k++) { Point pos = emptyPositions.remove(rand.nextInt(emptyPositions.size())); board[pos.x][pos.y] = COLOR_NAMES[rand.nextInt(COLOR_NAMES.length)]; } } // 绘制棋盘 private void drawBoard(Graphics g) { Graphics2D g2d = (Graphics2D) g; for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { int x1 = j * BALL_SIZE; int y1 = i * BALL_SIZE; g2d.setColor(Color.BLACK); g2d.drawRect(x1, y1, BALL_SIZE, BALL_SIZE); // 绘制棋盘网格 if (board[i][j] != null) { Color ballColor = getColor(board[i][j]); GradientPaint gradient = new GradientPaint(x1, y1, ballColor, x1 + BALL_SIZE, y1 + BALL_SIZE, ballColor.darker()); g2d.setPaint(gradient); g2d.fillOval(x1 + 5, y1 + 5, BALL_SIZE - 10, BALL_SIZE - 10); // 绘制球体 g2d.setColor(new Color(0, 0, 0, 50)); // 半透明黑色阴影 g2d.fillOval(x1 + 7, y1 + 7, BALL_SIZE - 10, BALL_SIZE - 10); g2d.setColor(Color.BLACK); g2d.drawOval(x1 + 5, y1 + 5, BALL_SIZE - 10, BALL_SIZE - 10); if (selectedBall != null && selectedBall.x == i && selectedBall.y == j) { g2d.setColor(Color.YELLOW); // 选中球的边框颜色 g2d.setStroke(new BasicStroke(3)); // 边框粗细 g2d.drawOval(x1 + 5, y1 + 5, BALL_SIZE - 10, BALL_SIZE - 10); g2d.setStroke(new BasicStroke(1)); } } } } } // 获取颜色 private Color getColor(String colorName) { for (int i = 0; i < COLOR_NAMES.length; i++) { if (COLOR_NAMES[i].equals(colorName)) { return COLORS[i]; } } return null; // 不应该发生 } // 处理棋盘点击事件 private void onCanvasClick(MouseEvent e) { int row = e.getY() / BALL_SIZE; int col = e.getX() / BALL_SIZE; if (row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE) { String mode = (String) modeMenu.getSelectedItem(); if ("训练模式".equals(mode)) { handlePlayerMove(row, col, "玩家1"); } else if ("人人对弈".equals(mode)) { handlePlayerMove(row, col, currentPlayer); } else if ("人机对弈".equals(mode)) { handlePlayerMove(row, col, "玩家1"); if ("AI".equals(currentPlayer)) { aiTimer.start(); } } canvas.repaint(); updateScores(); } } // 处理玩家移动 private void handlePlayerMove(int row, int col, String player) { if (selectedBall != null) { if (board[row][col] == null && isValidMove(selectedBall, new Point(row, col))) { board[row][col] = board[selectedBall.x][selectedBall.y]; board[selectedBall.x][selectedBall.y] = null; moves.add(new String[]{player, selectedBall.x + "," + selectedBall.y, row + "," + col}); selectedBall = null; if (!checkElimination(row, col, player)) { generateBalls(3); } switchPlayer(); } else { selectedBall = new Point(row, col); } } else if (board[row][col] != null) { selectedBall = new Point(row, col); } } // 处理AI移动 private void handleAIMove() { aiTimer.stop(); boolean moveMade = findAndMoveBallForAI(); if (!moveMade) { blockPlayer(); } canvas.repaint(); updateScores(); if ("AI".equals(currentPlayer)) { switchPlayer(); } } // 查找并执行AI最佳移动 private boolean findAndMoveBallForAI() { Point bestMoveStart = null, bestMoveEnd = null; int bestScore = -1; for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { if (board[i][j] != null) { for (int ni = 0; ni < BOARD_SIZE; ni++) { for (int nj = 0; nj < BOARD_SIZE; nj++) { if (board[ni][nj] == null && isValidMove(new Point(i, j), new Point(ni, nj))) { board[ni][nj] = board[i][j]; board[i][j] = null; int score = eliminationScore(ni, nj); board[i][j] = board[ni][nj]; board[ni][nj] = null; if (score > bestScore) { bestScore = score; bestMoveStart = new Point(i, j); bestMoveEnd = new Point(ni, nj); } } } } } } } if (bestMoveStart != null) { board[bestMoveEnd.x][bestMoveEnd.y] = board[bestMoveStart.x][bestMoveStart.y]; board[bestMoveStart.x][bestMoveStart.y] = null; moves.add(new String[]{"AI", bestMoveStart.x + "," + bestMoveStart.y, bestMoveEnd.x + "," + bestMoveEnd.y}); checkElimination(bestMoveEnd.x, bestMoveEnd.y, "AI"); return true; } return false; } // 模拟消除得分 private int eliminationScore(int row, int col) { int totalScore = 0; String color = board[row][col]; int[][] directions = {{1, 0}, {0, 1}, {1, 1}, {1, -1}}; for (int[] dir : directions) { int count = 1; for (int i = 1; i < 5; i++) { int r = row + dir[0] * i; int c = col + dir[1] * i; if (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && color.equals(board[r][c])) { count++; } else { break; } } for (int i = 1; i < 5; i++) { int r = row - dir[0] * i; int c = col - dir[1] * i; if (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && color.equals(board[r][c])) { count++; } else { break; } } if (count >= 5) { totalScore += (count - 1); } } return totalScore; } // 阻碍玩家 private void blockPlayer() { List<Point> emptyPositions = new ArrayList<>(); List<Point> startPositions = new ArrayList<>(); for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { if (board[i][j] == null) { emptyPositions.add(new Point(i, j)); } else { startPositions.add(new Point(i, j)); } } } if (!emptyPositions.isEmpty() && !startPositions.isEmpty()) { Random rand = new Random(); Point startPos = startPositions.get(rand.nextInt(startPositions.size())); Point endPos = emptyPositions.get(rand.nextInt(emptyPositions.size())); board[endPos.x][endPos.y] = board[startPos.x][startPos.y]; board[startPos.x][startPos.y] = null; moves.add(new String[]{"AI", startPos.x + "," + startPos.y, endPos.x + "," + endPos.y}); if (!checkElimination(endPos.x, endPos.y, "AI")) { generateBalls(3); } switchPlayer(); } } // 切换玩家 private void switchPlayer() { String mode = (String) modeMenu.getSelectedItem(); if ("人人对弈".equals(mode)) { currentPlayer = "玩家1".equals(currentPlayer) ? "玩家2" : "玩家1"; } else if ("人机对弈".equals(mode)) { currentPlayer = "玩家1".equals(currentPlayer) ? "AI" : "玩家1"; } if (!"训练模式".equals(mode)) { currentPlayerLabel.setText("当前玩家: " + currentPlayer); currentPlayerLabel.setVisible(true); } else { currentPlayerLabel.setVisible(false); } if ("人机对弈".equals(mode) && "AI".equals(currentPlayer) && !aiTimer.isRunning()) { aiTimer.start(); } } // 检查消除 private boolean checkElimination(int row, int col, String player) { boolean eliminated = false; String color = board[row][col]; int[][] directions = {{1, 0}, {0, 1}, {1, 1}, {1, -1}}; for (int[] dir : directions) { int count = 1; for (int i = 1; i < 5; i++) { int r = row + dir[0] * i; int c = col + dir[1] * i; if (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && color.equals(board[r][c])) { count++; } else { break; } } for (int i = 1; i < 5; i++) { int r = row - dir[0] * i; int c = col - dir[1] * i; if (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && color.equals(board[r][c])) { count++; } else { break; } } if (count >= 5) { eliminated = true; for (int i = -4; i < 5; i++) { int r = row + dir[0] * i; int c = col + dir[1] * i; if (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && color.equals(board[r][c])) { board[r][c] = null; } } playerScores.put(player, playerScores.get(player) + (count - 1)); } } return eliminated; } // 验证移动是否合法 private boolean isValidMove(Point start, Point end) { if (start.equals(end)) return false; Queue<Point> queue = new LinkedList<>(); Set<Point> visited = new HashSet<>(); queue.add(start); visited.add(start); while (!queue.isEmpty()) { Point current = queue.poll(); if (current.equals(end)) return true; for (int[] dir : new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) { int nr = current.x + dir[0]; int nc = current.y + dir[1]; Point next = new Point(nr, nc); if (nr >= 0 && nr < BOARD_SIZE && nc >= 0 && nc < BOARD_SIZE && board[nr][nc] == null && !visited.contains(next)) { queue.add(next); visited.add(next); } } } return false; } // 手动结束游戏 private void endGameManual() { if (!moves.isEmpty()) { endGame(); } else { JOptionPane.showMessageDialog(this, "没有任何动作记录,游戏无法结束。"); } } // 保存当前棋谱到文件 private void saveMovesToFile() { try (BufferedWriter writer = new BufferedWriter(new FileWriter("game_records.txt", true))) { for (String[] move : moves) { writer.write(move[0] + ": " + move[1] + " -> " + move[2]); writer.newLine(); } writer.write("END_OF_GAME"); writer.newLine(); } catch (IOException e) { e.printStackTrace(); } } // 结束游戏 private void endGame() { saveMovesToFile(); // 保存当前棋谱到文件 canvas.removeMouseListener(canvas.getMouseListeners()[0]); canvas.repaint(); displayEndGameOptions(); } // 显示游戏结束选项 private void displayEndGameOptions() { String mode = (String) modeMenu.getSelectedItem(); String message; if ("训练模式".equals(mode)) { message = "游戏结束!\n玩家得分: " + playerScores.get("玩家1"); } else { int player1Score = playerScores.get("玩家1"); int player2Score = playerScores.get("玩家2"); int aiScore = playerScores.get("AI"); if ("人人对弈".equals(mode)) { if (player1Score > player2Score) { message = "游戏结束!\n玩家1获胜,得分为: " + player1Score + "\n玩家2得分: " + player2Score; } else if (player2Score > player1Score) { message = "游戏结束!\n玩家2获胜,得分为: " + player2Score + "\n玩家1得分: " + player1Score; } else { message = "游戏结束!\n平局!\n玩家1得分: " + player1Score + "\n玩家2得分: " + player2Score; } } else if ("人机对弈".equals(mode)) { if (player1Score > aiScore) { message = "游戏结束!\n玩家1获胜,得分为: " + player1Score + "\nAI得分: " + aiScore; } else if (aiScore > player1Score) { message = "游戏结束!\nAI获胜,得分为: " + aiScore + "\n玩家1得分: " + player1Score; } else { message = "游戏结束!\n平局!\n玩家1得分: " + player1Score + "\nAI得分: " + aiScore; } } else { message = "游戏结束!"; } } JOptionPane.showMessageDialog(this, message); int option = JOptionPane.showOptionDialog(this, "选择操作", "游戏结束", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, new String[]{"重新开始", "结束游戏"}, "重新开始"); if (option == JOptionPane.YES_OPTION) { resetGame(); } else { System.exit(0); } } // 加载所有棋谱记录 private void loadAllGames() { allGames = new ArrayList<>(); List<String> currentGame = new ArrayList<>(); File file = new File("game_records.txt"); if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); return; } } try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String line; while ((line = reader.readLine()) != null) { if (line.equals("END_OF_GAME")) { allGames.add(new ArrayList<>(currentGame)); currentGame.clear(); } else { currentGame.add(line); } } } catch (IOException e) { e.printStackTrace(); } } // 显示棋谱 private void showMoves() { if (allGames.isEmpty()) { JOptionPane.showMessageDialog(this, "没有棋谱记录", "棋谱", JOptionPane.INFORMATION_MESSAGE); return; } // 设置 currentGameIndex 为最新一局记录的索引 currentGameIndex = allGames.size() - 1; JFrame movesFrame = new JFrame("棋谱"); JTextArea movesTextArea = new JTextArea(20, 40); movesTextArea.setEditable(false); JScrollPane scrollPane = new JScrollPane(movesTextArea); JButton prevButton = new JButton("上一局"); prevButton.addActionListener(e -> showPrevGame(movesTextArea)); JButton nextButton = new JButton("下一局"); nextButton.addActionListener(e -> showNextGame(movesTextArea)); JPanel buttonPanel = new JPanel(); buttonPanel.add(prevButton); buttonPanel.add(nextButton); movesFrame.setLayout(new BorderLayout()); movesFrame.add(scrollPane, BorderLayout.CENTER); movesFrame.add(buttonPanel, BorderLayout.SOUTH); movesFrame.pack(); movesFrame.setLocationRelativeTo(this); updateMovesTextArea(movesTextArea); movesFrame.setVisible(true); } private void showPrevGame(JTextArea movesTextArea) { if (currentGameIndex > 0) { currentGameIndex--; updateMovesTextArea(movesTextArea); } else { JOptionPane.showMessageDialog(this, "没有上一局记录", "棋谱", JOptionPane.INFORMATION_MESSAGE); } } private void showNextGame(JTextArea movesTextArea) { if (currentGameIndex < allGames.size() - 1) { currentGameIndex++; updateMovesTextArea(movesTextArea); } else { JOptionPane.showMessageDialog(this, "没有下一局记录", "棋谱", JOptionPane.INFORMATION_MESSAGE); } } private void updateMovesTextArea(JTextArea movesTextArea) { movesTextArea.setText(""); for (String move : allGames.get(currentGameIndex)) { movesTextArea.append(move + "\n"); } } // 更新得分显示 private void updateScores() { String mode = (String) modeMenu.getSelectedItem(); if ("训练模式".equals(mode)) { scoreLabel1.setText("玩家得分: " + playerScores.get("玩家1")); scoreLabel2.setVisible(false); } else if ("人机对弈".equals(mode)) { scoreLabel1.setText("玩家得分: " + playerScores.get("玩家1")); scoreLabel2.setText(" AI得分: " + playerScores.get("AI")); scoreLabel2.setVisible(true); } else { scoreLabel1.setText("玩家1得分: " + playerScores.get("玩家1")); scoreLabel2.setText(" 玩家2得分: " + playerScores.get("玩家2")); scoreLabel2.setVisible(true); } } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { BallGame game = new BallGame(); game.setVisible(true); }); } }标签:Java,游戏,int,private,玩家,board,new,消球,SIZE From: https://blog.csdn.net/m0_74725323/article/details/140299212