iT邦幫忙

0

Day30 | 30 天學習總結:成果展示與未來學習規劃

  • 分享至 

  • xImage
  •  

經歷了一連串的實作與調整,我們的 Tic-Tac-Toe(井字棋) Swing 遊戲專案終於正式劃下完美的句點!

從最開始建立一個空無一物的黑色視窗,到加入 3x3 網格面板、實作兩位玩家的回合交替、四種方向的連線勝負判斷,最後再加上平局機制與勝利/平手高亮特效,我們成功完成了一款功能完整且質感極佳的桌面小遊戲。

在這篇完結篇中,我們將重頭梳理整個專案的核心架構,並提供最終完整的原始碼,方便大家對照與學習!

  • 專案核心架構與知識點回顧
    視窗介面與版面配置(Swing UI):
  1. 組合模式(Composition):不直接繼承 JFrame,而是採用元件組合的方式來建構介面。
  2. BorderLayout 邊界佈局:將視窗分為頂部的 textPanel(NORTH)與中央的 boardPanel(CENTER)。
  3. GridLayout(3, 3) 網格佈局:讓棋盤底座自動切分為 9 個均等的方格,擺脫手動計算座標的麻煩。

資料結構與狀態控管 :

  1. JButton[3][3] 二維陣列:利用矩陣結構同步管理 UI 元件與棋盤資料,為後續的連線檢查打下基礎。
  2. gameOver 與 turns 控制:
    (1.)gameOver 布林旗標能及時鎖定棋盤,避免玩家在分出勝負後誤觸按鈕。
    (2.)turns 步數統計能精確掌握棋盤狀態,當步數達到 9 步且無人勝出時自動判定為平手(Tie)。

