Python中的Nights and Crosss或Tic Tac Toe

本文介绍了一个基于Python控制台的经典井字棋游戏实现,重点在于将游戏逻辑与用户界面分离,允许开发者使用不同框架创建更复杂的UI,而不影响核心游戏逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A console-based implementation of the classic paper and pencil game in Python.

Python中经典纸笔游戏的基于控制台的实现。

I’m not the first person to write a Tic Tac Toe game and I won’t be the last but a central theme of this project is the separation of the game logic from the UI. I have written a simple console-based UI but you could write something more sophisticated using another framework without touching the game logic class.

我不是第一个编写Tic Tac Toe游戏的人,我也不会是最后一个人,但是这个项目的中心主题是将游戏逻辑与UI分离。 我已经编写了一个基于控制台的简单UI,但是您可以使用其他框架编写更复杂的内容,而无需接触游戏逻辑类。

The project consists of two files:

该项目包含两个文件:

  • nac.py

    nac.py
  • nacconsole.py

    nacconsole.py

You can clone/download the Github repository if you don’t want to copy/paste the code by hand.

如果您不想手工复制/粘贴代码,则可以克隆/下载Github存储库

The nac.py file contains a class implementing the game logic, independently of any user interface. The nacconsole.py file contains a class providing a console-based UI using curses. I wrote an introduction to curses a while ago which you might like to read before this article.

nac.py文件包含一个实现游戏逻辑的类,与任何用户界面无关。 nacconsole.py文件包含使用curses提供基于控制台的UI的类。 我写了一篇有关curses简介 ,您可能想在本文之前阅读它。

This is the rather long NaC class. You might find it easier to read the descriptions of the various functions first before coming back to the code.

这是相当长的NaC课程。 您可能会发现,在回到代码之前,先阅读各种功能的说明会比较容易。

级别枚举 (Levels enum)

Not essential as I could just use arbitrary numbers or strings but it does make the code neater.

不是必需的,因为我可以只使用任意数字或字符串,但这确实使代码更整洁。

__在里面__ (__init__)

The on_change and on_game_over arguments are functions injected by the code instantiating an object, and are called when the respective situations occur. We’ll see the UI use these later on.

on_changeon_game_over参数是通过实例化对象的代码注入的函数,并在发生相应情况时被调用。 稍后我们将看到UI使用它们。

The rest of __init__ just creates a few variables for use within the class.

__init__的其余部分仅创建一些变量供类使用。

human_move (human_move)

This method is for the UI to call when the human (or other sentient being capable of using a keyboard) makes a move. The argument is the square as described in the docstring. We convert that into a row/column pair using the __get_row_column function and then check the square hasn’t already been filled. If not the square’s value is set to “X” and the _on_change function called to let the UI know it needs to draw the symbol in the square. Lastly _move_count is implemented and we check whether the game is now over.

当人员(或其他能够使用键盘的感知器)移动时,此方法供UI调用。 参数是文档字符串中描述的正方形。 我们使用__get_row_column函数将其转换为行/列对,然后检查正方形是否尚未填充。 如果不是,则将正方形的值设置为“ X”,并调用_on_change函数以使UI知道需要在正方形中绘制符号。 最后,实现_move_count ,我们检查游戏是否结束。

computer_move (computer_move)

This is called after human_move, or by the UI if the user decides to let the computer move first.

这是在human_move之后human_move ,如果用户决定先让计算机移动,则由UI调用。

It calls one of three other functions depending on the level, before incrementing _move_count and checking for a winner.

在递增_move_count并检查获胜者之前,它会根据级别调用其他三个函数之一。

新游戏 (new_game)

Here we just need to reset the variables representing the game state.

在这里,我们只需要重置代表游戏状态的变量即可。

get_level_string (get_level_string)

This function returns the level as a string suitable for displaying in a UI.

此函数以适合在UI中显示的字符串形式返回级别。

__computer_move_idiot (__computer_move_idiot)

In idiot mode the computer searches at random for an empty square to place its “O” in.

在白痴模式下,计算机随机搜索一个空的正方形以放置其“ O”。

__computer_move_average (__computer_move_average)

The average level uses either idiot level or genius level at random with 50:50 probability.

平均水平随机使用白痴水平或天才水平,概率为50:50。

__computer_move_genius (__computer_move_genius)

If it is starting the game the computer will always select the centre square. Otherwise it uses a list of win paths which I’ll describe in a moment to find the optimum square. The stages it goes through are:

如果开始游戏,计算机将始终选择中心方块。 否则,它将使用一系列获胜路径,稍后我将对其进行描述以找到最佳平方。 它经历的阶段是:

  • Check if any squares give the computer an immediate win

    检查是否有方格可以立即赢得计算机
  • Check if any squares give the user an immediate win; if so block them

    检查是否有正方形能立即赢得用户; 如果是这样,阻止他们
  • Check if any squares give the computer a potential win next time

    检查是否有任何方格下次可以赢得计算机
  • Check if any squares give the user a potential win next time; if so block them

    检查是否有任何平方数会给用户下一次潜在的胜利; 如果是这样,阻止他们
  • If none of these succeed call __computer_move_idiot to use a random square

    如果这些都不成功,请调用__computer_move_idiot以使用随机平方

__get_row_column (__get_row_column)

This converts the square numbers 1–9 entered by the user to a row/column pair.

