项目:Tic-Tac-Toe 井字棋
😋 我们为你提供了一个简单有趣的项目,帮助你进行知识巩固,请认真阅读文档内容。
如果你卡住了,请记得回来阅读文档,或请求身边人的帮助。
进入项目目录后,运行:
pip3 install -r requirements.txt
以安装该项目所需的 Python 包(pygame)
理解
这个项目有两个主要文件: runner.py
和 tictactoe.py
。 tictactoe.py
包含了玩游戏和做出最佳动作的所有逻辑。 runner.py
已经为你实现,它包含了运行游戏图形界面的所有代码。一旦你完成了 tictactoe.py
中所有必需的功能,你就可以运行 python runner.py
来对抗你的人工智能了!
让我们打开 tictactoe.py
来了解所提供的内容。首先,我们定义了三个变量:X、O 和 EMPTY,以表示游戏的可能移动。
函数 initial_state
返回游戏的启动状态。对于这个问题,我们选择将游戏状态表示为三个列表的列表(表示棋盘的三行),其中每个内部列表包含三个值,即 X、O 或 EMPTY。以下是我们留给你实现的功能!
说明
实现 player
, actions
, result
, winner
, terminal
, utility
, 以及 minimax
.
player
函数应该以棋盘状态作为输入,并返回轮到哪个玩家(X 或 O)。- 在初始游戏状态下,X 获得第一步。随后,玩家交替进行每一个动作。
- 如果提供结束棋盘状态作为输入(即游戏已经结束),则任何返回值都是可接受的。
actions
函数应该返回一组在给定的棋盘状态上可以采取的所有可能的操作。- 每个动作都应该表示为元组
(i,j)
,其中i
对应于移动的行(0、1 或 2),j
对应于行中的哪个单元格对应于移动(也是 0、1、或 2)。 - 可能的移动是棋盘上任何没有 X 或 O 的单元格。
- 如果提供结束棋盘状态作为输入,则任何返回值都是可接受的。
- 每个动作都应该表示为元组
result
函数以一个棋盘状态和一个动作作为输入,并且应该返回一个新的棋盘状态,而不修改原始棋盘。- 如果
action
函数接受了一个无效的动作,你的程序应该raise an exception. - 返回的棋盘状态应该是从原始输入棋盘,并让轮到它的玩家在输入动作指示的单元格处移动所产生的棋盘。
- 重要的是,原始棋盘应该保持不变:因为 Minimax 最终需要在计算过程中考虑许多不同的棋盘状态。这意味着简单地更新棋盘上的单元格本身并不是
result
函数的正确实现。在做出任何更改之前,你可能需要先对棋盘状态进行deep copy。
- 如果
winner
函数应该接受一个棋盘作为输入,如果游戏结束,则返回游戏的获胜者。- 如果 X 玩家赢得了游戏,函数应该返回 X。如果 O 玩家赢得了比赛,函数应该返回 O。
- 一个人可以通过水平、垂直或对角连续三次移动赢得比赛。
- 你可以认为最多会有一个赢家(也就是说,没有一个棋盘会同时有两个玩家连着三个,因为这将是一个无效的棋盘状态)。
- 如果游戏没有赢家(要么是因为游戏正在进行,要么是因为比赛以平局结束),函数应该返回
None
。
terminal
函数应该接受一个棋盘作为输入,并返回一个布尔值,指示游戏是否结束。- 如果游戏结束,要么是因为有人赢得了游戏,要么是由于所有单元格都已填充而没有人获胜,则函数应返回
True
。 - 否则,如果游戏仍在进行中,则函数应返回
False
。
- 如果游戏结束,要么是因为有人赢得了游戏,要么是由于所有单元格都已填充而没有人获胜,则函数应返回
utility
函数应接受结束棋盘状态作为输入,并输出该棋盘的分数。- 如果 X 赢得了比赛,则分数为 1。如果 O 赢得了比赛,则分数为 -1。如果比赛以平局结束,则分数为 0。
- 你可以假设只有当
terminal(board)
为 True 时,才会在棋盘上调用utility
。
minimax
函数应该以一个棋盘作为输入,并返回玩家在该棋盘上移动的最佳移动。- 返回的移动应该是最佳动作
(i,j)
,这是棋盘上允许的动作之一。如果多次移动都是同样最佳的,那么这些移动中的任何一次都是可以接受的。 - 如果该棋盘是结束棋盘状态,则
minimax
函数应返回None
。 对于所有接受棋盘作为输入的函数,你可以假设它是一个有效的棋盘(即,它是包含三行的列表,每行都有三个值 X、O 或 EMPTY)。你不应该修改所提供的函数声明(每个函数的参数的顺序或数量)。、 一旦所有功能都得到了正确的实现,你就应该能够运行python runner.py
并与你的人工智能进行比赛。而且,由于井字棋是双方最佳比赛的平局,你永远不应该能够击败人工智能(尽管如果你打得不好,它可能会打败你!)
- 返回的移动应该是最佳动作
提示
如果你想在不同的 Python 文件中测试你的函数,你可以用类似于
from tictactoe import initial_state
的代码来导入它们。欢迎在
tictactoe.py
中添加其他辅助函数,前提是它们的名称不会与模块中已有的函数或变量名称冲突。alpha-beta 剪枝是可选的,这可能会让你的人工智能运行更高效!
条件允许直接在 CS50 的服务器上进行测试,操作方法:
运行
bashcheck50 ai50/projects/2024/x/tictactoe
1再运行:
bashstyle50 tictactoe.py
1