如何在 Python 中创建井字游戏?

用 Python 构建你的井字游戏

本文将引导你使用 Python 编写一个简单的井字游戏。这个过程能帮助你掌握游戏逻辑的构建,并了解代码结构。

游戏是人们娱乐的重要方式之一。我们可以在网络、移动设备、电脑等各种平台上找到各种类型的游戏。这次,我们不制作复杂的大型游戏,而是用 Python 创建一个命令行(CLI)版本的井字游戏。

如果你还不熟悉井字游戏,可以点击这里进行了解。即使你不熟悉游戏规则,也不必担心,我们将在文章中详细介绍。

井字游戏简介

本教程分为三个部分。第一部分将讲解井字游戏的基本规则。第二部分将介绍帮助我们构建游戏逻辑的算法。最后,我们将看到结构化的代码及其详细解释。

如果你已经熟悉井字游戏,可以直接跳过第一部分。

现在,让我们开始第一部分吧。

如何玩井字游戏

井字游戏需要两名玩家参与,每名玩家使用不同的标志。通常使用的标志是 “X” 和 “O”。游戏在一个包含 9 个格子的棋盘上进行。

下面是井字棋盘的示意图:

井字游戏棋盘

游戏规则如下:

  • 首先,一名玩家在一个空的格子上放置自己的标志。
  • 然后,另一名玩家在另一个空的格子上放置自己的标志。
  • 玩家的目标是将自己的标志连成一行、一列或一条对角线。
  • 游戏将持续进行,直到一名玩家获胜,或者所有格子都被填满但无人获胜(平局)。

让我们看一些游戏实例:

在上面的例子中,玩家 “X” 赢得了比赛。所有的 “X” 标志都沿对角线排列。因此,该玩家获胜。

总共有 8 种获胜的排列方式。让我们看看这 8 种排列方式:

最后,如果棋盘被填满,但没有出现任何获胜排列,则游戏为平局。现在你应该已经了解如何玩井字游戏了。

现在你可以去这里亲自体验一下,完全理解游戏规则。如果你已经理解了,那么让我们进入下一部分。

游戏算法

接下来,我们将讨论编写游戏代码的算法。这个算法可以帮助你用任何编程语言编写游戏。让我们看看它是如何实现的。

  • 创建一个二维数组来表示棋盘,并将每个元素初始化为空。
    • 你可以使用任何符号表示空。这里,我们使用连字符 ‘-’。
  • 编写一个函数来检查棋盘是否被填满。
    • 遍历棋盘,如果发现任何一个格子为空,则返回 `false`,否则返回 `true`。
  • 编写一个函数来检查玩家是否获胜。
    • 我们需要检查上一节中讨论的所有获胜可能性。
    • 检查所有行、列和两条对角线。
  • 编写一个函数来显示棋盘,因为在用户玩游戏时,我们会多次向用户展示棋盘。
  • 编写一个函数来启动游戏。
    • 随机选择一位玩家先行。
    • 进入一个无限循环,当游戏结束(无论是获胜还是平局)时跳出循环。
      • 向用户展示棋盘,提示用户选择下一步行动的格子。
      • 要求用户输入行号和列号。
      • 用相应玩家的标志更新格子。
      • 检查当前玩家是否获胜。
      • 如果当前玩家获胜,则打印获胜消息,并跳出无限循环。
      • 接下来,检查棋盘是否被填满。
      • 如果棋盘已满,则打印平局消息,并跳出无限循环。
    • 最后,向用户展示最终的棋盘状态。

你可能已经对游戏流程有了大概的了解。如果你没有完全理解,也不必担心,看完代码你会更加清晰。

现在让我们进入代码部分。我假设你的电脑上已经安装了 Python,可以尝试运行代码。

代码实现

请仔细阅读下面的代码:

import random

class TicTacToe:

    def __init__(self):
        self.board = []

    def create_board(self):
        for i in range(3):
            row = []
            for j in range(3):
                row.append('-')
            self.board.append(row)

    def get_random_first_player(self):
        return random.randint(0, 1)

    def fix_spot(self, row, col, player):
        self.board[row][col] = player

    def is_player_win(self, player):
        win = None

        n = len(self.board)

        # checking rows
        for i in range(n):
            win = True
            for j in range(n):
                if self.board[i][j] != player:
                    win = False
                    break
            if win:
                return win

        # checking columns
        for i in range(n):
            win = True
            for j in range(n):
                if self.board[j][i] != player:
                    win = False
                    break
            if win:
                return win

        # checking diagonals
        win = True
        for i in range(n):
            if self.board[i][i] != player:
                win = False
                break
        if win:
            return win

        win = True
        for i in range(n):
            if self.board[i][n - 1 - i] != player:
                win = False
                break
        if win:
            return win
        return False

        for row in self.board:
            for item in row:
                if item == '-':
                    return False
        return True

    def is_board_filled(self):
        for row in self.board:
            for item in row:
                if item == '-':
                    return False
        return True

    def swap_player_turn(self, player):
        return 'X' if player == 'O' else 'O'

    def show_board(self):
        for row in self.board:
            for item in row:
                print(item, end=" ")
            print()

    def start(self):
        self.create_board()

        player="X" if self.get_random_first_player() == 1 else 'O'
        while True:
            print(f"Player {player} turn")

            self.show_board()

            # taking user input
            row, col = list(
                map(int, input("Enter row and column numbers to fix spot: ").split()))
            print()

            # fixing the spot
            self.fix_spot(row - 1, col - 1, player)

            # checking whether current player is won or not
            if self.is_player_win(player):
                print(f"Player {player} wins the game!")
                break

            # checking whether the game is draw or not
            if self.is_board_filled():
                print("Match Draw!")
                break

            # swapping the turn
            player = self.swap_player_turn(player)

        # showing the final view of board
        print()
        self.show_board()


# starting the game
tic_tac_toe = TicTacToe()
tic_tac_toe.start()

下面是代码的运行示例输出:

$ python tic_tac_toe.py 
Player X turn
- - -
- - -
- - -
Enter row and column numbers to fix spot: 1 1

Player O turn
X - -
- - -
- - -
Enter row and column numbers to fix spot: 2 1

Player X turn
X - -
O - -
- - -
Enter row and column numbers to fix spot: 1 2

Player O turn
X X -
O - -
- - -
Enter row and column numbers to fix spot: 1 3

Player X turn
X X O
O - -
- - -
Enter row and column numbers to fix spot: 2 2

Player O turn
X X O
O X -
- - -
Enter row and column numbers to fix spot: 3 3

Player X turn
X X O        
O X -        
- - O
Enter row and column numbers to fix spot: 3 2

Player X wins the game!

X X O
O X -
- X O
  

帮助你理解代码结构的一些要点:

  • 我们使用一个类来组织所有的方法,这样可以方便地在其他代码中重用。
  • 我们将每个任务都定义为独立的函数,即使任务很小。这有助于代码的维护。
  • 如果我们想更新游戏,上述两种方法可以帮助我们轻松完成更新。

你可以根据自己的喜好调整代码结构。代码结构并不受限制。

总结

恭喜! 你已经成功地从头开始创建了一个井字游戏。虽然这不是我们每天玩的视觉游戏,但它可以帮助你掌握游戏逻辑,并保持代码的结构清晰。可以尝试使用类似的思路来创建其他有趣的游戏。也许你能找到一些儿时的经典游戏。

编码愉快!👩‍💻

接下来,你可以探索如何使用 Python 的 `unittest` 模块创建猜数字游戏和单元测试。

喜欢这篇文章吗?分享给你的朋友吧!