井字棋(三子棋) 双人对战

Admin
发布于 2026-05-22 / 3 阅读
0
0
"""
井字棋(三子棋) 双人对战
Tic-Tac-Toe: Two Player Console Version
"""

class TicTacToe:
    def __init__(self):
        self.board = [' ' for _ in range(9)]
        self.current_player = 'X'
        self.winner = None
        self.game_over = False
        self.move_count = 0
        self.win_combinations = [
            [0, 1, 2], [3, 4, 5], [6, 7, 8],  # Rows
            [0, 3, 6], [1, 4, 7], [2, 5, 8],  # Columns
            [0, 4, 8], [2, 4, 6]              # Diagonals
        ]

    def make_move(self, position):
        """Place a mark on the board if valid."""
        if self.board[position] == ' ' and not self.game_over:
            self.board[position] = self.current_player
            self.move_count += 1
            if self.check_winner():
                self.game_over = True
            elif self.check_tie():
                self.game_over = True
                self.winner = 'Tie'
            else:
                # Switch player only if game isn't over
                self.current_player = 'O' if self.current_player == 'X' else 'X'
            return True
        return False

    def check_winner(self):
        """Check if there is a winner."""
        for combination in self.win_combinations:
            a, b, c = combination
            if self.board[a] == self.board[b] == self.board[c] != ' ':
                self.winner = self.board[a]
                return True
        return False

    def check_tie(self):
        """Check if the game is a tie."""
        return all(cell != ' ' for cell in self.board)

    def print_board(self):
        """Display the current board state."""
        print("\n")
        for i in range(3):
            row = self.board[i*3:(i+1)*3]
            print(f" {row[0]} | {row[1]} | {row[2]} ")
            if i < 2:
                print("---|---|---")
        print("\n")

def get_player_input(player):
    """Get valid input from the player."""
    while True:
        try:
            pos = int(input(f"Player {player}, enter position (1-9): ")) - 1
            if 0 <= pos <= 8:
                return pos
            else:
                print("Position must be between 1 and 9.")
        except ValueError:
            print("Invalid input. Please enter a number.")

def main():
    game = TicTacToe()
    print("Welcome to Tic-Tac-Toe!")
    print("Positions are numbered 1-9 as follows:")
    print(" 1 | 2 | 3 ")
    print("---|---|---")
    print(" 4 | 5 | 6 ")
    print("---|---|---")
    print(" 7 | 8 | 9 ")

    while not game.game_over:
        game.print_board()
        pos = get_player_input(game.current_player)
        
        if not game.make_move(pos):
            print("That position is already taken! Try again.")
            continue

    # Game Over
    game.print_board()
    if game.winner == 'Tie':
        print("It's a Tie!")
    else:
        print(f"Congratulations! Player {game.winner} wins!")

if __name__ == "__main__":
    main()

这段代码实现了一个经典的**井字棋(三子棋)**的双人对战控制台游戏。下面我将为你详细解析这段代码的实现原理、用途以及注意事项。

### 一、 代码含义与实现原理

这段代码采用了**面向对象编程(OOP)**的思想,将游戏的核心逻辑封装在 TicTacToe 类中,并通过外部函数处理用户输入和主流程控制。

#### 1. 核心数据结构

* 一维列表表示二维棋盘:代码使用长度为9的一维列表 self.board 来表示3x3的棋盘。索引映射关系如下:

```text

0 | 1 | 2

---------

3 | 4 | 5

---------

6 | 7 | 8

```

* 获胜组合预定义self.win_combinations 列表提前存储了所有可能获胜的8种坐标组合(3行 + 3列 + 2对角线),这是一种典型的“空间换时间”策略,避免了复杂的坐标计算。

#### 2. 核心方法解析

* *__init__**:初始化空棋盘(用空格 ' ' 填充)、设置先手玩家为 'X'、重置胜者与游戏状态、初始化落子计数器和获胜组合。

* *make_move(position)**:

* 合法性检查:判断该位置是否为空且游戏是否未结束。

* 状态更新:落子并增加落子计数。

* 胜负/平局判定:落子后立即检查是否有人获胜或平局。如果游戏结束,更新状态;如果未结束,切换当前玩家(X变O,O变X)。

* *check_winner()**:遍历预定义的8种获胜组合,判断同组的三个位置是否被同一个玩家(且非空格)占据。

* *check_tie()**:使用 all() 函数判断棋盘上是否还有空格。如果没有空格且之前没检测出胜者,则为平局。

* *print_board()**:通过列表切片 self.board[i*3:(i+1)*3] 将一维列表按行截断,格式化打印出当前棋盘。

#### 3. 交互与主流程

* *get_player_input(player)**:使用 try-except 捕获非数字输入,并通过范围判断确保输入在1-9之间。注意:用户输入的是1-9,而内部列表索引是0-8,代码在此处做了 -1 的转换。

* *main()**:游戏的主循环。不断获取输入并尝试落子,如果落子失败(位置已被占用)则提示重试。游戏结束后打印最终棋盘和结果。

---

### 二、 用途

1. 编程教学与学习:这是一个非常经典的编程入门项目,涵盖了条件判断、循环、列表操作、面向对象设计、异常处理等基础知识点。

2. 游戏原型开发:可以作为更复杂棋盘游戏(如五子棋、黑白棋)的基础框架,只需修改棋盘大小和获胜判定逻辑即可。

3. AI算法验证:虽然当前是双人对战,但这个架构很容易扩展为“人机对战”。你可以基于此框架实现极小极大算法或蒙特卡洛树搜索(MCTS),来训练一个不可战胜的井字棋AI。

---

### 三、 注意事项与改进建议

1. 平局判定的逻辑隐患

* 现状:代码中 check_tie() 判断的是“棋盘是否下满”。但在 make_move 中,是先判断胜负,再判断平局。这虽然在逻辑上是正确的(因为如果第9步获胜check_winner 会先返回 True),但更优雅、更符合直觉的做法是将平局条件定义为:*self.move_count == 9**。因为当落子达到9次且没有胜者时,必然是平局,这样无需每次遍历整个棋盘检查空格。

2. 输入流的阻塞问题

* input() 函数是阻塞的,且如果用户输入了多余的字符(如 1 2int() 转换可能会引发奇怪的行为。可以优化为读取一行并去除首尾空格后再转换。

3. 魔法数字

代码中存在一些硬编码的数字,如 range(9), 0 <= pos <= 8, i3 等。如果未来想将游戏扩展为4x4棋盘,需要修改多处代码。建议将棋盘大小定义为类常量,如 BOARD_SIZE = 3

4. 游戏重开功能

* 当前游戏一旦结束,程序就退出了。可以在 main() 函数末尾增加一个询问玩家是否再玩一局的逻辑,如果选择是,则重新实例化 TicTacToe() 对象。

改进后的平局判定示例:

```python

def check_tie(self):

# 使用落子计数器判断,效率更高,逻辑更清晰

return self.move_count == 9

```

总的来说,这是一段结构清晰、逻辑严谨、格式规范的Python入门级游戏代码,非常适合用来学习编程基础和面向对象思想。


评论