首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 棋類遊戲實戰:單機版五子棋完整實現指南

     編輯:Java 相關知識

專案目標

  • 掌握 Swing GUI 佈局與自定義繪製
  • 實現滑鼠事件監聽(點擊落子、移動預覽)
  • 完成五子棋核心邏輯:落子驗證、勝負判定、悔棋、重新開始

核心類別設計

ChessBoard 棋盤類

繼承 JPanel,實作 MouseListener、內部類實作 MouseMotionListener。

關鍵常數:

public static final int MARGIN = 30;       // 邊距
public static final int GRID_SPAN = 35;    // 格子間距
public static final int ROWS = 15;         // 行數
public static final int COLS = 15;         // 列數

棋子儲存:

Point[] chessList = new Point[(ROWS+1)*(COLS+1)];
int chessCount = 0;           // 當前棋子數
boolean isBlack = true;       // 黑棋先行
boolean gameOver = false;     // 遊戲結束標記
int xIndex, yIndex;           // 最後落子座標索引

棋子繪製與視覺效果

// paintComponent 核心繪製邏輯
for (int i = 0; i < chessCount; i++) {
    int xPos = chessList[i].getX() * GRID_SPAN + MARGIN;
    int yPos = chessList[i].getY() * GRID_SPAN + MARGIN;
    
    // 使用漸變繪製立體棋子效果
    Color colortemp = chessList[i].getColor();
    if (colortemp == Color.BLACK) {
        RadialGradientPaint paint = new RadialGradientPaint(
            xPos - 15 + 25, yPos - 15 + 10, 20,
            new float[]{0f, 1f}, new Color[]{Color.WHITE, Color.BLACK});
        ((Graphics2D) g).setPaint(paint);
    } else { // 白棋
        RadialGradientPaint paint = new RadialGradientPaint(
            xPos - 15 + 25, yPos - 15 + 10, 70,
            new float[]{0f, 1f}, new Color[]{Color.WHITE, Color.BLACK});
        ((Graphics2D) g).setPaint(paint);
    }
    ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                                      RenderingHints.VALUE_ANTIALIAS_ON);
    g.fill(new Ellipse2D.Float(xPos - 15, yPos - 15, 34, 35));
    
    // 標記最後一步(紅框)
    if (i == chessCount - 1) {
        g.setColor(Color.RED);
        g.drawRect(xPos - 15, yPos - 15, 34, 35);
    }
}

滑鼠移動預覽(游標變化)

addMouseMotionListener(new MouseMotionListener() {
    public void mouseMoved(MouseEvent e) {
        int x1 = (e.getX() - MARGIN + GRID_SPAN/2) / GRID_SPAN;
        int y1 = (e.getY() - MARGIN + GRID_SPAN/2) / GRID_SPAN;
        // 超出邊界、遊戲結束、已有棋子 → 預設游標
        if (x1 < 0 || x1 > ROWS || y1 < 0 || y1 > COLS || gameOver || findChess(x1, y1)) {
            setCursor(Cursor.getDefaultCursor());
        } else {
            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        }
    }
    public void mouseDragged(MouseEvent e) {}
});

落子處理與勝負判定

public void mousePressed(MouseEvent e) {
    if (gameOver) return;
    
    xIndex = (e.getX() - MARGIN + GRID_SPAN/2) / GRID_SPAN;
    yIndex = (e.getY() - MARGIN + GRID_SPAN/2) / GRID_SPAN;
    
    // 邊界檢查、重複落子檢查
    if (xIndex < 0 || xIndex > ROWS || yIndex < 0 || yIndex > COLS) return;
    if (findChess(xIndex, yIndex)) return;
    
    // 新增棋子
    chessList[chessCount++] = new Point(xIndex, yIndex, isBlack ? Color.BLACK : Color.WHITE);
    repaint();
    
    // 勝負判定
    if (isWin()) {
        JOptionPane.showMessageDialog(this, "恭喜," + (isBlack ? "黑棋" : "白棋") + "獲勝!");
        gameOver = true;
    }
    isBlack = !isBlack; // 切換回合
}

四向連線檢測算法

