您的位置:

Python 中的井字棋

在下面的教程中,我们将从头开始使用 Python 编程语言创建一个名为井字棋的游戏。为了更好地理解,我们将整个程序分成了不同的步骤。但是在我们进入程序之前,让我们了解一下这个游戏。

什么是井字棋?

井字棋是两名玩家在3×3 方形格子上玩的游戏之一。每个玩家在各自的回合中占据一个单元,保持以垂直、水平或对角线模式放置三个相似标记的目标。第一个玩家使用十字(X) 作为标记,而另一个玩家使用零或零(O) 。

现在,让我们了解井字棋的设计。

井字棋的设计

我们将使用命令提示符来玩井字棋。因此,主要目标是为井字棋构建一个设计。

目的:如果玩家需要标记特定的区块,他/她必须输入网格中显示的对应数字。例如,我们想占据右上方的块,然后我们必须在终端中输入数字 3。

让我们理解生成网格的代码片段。

程序:


# Function to print the Tic-Tac-Toe Design
def mytictactoe(val):
    print("\n")
    print("\t     |     |")
    print("\t  {}  |  {}  |  {}".format(val[0], val[1], val[2]))
    print('\t_____|_____|_____')

    print("\t     |     |")
    print("\t  {}  |  {}  |  {}".format(val[3], val[4], val[5]))
    print('\t_____|_____|_____')

    print("\t     |     |")

    print("\t  {}  |  {}  |  {}".format(val[6], val[7], val[8]))
    print("\t     |     |")
    print("\n")

说明:

在上面的代码片段中,我们为井字棋定义了一个以值为参数的函数。这里的值参数是由网格中每个单元的状态组成的列表。在功能中,我们打印了井字棋网格的设计。

现在,下一步是借助数据结构存储数据。

使用数据结构存储数据

任何游戏的原理都依赖于游戏背后的机制。因为我们正在创建一个相对简单和容易的游戏,包括的机制也很简单。

在任何时间点,都需要两条关键信息:

  1. 网格状态:我们必须创建一个包含每个单元状态的数据结构。这个州可以是有人的,也可以是空的。
  2. 每个玩家的招式:不知何故需要知道每个玩家过去和现在的招式,也就是‘X’和‘O’所占据的位置。

注:以上数据可借助网格状态进行访问。然而,每次我们想要玩家的位置时,都需要遍历这些信息。这可以称为时间与空间复杂性的权衡。为了节省时间,这是一种通用技术。

其语法如下所示:

代码片段:


# Function for a single game of Tic-Tac-Toe
def singlegame(curplayer):
    # Representing the Tic-Tac-Toe
    val = [' ' for i in range(9)]

    # Storing the positions occupied by X and O
    playerpos = {'X' : [], 'O' : []}

说明:

在上面的代码片段中,我们为井字棋定义了一个函数,其中值代表前一个函数的参数, playerpos 分别存储由十字(X) 和零(O) 占据的区块位置。

管理网格状态的字符列表中通常有三个值:

  1. ' ' -该字符表示空单元格。
  2. ‘X’-这个字符表示一个细胞被 X 玩家居住。
  3. ‘O’-这个字符表示一个细胞被 O 玩家居住。

每个玩家的移动被保存为整数列表的字典,其中键由对应玩家的【X】和【O】表示。它们各自的列表由提供给它们所在网格中的单元格的数字组成。

注意:curplayer 变量存储当前玩家的移动,如“X”或“O”。

理解游戏循环

每一个游戏都由某种类型的游戏循环组成,允许玩家玩游戏,直到玩家赢了或者游戏打成平手。在井字棋中,每个循环迭代都表示任何玩家的一次移动。

让我们考虑下面的代码片段来设计游戏循环。

语法:


# Loop of Game for a single game of Tic-Tac-Toe
while True:
    mytictactoe(val)

说明:

正如我们所观察到的,我们已经使用 While循环打印了函数 mytictactoe() 的值,为 Tic-Tac-Toe 的单个游戏生成了一个游戏循环。

处理玩家的输入

对于游戏中的每一次迭代,玩家必须为他的移动提供输入。让我们考虑以下语法来处理玩家的输入。

语法:


# Try-Exception block for CHANCE input
try:
    print("Player ", curplayer, " turn. Choose your Block : ", end="")
    chance = int(input())
except ValueError:
    print("Invalid Input!!! Try Again")
    continue

# Sanity check for CHANCE input
if chance < 1 or chance > 9:
    print("Invalid Input!!! Try Again")
    continue

# Checking if the block is not occupied already
if val[chance - 1] != ' ':
    print("Oops! The Place is already occupied. Try again!!")
    continue

说明:

对于上面的代码片段,我们已经创建了一个 try 块来处理玩家的非预期值。然后我们处理了值错误的异常,这样游戏就不会停止。稍后,我们进行了一些健全性检查,例如输入的值是否是有效的位置,如果是有效的位置,它是否已经被填充?

现在,让我们进入下一步。

更新游戏信息

根据玩家提供的输入,为了游戏的顺利进行,我们必须更新信息。我们可以通过在主项目中添加以下代码片段来更新游戏信息。

语法:


# Updating the game information

# Update the status of the grid
val[chance - 1] = curplayer

# Update the positions of the player
playerpos[curplayer].append(chance)

说明:

在上面的代码片段中,我们通过更新网格的状态和玩家的位置来更新游戏信息。值列表将根据当前玩家更新填充的单元格。玩家的位置会加上当前玩家刚刚占据的位置。

一旦 val 列表更新,我们将调用 mytictactoe() 函数,网格如下图所示:

输出:

             |     |
          1  |  2  |  3
        _____|_____|_____
             |     |
          4  |  5  |  6
        _____|_____|_____
             |     |
          7  |  8  |  9
             |     |

检查胜负

在每一次移动之后,我们需要检查是否有球员赢得了比赛或者比赛已经打成平手。我们可以借助下面给出的语法来检查它:

语法:


# Calling Function to check Victory
if check_Victory(playerpos, curplayer):
    mytictactoe(val)
    print("Congratulations! Player ", curplayer, " has won the game!")     
    print("\n")
    return curplayer

# Calling Function to check Tie
if check_Tie(playerpos):
    mytictactoe(val)
    print("Oh! Game Tied")
    print("\n")
    return 'D'

说明:

在上面的语法中,我们使用了 if 语句来检查 Win 或 Tie。单人游戏()功能将返回当前玩家,如果他/她赢得游戏。否则,比赛打成平手,‘D’被送回。

让我们考虑一下检查是否有玩家获胜的功能。

语法:


# Defining Function to check Victory
def check_Victory(playerpos, curplayer):

    # All probable winning combinations
    solution = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9], [1, 5, 9], [3, 5, 7]]

    # Loop to check whether any winning combination is satisfied or not
    for i in solution:
        if all(j in playerpos[curplayer] for j in i):

            # Return True if any winning combination is satisfied
            return True
    # Return False if no combination is satisfied 
    return False       

# Defining Function to check if the game is Tied
def check_Tie(playerpos):
    if len(playerpos['X']) + len(playerpos['O']) == 9:
        return True
    return False   

说明:

在上面的代码片段中,我们已经定义了检查胜利或平局的函数。这些功能分别是 check_Victory() 和 check_Tie() 。

  1. check_Victory(): 它存储了赢得游戏的所有组合。该功能检查当前玩家的位置是否满足任何获胜组合。如果是,它将返回真;否则,不满足要求将为假。
  2. check_Tie(): 挺简单的,检查所有‘九’位是否都被占用,游戏就平手了。

切换当前玩家

每个玩家一次只有一次机会。因此,每成功一次,当前玩家将被交换。让我们考虑下面的代码片段:

语法:


# Switching moves of the player
if curplayer == 'X':
    curplayer = 'O'
else:
    curplayer = 'X'

说明:

在下面的代码片段中,我们使用了 if-else 语句来切换玩家的移动,如果当前玩家标记了一个位置,那么当前玩家将被改变,另一个玩家将标记他们的移动。

这些是我们在制作一个游戏时需要关注的一些步骤。然而,我们将创建一个记分板系统来记录想要玩多种游戏的玩家。

输入玩家姓名

因为我们正在创建一个记分牌,所以我们有必要显示每个玩家的名字。

下面是相同的语法:

语法:


if __name__ == "__main__":

    print("First Player")
    FirstPlayer = input("Specify the Name: ")
    print("\n")

    print("Second Player")
    SecondPlayer = input("Specify the Name: ")
    print("\n")

说明:

我们可以观察到,我们使用了特殊变量 name 来获得“ main ”的值。然后,我们分别为第一个和第二个玩家的名字提供了输入。这将成为程序的入口点,当我们将要执行的程序时,它会先询问名字。

存储关于游戏的信息

我们必须存储诸如当前玩家、玩家选择(即 X 或 O)、可用选择(X 或 O)和记分板等信息。

语法:


# Storing the player who chooses X and O
curplayer = FirstPlayer

# Storing the Players' choice
playerchoice = {'X' : "", 'O' : ""}

# Storing the options
opt = ['X', 'O']

# Storing the scoreboard
scoreboard = {FirstPlayer: 0, SecondPlayer: 0}
myscoreboard(scoreboard)

说明:

在上面的代码片段中,我们已经将当前玩家设置为第一个玩家。我们还存储了玩家的选择、可用选项和记分板。

设计记分板

我们将在字典数据结构中设计一个记分板。对于这个记分牌,玩家的名字将作为键,他们的胜利总数将作为值。让我们考虑下面的代码片段来设计井字棋的记分牌。

语法:


def myscoreboard(scoreboard):
    print("\t--------------------------------")
    print("\t         SCORE BOARD       ")
    print("\t--------------------------------")

    listofplayers = list(scoreboard.keys())
    print("\t   ", listofplayers[0], "\t    ", scoreboard[listofplayers[0]])
    print("\t   ", listofplayers[1], "\t    ", scoreboard[listofplayers[1]])

    print("\t--------------------------------\n")

说明:

在上面的代码片段中,我们将函数定义为 myscoreboard ,它将参数作为记分牌。然后,我们为计分板打印了设计。我们已经使用定义了一个变量,该变量将玩家的名字存储为一个列表。按键()功能。然后我们将它们编入记分牌并显示分数。

创建外部游戏循环

为了维持井字棋的多场比赛,我们要求游戏再循环一次。当前玩家将为每场比赛选择标记。选择菜单应该在游戏的每次迭代中显示。

让我们考虑以下语法来创建外部玩家循环。

语法:


# Loop for a series of Tic-Tac-Toe game
# The loop executes until the players quit
while True:

     # Main Menu for Players
     print(curplayer, "will make the choice:")
     print("Press 1 for X")
     print("Press 2 for O")
     print("Press 3 to Quit")

说明:

在上面的代码片段中,我们创建了一个 while 循环来显示玩家的主菜单,当前玩家可以在标记(十字“X”或零“O”)之间进行选择或退出游戏。

输出:

First Player
Specify the Name: Andy

Second Player
Specify the Name: Carlo

        --------------------------------
                 SCORE BOARD       
        --------------------------------
            Andy             0
            Carlo            0
        --------------------------------

Andy will make the choice:
Press 1 for X
Press 2 for O
Press 3 to Quit

处理和分配玩家的选择

我们需要为每次迭代处理和存储当前玩家的选择。让我们考虑下面的代码片段。

语法:


# Try exception for THE_CHOICE input
try:
    the_choice = int(input())   
  except ValueError:
    print("Invalid Input!!! Try Again\n")
    continue

# Conditions for player choice  
if the_choice == 1:
    playerchoice['X'] = curplayer
    if curplayer == FirstPlayer:
        playerchoice['O'] = SecondPlayer
    else:
        playerchoice['O'] = FirstPlayer

elif the_choice == 2:
     playerchoice['O'] = curplayer
     if curplayer == FirstPlayer:
         playerchoice['X'] = SecondPlayer
     else:
         playerchoice['X'] = FirstPlayer

elif the_choice == 3:
     print("The Final Scores")
     myscoreboard(scoreboard)
     break  

else:
     print("Invalid Selection!! Try Again\n")

说明:

在上面的代码片段中,我们使用了 try-exception 块来处理 _choice 输入的任何异常。然后,我们使用 If-else语句为当前玩家创建了可供选择的选项菜单。

根据玩家的选择,数据将被存储。这很有意义,因为它会告诉我们每场比赛后谁赢了。

执行游戏

一旦存储了所有需要的信息,我们就可以执行独立的比赛并记录胜利标记。

其语法如下所示。

语法:


# Storing the winner in a single game of Tic-Tac-Toe
win = singlegame(opt[the_choice - 1])

说明:

在上面的代码片段中,我们已经存储了一个井字棋的赢家详细信息。

更新记分板

井字棋的每一场比赛后,我们都要更新记分牌。

让我们考虑以下代码片段来更新记分板。

语法:


# Updation of the scoreboard as per the winner
if win != 'D' :
    playerWon = playerchoice[win]
    scoreboard[playerWon] = scoreboard[playerWon] + 1

myscoreboard(scoreboard)

说明:

在上面的代码片段中,我们使用了 if语句来检查匹配是否不匹配。一旦比赛没有结束,记分牌就会更新。

切换选择播放器

在玩游戏时,切换选择标记的机会变得强制性。因此,让我们考虑以下语法来理解交换。

语法:


# Switching player who chooses X or O
if curplayer == FirstPlayer:
    curplayer = SecondPlayer
else:
    curplayer = FirstPlayer

说明:

在上面的代码片段中,我们再次使用 If-else语句在玩家之间切换以选择标记(十字或无)。

因此,我们成功地构建了自己的井字棋。游戏代码可从该链接下载: 点击此处下载

游戏时间到了

由于所有的步骤最终都完成了,这里是游戏的最终输出。

输出:

First Player
Specify the Name: Andy

Second Player
Specify the Name: Carlo

        --------------------------------
                 SCORE BOARD       
        --------------------------------
            Andy             0
            Carlo            0
        --------------------------------

Andy will make the choice:
Press 1 for X
Press 2 for O
Press 3 to Quit
1

             |     |
             |     |
        _____|_____|_____
             |     |
             |     |
        _____|_____|_____
             |     |
             |     |
             |     |

Player  X  turn. Choose your Block : 5

             |     |
             |     |   
        _____|_____|_____
             |     |
             |  X  |   
        _____|_____|_____
             |     |
             |     |   
             |     |

Player  O  turn. Choose your Block : 3

             |     |
             |     |  O
        _____|_____|_____
             |     |
             |  X  |   
        _____|_____|_____
             |     |
             |     |   
             |     |

Player  X  turn. Choose your Block : 1

             |     |
          X  |     |  O
        _____|_____|_____
             |     |
             |  X  |   
        _____|_____|_____
             |     |
             |     |   
             |     |

Player  O  turn. Choose your Block : 9

             |     |
          X  |     |  O
        _____|_____|_____
             |     |
             |  X  |   
        _____|_____|_____
             |     |
             |     |  O
             |     |

Player  X  turn. Choose your Block : 6

             |     |
          X  |     |  O
        _____|_____|_____
             |     |
             |  X  |  X
        _____|_____|_____
             |     |
             |     |  O
             |     |

Player  O  turn. Choose your Block : 4

             |     |
          X  |     |  O
        _____|_____|_____
             |     |
          O  |  X  |  X
        _____|_____|_____
             |     |
             |     |  O
             |     |

Player  X  turn. Choose your Block : 2

             |     |
          X  |  X  |  O
        _____|_____|_____
             |     |
          O  |  X  |  X
        _____|_____|_____
             |     |
             |     |  O
             |     |

Player  O  turn. Choose your Block : 8

             |     |
          X  |  X  |  O
        _____|_____|_____
             |     |
          O  |  X  |  X
        _____|_____|_____
             |     |
             |  O  |  O
             |     |

Player  X  turn. Choose your Block : 7

             |     |
          X  |  X  |  O
        _____|_____|_____
             |     |
          O  |  X  |  X
        _____|_____|_____
             |     |
          X  |  O  |  O
             |     |

Game Tied

        --------------------------------
                 SCORE BOARD       
        --------------------------------
            Andy             0
            Carlo            0
        --------------------------------

Carlo will make the choice:
Press 1 for X
Press 2 for O
Press 3 to Quit
3
The Final Scores
        --------------------------------
                 SCORE BOARD
        --------------------------------
            Andy             0
            Carlo            0
        --------------------------------