这会将用户输入的1–9的平方数转换为行/列对。

__check_for_winner (__check_for_winner)

Check the three rows, three columns and two diagonals to see if any have three matching symbols.

检查三行,三列和两个对角线,看是否有三个匹配的符号。

__create_win_paths (__create_win_paths)

This function creates a list of nine dictionaries representing the nine possible win paths, ie straight lines of three squares. Each dictionary contains the row/column numbers of the three squares as well as the counts of each of the three possible states of a square: empty, “O” and “X”.

此函数创建九个字典的列表,这些字典代表九种可能的获胜路径,即三个正方形的直线。 每个字典都包含三个正方形的行/列号,以及一个正方形的三个可能状态中的每个状态的计数:空,“ O”和“ X”。

__populate_win_paths (__populate_win_paths)

Here we set the counts of each possible state per win path for use by __computer_move_genius as described above.

在这里,我们如上所述设置每个获胜路径的每个可能状态的计数,以供__computer_move_genius使用。

__find_empty_square (__find_empty_square)

If __computer_move_genius finds a win path it wishes to use for its next move it calls this function to find an empty square in this path. If there are two the first is returned.

如果__computer_move_genius找到了希望用于下一步操作的获胜路径,它将调用此函数以在该路径中找到一个空正方形。 如果有两个,则返回第一个。

That’s the NaC class finished so now we can move on to writing a UI in nacconsole.py. As I mentioned above I have written a console UI with curses but the NaC class has no user interface code so can be used with any library or framework, for example PyGame, Tkinter, PyQt or whatever.

NaC类完成了,所以现在我们可以继续在nacconsole.py中编写UI。 正如我上面提到的,我编写了一个带有curses的控制台UI,但是NaC类没有用户界面代码,因此可以与任何库或框架一起使用,例如PyGame,Tkinter,PyQt等。

__在里面__ (__init__)

After a load of stuff to set up curses we create an instance of NaC before calling functions to draw the board and show the level. Finally we call a function to start the event loop.

在设置好很多东西后,我们在调用函数画图和显示关卡之前创建了一个NaC实例。 最后,我们调用一个函数来启动事件循环。

__event_loop (__event_loop)

After being created user interfaces just sit and wait for the user to do something, that something mostly being hitting a key or moving/clicking the mouse. We therefore need an infinite loop which “listens” for such events and responds accordingly. This program only accepts keyboard input so we call self.screen.getch() in the loop and then check if the key is one which is meaningful for the game.

创建用户界面后,只需坐着等待用户做某事,大部分是敲键或移动/单击鼠标。 因此,我们需要一个无限循环来“监听”此类事件并做出相应响应。 该程序仅接受键盘输入,因此我们在循环中调用self.screen.getch() ,然后检查该键是否对游戏有意义。

The characters which we need to respond to are numbers 1 to 9 (ASCII 49 to 58) and a few letters. The relevant functions are called in an if/elif stack and any others are ignored.

我们需要响应的字符是1到9(ASCII 49到58)和几个字母。 相关功能在if / elif堆栈中调用,其他任何功能均被忽略。

Note that although this is an infinite loop we do provide a way out via “x” for exit.

请注意,尽管这是一个无限循环,但我们确实提供了通过“ x”退出的方法。

__draw_board (__draw_board)

This is a fiddly but straightforward function which draws the nine empty squares and also a panel at the bottom listing the valid characters for user input.

这是一个简单但简单的功能,它绘制了九个空的正方形,并且在底部的面板上列出了可供用户输入的有效字符。

__show_level (__show_level)

Showing the level is done by a separate function so it can be called without redrawing the whole game.

显示关卡是由一个单独的函数完成的,因此无需重新绘制整个游戏就可以调用它。

on_game_changed (on_game_changed)

This is one of the functions passed to the NaC class’s __init__, and is called by the NaC class when either the computer or the user make a move. It therefore just needs to draw an “X” or “O” in the correct square.

这是传递给NaC类的__init__的函数之一,并且在计算机或用户进行移动时由NaC类调用。 因此,只需要在正确的正方形上绘制“ X”或“ O”即可。

on_game_over (on_game_over)

This is the other function passed to the NaC class and is called when the game is over. The winner argument is “X” for the player, “O” for the computer or “ “ for a draw. The function uses this to create a suitable message which is then displayed in an orange box in the middle of the board.

这是传递给NaC类的另一个函数,在游戏结束时被调用。 获胜者参数是玩家“ X”,计算机是“ O”或平局是“”。 该函数使用它来创建合适的消息,然后将其显示在板中间的橙色框中。

Lastly we create an instance of the NaCConsole class which is all that is necessary to kick off a new game.

最后,我们创建NaCConsole类的实例,这是启动新游戏所需的全部。

运行程序 (Running the Program)

The coding is finished so lets run the program.

编码完成,因此让我们运行程序。

python3.8 nacconsole.py

python3.8 nacconsole.py

This is a screenshot of a sample game after completion.

这是完成后的示例游戏的屏幕截图。

Image for post

If you want to write your own UI or if you can improve the game logic please feel free to fork the Github repository.

如果您想编写自己的UI或可以改善游戏逻辑,请随时使用Github存储库。

翻译自: https://medium.com/explorations-in-python/noughts-and-crosses-or-tic-tac-toe-in-python-29945154f8ad

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值