private boolean isWin() {
    int continueCount = 1;
    Color c = isBlack ? Color.BLACK : Color.WHITE;
    
    // 橫向(左右)
    for (int x = xIndex - 1; x >= 0; x--) 
        if (getChess(x, yIndex, c) != null) continueCount++; else break;
    for (int x = xIndex + 1; x <= COLS; x++) 
        if (getChess(x, yIndex, c) != null) continueCount++; else break;
    if (continueCount >= 5) return true;
    
    // 縱向(上下)
    continueCount = 1;
    for (int y = yIndex - 1; y >= 0; y--) 
        if (getChess(xIndex, y, c) != null) continueCount++; else break;
    for (int y = yIndex + 1; y <= ROWS; y++) 
        if (getChess(xIndex, y, c) != null) continueCount++; else break;
    if (continueCount >= 5) return true;
    
    // 斜向:左上-右下 (\) 
    continueCount = 1;
    for (int x = xIndex - 1, y = yIndex - 1; x >= 0 && y >= 0; x--, y--) 
        if (getChess(x, y, c) != null) continueCount++; else break;
    for (int x = xIndex + 1, y = yIndex + 1; x <= COLS && y <= ROWS; x++, y++) 
        if (getChess(x, y, c) != null) continueCount++; else break;
    if (continueCount >= 5) return true;
    
    // 斜向:右上-左下 (/) 
    continueCount = 1;
    for (int x = xIndex + 1, y = yIndex - 1; x <= COLS && y >= 0; x++, y--) 
        if (getChess(x, y, c) != null) continueCount++; else break;
    for (int x = xIndex - 1, y = yIndex + 1; x >= 0 && y <= ROWS; x--, y++) 
        if (getChess(x, y, c) != null) continueCount++; else break;
    if (continueCount >= 5) return true;
    
    return false;
}

輔助功能:悔棋與重新開始

// 悔棋
public void goback() {
    if (chessCount == 0) return;
    chessList[chessCount - 1] = null;
    chessCount--;
    if (chessCount > 0) {
        xIndex = chessList[chessCount - 1].getX();
        yIndex = chessList[chessCount - 1].getY();
    }
    isBlack = !isBlack; // 切回上一手回合
    repaint();
}

// 重新開始
public void restartGame() {
    Arrays.fill(chessList, null);
    isBlack = true;
    gameOver = false;
    chessCount = 0;
    repaint();
}

Point 棋子資料類

public class Point {
    private int x, y;
    private Color color;
    public static final int DIAMETER = 30;
    
    public Point(int x, int y, Color color) {
        this.x = x; this.y = y; this.color = color;
    }
    // getter 方法...
}

主視窗與選單/工具列整合

public class StartChessJFrame extends JFrame {
    private ChessBoard chessBoard;
    private JMenuBar menuBar;
    private JMenu sysMenu;
    private JMenuItem startItem, exitItem, backItem;
    private JButton startBtn, exitBtn, backBtn;
    
    public StartChessJFrame() {
        // 選單列
        menuBar = new JMenuBar();
        sysMenu = new JMenu("系統");
        startItem = new JMenuItem("重新開始");
        exitItem = new JMenuItem("退出");
        backItem = new JMenuItem("悔棋");
        sysMenu.add(startItem); sysMenu.add(backItem); sysMenu.add(exitItem);
        menuBar.add(sysMenu);
        setJMenuBar(menuBar);
        
        // 底部工具列
        toolbar = new JPanel(new FlowLayout(FlowLayout.LEFT));
        startBtn = new JButton("重新開始");
        exitBtn = new JButton("退出");
        backBtn = new JButton("悔棋");
        toolbar.add(startBtn); toolbar.add(backBtn); toolbar.add(exitBtn);
        add(toolbar, BorderLayout.SOUTH);
        
        // 統一事件監聽
        ActionListener lis = e -> {
            Object src = e.getSource();
            if (src == startItem || src == startBtn) chessBoard.restartGame();
            else if (src == exitItem || src == exitBtn) System.exit(0);
            else if (src == backItem || src == backBtn) chessBoard.goback();
        };
        startItem.addActionListener(lis); startBtn.addActionListener(lis);
        backItem.addActionListener(lis); backBtn.addActionListener(lis);
        exitItem.addActionListener(lis); exitBtn.addActionListener(lis);
        
        add(chessBoard);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack(); setVisible(true);
    }
}

技術亮點總結

  1. 座標映射:螢幕像素座標 ↔ 棋盤邏輯索引雙向轉換
  2. 高品質繪製:Graphics2D + RadialGradientPaint + 反鋸齒實現立體棋子
  3. 即時視覺回饋:滑鼠移動時游標狀態提示可落子位置
  4. 高效勝負判定:以最後落子為中心四向擴展檢測,O(1) 時間複雜度
  5. 關注點分離:棋盤邏輯、棋子資料、主視窗控制分離
  6. 雙重操作入口:選單列與工具列並行,提升易用性

擴展方向

  • 加入禁手規則(三三、四四、長連)
  • 實作 AI 對戰(啟發式搜尋、蒙特卡洛樹搜尋)
  • 支援棋譜儲存/回放
  • 網路雙人對戰
  • 棋型提示與輔助分析
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved