最近突然想開設一個專欄了,專門為計算機專業的同行分享一些入門級的課程項目設計,旨在讓同學更好地了解CS項目的設計流程,同時給出代碼來介紹coding過程。
今天要分享的是第一個CS課程項目:交互友好的井字棋游戲。
1. 研究目的
井字棋(Tic-Tac-Toe)作為一款經典的雙人策略游戲,具有規則簡單、易于理解的特點,適合作為編程教育和人機交互研究的基礎案例。目前各類知識分享平臺上(如CSDN、知乎)的井字棋游戲雖然數量眾多,但部分存在功能單一、界面簡陋、交互體驗不佳等問題。本項目旨在開發一款具有現代 UI 設計和豐富交互功能的井字棋游戲,滿足用戶對游戲趣味性和易用性的需求。
本項目設計的井字棋游戲包含以下功能:
- 實現井字棋的基本游戲規則,包括輪流落子、勝負判定和平局檢測。
- 提供友好的用戶界面,支持玩家自定義名稱,增強游戲的個性化體驗。
- 開發附加功能,如悔棋、保存 / 加載游戲進度等,提升游戲的實用性。
- 引入音效和動畫效果,增強游戲的視覺和聽覺反饋,提高用戶體驗。
2. 技術方案
本項目采用 Python 語言結合 tkinter 庫實現,具體技術方案如下:
- 開發語言:Python 3.8
- GUI 庫:tkinter(Python 內置庫,無需額外安裝)
- 數據存儲:JSON 格式文件用于保存和加載游戲進度
- 多線程處理:使用 threading 模塊處理音效播放,避免阻塞 UI 線程
- 動畫效果:通過 tkinter 的 update () 方法和延時函數實現簡單動畫
系統架構采用面向對象設計,將游戲邏輯和界面交互分離,提高代碼的可維護性和可擴展性。主要類包括:
TicTacToe
:游戲主類,負責管理游戲狀態、處理用戶輸入和更新界面- 界面組件:包括棋盤按鈕、狀態標簽、控制按鈕等,通過 tkinter 實現
3. 實現流程
明確游戲的基本功能和交互邏輯,設計數據結構和類的關系。確定需要實現的核心功能包括:棋盤顯示、玩家輪流落子、勝負判定、悔棋、保存 / 加載游戲等。
首先實現游戲的核心邏輯,包括:
- 初始化棋盤和游戲狀態
- 處理玩家點擊事件,更新棋盤狀態
- 判斷勝負和平局條件
- 實現玩家輪流機制
其中,處理玩家點擊事件,更新棋盤狀態的代碼如下所示:
def make_move(self, row, col):"""處理玩家移動"""if self.board[row][col] == ' ' and self.game_active:# 記錄當前移動到歷史self.move_history.append((row, col, self.current_player))self.undo_button.config(state=tk.NORMAL) # 啟用悔棋按鈕# 播放放置音效self.play_sound('place')# 添加放置動畫self.animate_cell(row, col)# 更新棋盤數據self.board[row][col] = self.current_player# 更新按鈕顯示self.buttons[row][col].config(text=self.current_player)# 記錄上一步self.last_move = (row, col)self.last_move_label.config(text=f"上一步: {self.player_names[self.current_player]} 在位置 {row + 1},{col + 1}")# 檢查游戲狀態if self.check_winner(self.current_player):self.status_label.config(text=f"{self.player_names[self.current_player]} 獲勝!")self.game_active = Falseself.undo_button.config(state=tk.DISABLED) # 禁用悔棋按鈕# 播放勝利音效和動畫self.play_sound('win')self.animate_winning_cells()messagebox.showinfo("游戲結束", f"{self.player_names[self.current_player]} 獲勝!")elif self.is_board_full():self.status_label.config(text="游戲平局!")self.game_active = Falseself.undo_button.config(state=tk.DISABLED) # 禁用悔棋按鈕# 播放平局音效self.play_sound('draw')messagebox.showinfo("游戲結束", "游戲平局!")else:# 切換玩家self.current_player = 'O' if self.current_player == 'X' else 'X'self.status_label.config(text=f"當前玩家: {self.player_names[self.current_player]}")
當前玩家可以看到上一位玩家的下子坐標位置,可視化界面如下所示:
判斷勝負和平局條件的代碼如下所示:
def check_winner(self, player):"""檢查玩家是否獲勝,并記錄獲勝的格子"""# 檢查行for row in range(3):if all([self.board[row][col] == player for col in range(3)]):self.winning_cells = [(row, col) for col in range(3)]return True# 檢查列for col in range(3):if all([self.board[row][col] == player for row in range(3)]):self.winning_cells = [(row, col) for row in range(3)]return True# 檢查對角線if all([self.board[i][i] == player for i in range(3)]):self.winning_cells = [(i, i) for i in range(3)]return Trueif all([self.board[i][2 - i] == player for i in range(3)]):self.winning_cells = [(i, 2 - i) for i in range(3)]return Truereturn False
此外,該項目使用 tkinter 創建用戶界面,包括:
- 設計棋盤布局和樣式
- 添加狀態顯示區域,顯示當前玩家和游戲狀態
- 實現控制按鈕(悔棋、重新開始等)
- 支持玩家自定義名稱
其中,支持玩家自定義名稱的代碼如下所示:
def set_player_names(self):"""設置玩家名稱"""# 詢問玩家X的名稱name = simpledialog.askstring("玩家名稱", "請輸入玩家X的名稱:", parent=self.root)if name:self.player_names['X'] = name# 詢問玩家O的名稱name = simpledialog.askstring("玩家名稱", "請輸入玩家O的名稱:", parent=self.root)if name:self.player_names['O'] = name# 更新玩家標簽self.player_x_label.config(text=f"{self.player_names['X']} (X)")self.player_o_label.config(text=f"{self.player_names['O']} (O)")self.status_label.config(text=f"當前玩家: {self.player_names[self.current_player]}")# 啟用保存按鈕self.save_button.config(state=tk.NORMAL)
支持玩家自定義名稱的可視化界面如下所示:
?我們逐步添加附加功能:
- 悔棋功能:記錄歷史操作,支持撤銷上一步
- 保存 / 加載功能:使用 JSON 格式保存游戲狀態到文件
- 音效系統:使用 playsound 庫播放操作音效
- 動畫效果:為棋子放置和獲勝狀態添加視覺動畫
其中,悔棋功能的代碼如下所示:
def undo_move(self):"""悔棋功能"""if not self.move_history:return # 沒有歷史記錄# 播放悔棋音效self.play_sound('undo')# 恢復上一步row, col, player = self.move_history.pop()self.board[row][col] = ' 'self.buttons[row][col].config(text='', bg='SystemButtonFace') # 恢復默認背景# 清除獲勝高亮if self.winning_cells:for r, c in self.winning_cells:self.buttons[r][c].config(bg='SystemButtonFace')self.winning_cells = []# 更新上一步信息if self.move_history:last_row, last_col, last_player = self.move_history[-1]self.last_move = (last_row, last_col)self.last_move_label.config(text=f"上一步: {self.player_names[last_player]} 在位置 {last_row + 1},{last_col + 1}")else:self.last_move = Noneself.last_move_label.config(text="上一步: 無")# 切換回上一個玩家self.current_player = playerself.status_label.config(text=f"當前玩家: {self.player_names[self.current_player]}")# 重新激活游戲(如果之前結束了)self.game_active = True# 如果沒有歷史記錄了,禁用悔棋按鈕if not self.move_history:self.undo_button.config(state=tk.DISABLED)
悔棋功能的可視化界面如下所示:
因此,該游戲的主要流程如下所示:
- 初始化 3x3 空棋盤
- 玩家 X 先開始游戲
- 輪流輸入位置(行和列,范圍 1-3)
- 程序會驗證輸入有效性并更新棋盤
- 每次移動后檢查是否有玩家獲勝或平局
- 游戲結束時顯示結果
4. 項目展示
前面說太多了,最后還是上傳個該項目的簡要演示視頻,供大家了解。
IMG_4950