井字棋游戏 (Tic-Tac-Toe)
场景描述
井字棋(Tic-Tac-Toe)是一款经典的两人对弈游戏,在3×3的棋盘上进行。玩家轮流放置自己的标记(X或O),率先在横、竖或斜方向连成一线者获胜。如果棋盘填满仍未分出胜负,则为平局。
本项目使用Python面向对象编程实现了一个完整的井字棋游戏,包含以下特性:
- 清晰的代码结构,分离了游戏逻辑和界面展示
- 输入验证,确保玩家只能选择空位
- 自动判断胜负和平局
- 友好的用户界面,显示当前棋盘状态
流程图
┌─────────────────────────────────────────────────────────────┐
│ 游戏开始 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌────────────────────────┐
│ 显示欢迎信息 │
│ "Welcome to tic-tac-toe"│
└────────────┬───────────┘
│
▼
┌────────────────────────┐
│ 初始化棋盘 (TTTBoard) │
│ current_player = X │
│ next_player = O │
└────────────┬───────────┘
│
▼
┌────────────────────────┐
│ 显示棋盘 │
│ (调用 __str__) │
└────────────┬───────────┘
│
┌────────────▼───────────┐
│ │
│ ┌──────────────────┐ │
│ │ 提示当前玩家输入 │ │◄───┐
│ │ "X/O请选择移动" │ │ │
│ └────────┬─────────┘ │ │
│ │ │ │
│ ┌────────▼─────────┐ │ │
│ │ 读取用户输入 (1-9)│ │ │
│ └────────┬─────────┘ │ │
│ │ │ │
│ ┌────────▼─────────┐ │ │
│ │ is_valid_space? │─────┼──── 无效
│ └────────┬─────────┘ │ │
│ │ │ │
│ │ 有效 │ │
│ ┌────────▼─────────┐ │ │
│ │ update_board() │ │ │
│ │ 放置标记 (X或O) │ │ │
│ └────────┬─────────┘ │ │
└────────────┼───────────┘ │
│ │
▼ │
┌────────────────────────┐ │
│ is_winner(current)? │ │
└────────────┬───────────┘ │
│ │
┌────────────┼────────────┐ │
│ │ │ │
是│ │否 │ │
▼ │ ▼ │
┌──────────────┐ ┌─────▼─────┐ ┌──────────────┐
│ 显示获胜信息 │ │is_board │ │ │
│ "X 获胜!" │ │ _full()? │ │ │
└──────┬───────┘ └─────┬─────┘ │ │
│ │ │ │
│ ┌───────┼───────┐│ │
│ 是│ │否 ││ │
│ ▼ ▼ ││ │
│ ┌──────────┐ ┌────────▼───────────┐ │
│ │显示平局 │ │ 交换玩家 │ │
│ │"平局" │ │ current, next = │ │
│ └────┬────┘ │ next, current │ │
│ │ └────────┬───────────┘ │
│ │ │ │
│ │ └──────────────┘
│ │ │
└────────┼──────────────────────┘
│
▼
┌──────────┐
│ 显示"游戏结束"│
└─────┬────┘
│
▼
┌──────────┐
│ 游戏结束 │
└──────────┘
完整的游戏过程示例
Welcome to tic-tac-toe
| | 1 2 3
-+-+-
| | 4 5 6
-+-+-
| | 7 8 9
X请选择移动: (1-9)
> 1
X| | 1 2 3
-+-+-
| | 4 5 6
-+-+-
| | 7 8 9
O请选择移动: (1-9)
> 2
X|O| 1 2 3
-+-+-
| | 4 5 6
-+-+-
| | 7 8 9
X请选择移动: (1-9)
> 3
X|O|X 1 2 3
-+-+-
| | 4 5 6
-+-+-
| | 7 8 9
O请选择移动: (1-9)
> 6
X|O|X 1 2 3
-+-+-
| |O 4 5 6
-+-+-
| | 7 8 9
X请选择移动: (1-9)
> 8
X|O|X 1 2 3
-+-+-
| |O 4 5 6
-+-+-
|X| 7 8 9
O请选择移动: (1-9)
> 7
X|O|X 1 2 3
-+-+-
| |O 4 5 6
-+-+-
O|X| 7 8 9
X请选择移动: (1-9)
> 9
X|O|X 1 2 3
-+-+-
| |O 4 5 6
-+-+-
O|X|X 7 8 9
O请选择移动: (1-9)
> 4
X|O|X 1 2 3
-+-+-
O| |O 4 5 6
-+-+-
O|X|X 7 8 9
X请选择移动: (1-9)
> 5
X|O|X 1 2 3
-+-+-
O|X|O 4 5 6
-+-+-
O|X|X 7 8 9
X 获胜!
游戏结束
在这个例子中,玩家X通过占据第5个位置(中心位置),完成了从左上到右下的对角线连线,从而赢得了比赛。
核心代码实现
1. 游戏棋盘类 (tictactoe_oop.py)
# 井字棋棋盘字典key
ALL_SPACES = [str(i) for i in range(1, 10)]
# 字符串常量
X, O, BLANK = 'X', 'O', ' '
class TTTBoard:
"""井字棋游戏"""
def __init__(self):
"""初始化棋盘"""
self._spaces = {space: BLANK for space in ALL_SPACES}
def is_valid_space(self, space) -> bool:
"""判断是否为有效的移动
Args:
space: 要检查的位置,应该是 '1' 到 '9' 的字符串
Returns:
bool: 如果该位置为空则返回 True,否则返回 False
"""
return space in ALL_SPACES and self._spaces[space] == BLANK
def is_winner(self, player):
"""判断玩家是否获胜"""
s, p = self._spaces, player # 简写
# 检查3行,3列,2对角线上的标记
return (s['1'] == s['2'] == s['3'] == p or
s['4'] == s['5'] == s['6'] == p or
s['7'] == s['8'] == s['9'] == p or
s['1'] == s['4'] == s['7'] == p or
s['2'] == s['5'] == s['8'] == p or
s['3'] == s['6'] == s['9'] == p or
s['1'] == s['5'] == s['9'] == p or
s['3'] == s['5'] == s['7'] == p)
def is_board_full(self) -> bool:
"""判断棋盘是否已满
Returns:
bool: 如果棋盘已满返回 True,否则返回 False
"""
return not any(space == BLANK for space in self._spaces.values())
def update_board(self, space, player):
"""更新棋盘
Args:
space: 要更新的位置,应该是 '1' 到 '9' 的字符串
player: 要更新位置的玩家,应该是 'X' 或 'O'
"""
self._spaces[space] = player
def __str__(self):
"""返回棋盘字符串"""
return f"""
{self._spaces['1']}|{self._spaces['2']}|{self._spaces['3']} 1 2 3
-+-+-
{self._spaces['4']}|{self._spaces['5']}|{self._spaces['6']} 4 5 6
-+-+-
{self._spaces['7']}|{self._spaces['8']}|{self._spaces['9']} 7 8 9
"""
__repr__ = __str__
2. 游戏主程序 (app.py)
from tictactoe_oop import (TTTBoard, X, O)
def run():
print("Welcome to tic-tac-toe")
board = TTTBoard()
current_player, next_player = X, O # X先行,O后行
while True:
print(board)
move = None
while not board.is_valid_space(move):
print(f"{current_player}请选择移动: (1-9)")
move = input("> ")
board.update_board(move, current_player) # 执行移动
# 检查游戏是否结束
if board.is_winner(current_player): # 首先检查一方是否获胜
print(board)
print(f"{current_player} 获胜!")
break
elif board.is_board_full(): # 检查棋盘是否已满
print(board)
print("平局")
break
current_player, next_player = next_player, current_player # 交换玩家
print("游戏结束")
if __name__ == '__main__':
run()