全方位勝負與平局判定 :
在 checkWinner() 方法中,我們實作了四種連線檢查:

  1. Horizontal(水平):遍歷各列(Row),比對三格文字是否相同。
  2. Vertical(垂直):遍歷各行(Column),比對三格文字是否相同。
  3. Diagonally(主對角線):檢查 (0,0)、(1,1)、(2,2) 座標連線。
  4. Anti-diagonally(反對角線):檢查 (0,2)、(1,1)、(2,0) 座標連線。
  5. Tie(平局):當 turns == 9 時觸發,全體棋子轉為橘色。
  • 最終完整程式碼(TicTacToe.java)
    以下是整理好的完整程式碼,包含完整的註解說明:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TicTacToe {
    // 視窗尺寸設定
    int boardWidth = 600;
    int boardHeight = 650; // 50px 用於頂部的文字面板

    // Swing UI 元件
    JFrame frame = new JFrame("Tic-Tac-Toe");
    JLabel textLabel = new JLabel();
    JPanel textPanel = new JPanel();
    JPanel boardPanel = new JPanel();

    // 遊戲狀態變數
    JButton[][] board = new JButton[3][3];
    String playerX = "X";
    String playerO = "O";
    String currentPlayer = playerX;

    boolean gameOver = false;
    int turns = 0;

    TicTacToe(){
        // 1. 初始化主視窗
        frame.setVisible(true);
        frame.setSize(boardWidth, boardHeight);
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());

        // 2. 初始化頂部文字標籤與面板
        textLabel.setBackground(Color.darkGray);
        textLabel.setForeground(Color.white);
        textLabel.setFont(new Font("Arial", Font.BOLD, 50));
        textLabel.setHorizontalAlignment(JLabel.CENTER);
        textLabel.setText("Tic-Tac-Toe");
        textLabel.setOpaque(true);

        textPanel.setLayout(new BorderLayout());
        textPanel.add(textLabel);
        frame.add(textPanel, BorderLayout.NORTH);

        // 3. 初始化 3x3 網格棋盤面板
        boardPanel.setLayout(new GridLayout(3, 3));
        boardPanel.setBackground(Color.darkGray);
        frame.add(boardPanel);

        // 4. 建立 9 個按鈕並綁定事件監聽器
        for (int r = 0; r < 3; r++){
            for (int c = 0; c < 3; c++){
                JButton tile = new JButton();
                board[r][c] = tile;
                boardPanel.add(tile);

                tile.setBackground(Color.darkGray);
                tile.setForeground(Color.white);
                tile.setFont(new Font("Arial", Font.BOLD, 120));
                tile.setFocusable(false);

                // 按鈕點擊監聽
                tile.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e){
                        if (gameOver) return; // 遊戲結束阻斷點擊
                        JButton tile = (JButton)e.getSource();
                        
                        // 確保該格子尚未下過
                        if (tile.getText() == ""){
                            tile.setText(currentPlayer);
                            turns++; // 增加總步數
                            
                            checkWinner(); // 檢查勝負與平手
                            
                            // 未結束時切換回合
                            if (!gameOver){
                                currentPlayer = currentPlayer == playerX ? playerO : playerX;
                                textLabel.setText(currentPlayer + "'s turn.");
                            }
                        }
                    }
                });
            }
        }
    }

    // 勝負與平手檢查方法
    void checkWinner() {
        // 1. 水平方向檢查 (Horizontal)
        for (int r = 0; r < 3; r++) {
            if (board[r][0].getText() == "") continue;

            if (board[r][0].getText() == board[r][1].getText() && 
                board[r][1].getText() == board[r][2].getText()){
                    for (int i = 0; i < 3; i++) {
                        setWinner(board[r][i]);
                    }
                    gameOver = true;
                    return;
                }
        }

        // 2. 垂直方向檢查 (Vertical)
        for (int c = 0; c < 3; c++){
            if (board[0][c].getText() == "") continue;

            if (board[0][c].getText() == board[1][c].getText() && 
                board[0][c].getText() == board[2][c].getText()){
                for (int i = 0; i < 3; i++){
                    setWinner(board[i][c]);
                }
                gameOver = true;
                return;
                }
        }

        // 3. 主對角線檢查 (Diagonally)
        if (board[0][0].getText() == board[1][1].getText() && 
            board[1][1].getText() == board[2][2].getText() && 
            board[0][0].getText() != "") {
            for (int i = 0; i < 3; i++){
                setWinner(board[i][i]);
            }
            gameOver = true;
            return;
        }

        // 4. 反對角線檢查 (Anti-diagonally)
        if (board[0][2].getText() == board[1][1].getText() && 
            board[1][1].getText() == board[2][0].getText() &&
            board[0][2].getText() != "") {
                setWinner(board[0][2]);
                setWinner(board[1][1]);
                setWinner(board[2][0]);
                gameOver = true;
                return;
        }

        // 5. 平局檢查 (Tie)
        if (turns == 9) {
            for (int r = 0; r < 3; r++) {
                for (int c = 0; c < 3; c++) {
                    setTie(board[r][c]);
                }
            }
            gameOver = true;
        }
    }

    // 獲勝視覺樣式
    void setWinner(JButton tile) {
        tile.setForeground(Color.green);
        tile.setBackground(Color.gray);
        textLabel.setText(currentPlayer + " is the winner!");
    }

    // 平手視覺樣式
    void setTie(JButton tile) {
        tile.setForeground(Color.orange);
        tile.setBackground(Color.gray);
        textLabel.setText("Tie!");
    }
}
  • 開發心得與未來擴充想法
    透過這次的實作練習,我們深入學習了 Java 桌面應用程式(Swing)的佈局管理、事件處理機制以及邏輯判斷的思考方式。
    未來如果想讓這款遊戲更有趣味性,還可以挑戰以下擴充功能:
  1. 重新開始(Restart)按鈕:在頂部或底部加入按鈕,點擊後清空棋盤並重置變數。
  2. 計分板(Scoreboard):記錄 X 與 O 獲勝的總次數。
  3. AI 單人對戰模式:實作簡單的隨機電腦下棋,甚至是運用 Minimax 演算法 打造無法被擊敗的 AI 對手!

*提醒邦友,使用第三方服務/API 時,請務必評估資安風險與隱私保護
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言