python编写井字棋_编写井字游戏

python编写井字棋

Programming computer games may be the most technically challenging (and possibly the best paying) job that a programmer can have. Top level games require the best from both programmers and computers.

计算机游戏进行编程可能是程序员可以从事的技术上最具挑战性的工作(可能是报酬最高的工作)。 顶级游戏需要程序员和计算机的鼎力支持。

Visual Basic 6 has now been thoroughly bypassed as a platform for game programming. (It never really was one. Even in the "good ol' days", serious game programmers would never use a high-level language like VB 6 because you just couldn't get the cutting edge performance that most games require.) But the simple "Tic Tac Toe" game is a great introduction to programming that is a little more advanced than "Hello World!"

现在, Visual Basic 6已被完全绕开作为游戏编程平台。 (它从来不是真正的一个。即使在“好日子”中,严肃的游戏程序员也永远不会使用VB 6这样的高级语言,因为您无法获得大多数游戏所要求的最先进的性能。)但是简单的“井字游戏”是对编程的一个很好的介绍,它比“ Hello World!”要先进一些

This is a great introduction to many of the fundamental concepts of programming since it combines techniques including:

这是对许多编程基本概念的精彩介绍,因为它结合了以下技术:

  • The use of arrays. The X and O markers are kept in separate arrays and the entire arrays are passed between functions to keep track of the progress of the game.

    使用数组 。 X和O标记保存在单独的数组中,整个数组在函数之间传递,以跟踪游戏的进度。

  • Using VB 6 level graphics: VB 6 doesn't offer great graphical capability, but the game is a good introduction to what is available. Much of the rest of this series is an exploration of how GDI+, the next generation of Microsoft graphics, replaces the VB 6 level graphics.

    使用VB 6级别的图形:VB 6没有提供强大的图形功能,但是该游戏很好地介绍了可用的功能。 本系列其余大部分内容探讨了下一代Microsoft图形GDI +如何替代VB 6级图形。
  • Using math calculations for program control: The program uses clever modulo (Mod) and integer division calculations using the two-game marker arrays to determine when a three-element "win" has occurred.

    使用数学计算进行程序控制:程序使用巧妙的模数(Mod)和整数除法计算,并使用两个游戏标记数组确定何时发生了三元素“胜利”。

The class of programming in this article is perhaps just a little past the beginning level but it should be good for "intermediate" programmers. But let's start at an elementary level to illustrate some of the concepts and get you started with your Visual Basic game programming career. Even students more advanced than that may find that it's slightly challenging to get the objects in the form just right.

本文中的编程类别可能仅比入门水平稍高一点,但是对“中级”程序员来说应该是不错的选择。 但是,让我们从基本的层次开始说明一些概念,并开始您的Visual Basic游戏编程生涯。 即使是比这更高级的学生,也可能会发现,正确放置表单中的对象有些挑战。

如何玩井字游戏 ( How to Play Tic Tac Toe )

If you've never played Tic Tac Toe, here are the rules. Two players alternate at placing Xs and Os into 3 x 3 playing field.

如果您从未玩过Tic Tac Toe ,请遵循以下规则。 两名玩家交替将X和O放置在3 x 3的游戏场地中。

Before the game starts, both players have to agree about who will go first and who will mark his moves with which symbol. After the first move, the players alternately place their marks in any empty cell. The goal of the game is to be the first player with three marks in a horizontal, diagonal or vertical line. If there are no empty cells and neither player has a winning combination, the game is a draw.

在游戏开始之前,两个玩家都必须就谁先走以及谁将用哪个符号标记自己的动作达成一致。 第一步之后,玩家将标记交替放置在任何空白单元格中。 游戏的目标是成为第一个在水平,对角线或垂直线上带有三个标记的玩家。 如果没有空单元,并且没有玩家有获胜组合,则该游戏为平局。

启动程序 ( Starting the Program )

Before starting any actual coding, it's always a good idea to change the names of any components you use. Once you start coding, the name will be used automatically by Visual Basic so you want it to be the right name. We'll use the form name frmTicTacToe and we'll also change the caption to "About Tic Tac Toe."

在开始任何实际编码之前,最好更改您使用的任何组件的名称。 一旦开始编码 ,Visual Basic将自动使用该名称,因此您希望它是正确的名称。 我们将使用表单名称frmTicTacToe ,并将标题更改为“关于井字游戏”。

With the form established, use the line toolbox control to draw a 3 x 3 grid. Click the line tool, then draw a line where you want it. You'll have to create four lines this way and adjust their length and position to make them look right. Visual Basic also has some convenient tools under the Format menu that will help. This is a great chance to practice with them.

建立表单后,使用线条工具箱控件绘制一个3 x 3的网格。 单击线条工具,然后在所需位置画一条线。 您必须以这种方式创建四行,并调整其长度和位置以使其看起来正确。 Visual Basic在“格式”菜单下还提供了一些方便的工具,这些工具会有所帮助。 这是和他们一起练习的绝好机会。

In addition to the playing grid, we'll need some objects for the X and O symbols that will be placed on the grid. Since there are nine spaces in the grid, we'll create an object array with nine spaces, called elements in Visual Basic.

除了播放网格外,我们还需要一些将用于放置在网格上的X和O符号的对象。 由于网格中有9个空格,因此我们将创建一个具有9个空格的对象数组,在Visual Basic中称为元素。

There are several ways to do just about everything in the Visual Basic development environment, and creating control arrays is is no exception. Probably the easiest way is to create the first label (click and draw just like the line tool), name it, set all of the attributes (such as Font and ForeColor), and then make copies of it. VB 6 will ask if you want to create a control array. Use the name lblPlayGround for the first label.

在Visual Basic开发环境中,有几种方法可以完成所有事情,创建控件数组也不例外。 可能最简单的方法是创建第一个标签(单击并绘制,就像线条工具一样),命名它,设置所有属性(例如Font和ForeColor),然后复制它。 VB 6将询问您是否要创建控件数组。 将名称lblPlayGround用作第一个标签。

To create the other eight elements of the grid, select the first label object, set the Index property to zero, and press CTRL+C (copy). Now you can press CTRL+V (paste) to create another label object. When you copy objects like this, each copy will inherit all properties except Index from the first one. Index will increase by one for each copy. This is a control array because they all have the same name, but different index values.

若要创建网格的其他八个元素,请选择第一个标签对象,将Index属性设置为零,然后按CTRL + C(复制)。 现在,您可以按CTRL + V(粘贴)来创建另一个标签对象。 当您像这样复制对象时,每个副本都将继承除第一个索引以外的所有属性。 每个副本的索引将增加一。 这是一个控制数组,因为它们都具有相同的名称,但索引值不同。

If you create the array this way, all of the copies will be stacked on top of each other in the upper left corner of the form. Drag each label to one of the playing grid positions. Be sure that index values are sequential in the grid. The logic of the program depends on it. The label object with index value 0 should be in the top left corner, and the bottom right label should have index 8. If the labels cover the playing grid, select each label, right-click, and select Send to Back.

如果以这种方式创建阵列,则所有副本将在窗体的左上角彼此堆叠。 将每个标签拖到播放网格位置之一。 确保索引值在网格中是连续的。 程序的逻辑取决于它。 索引值为0的标签对象应位于左上角,而右下方的标签应具有索引8。如果标签覆盖了播放网格,请选择每个标签,单击鼠标右键,然后选择“发送回”。

Since there are eight possible ways to win the game, we'll need eight different lines to show the win on the playing grid. You will use the same technique to create another control array. First, draw the line, name it linWin, and set the Index property to zero. Then use copy-paste technique to produce seven more lines. The following illustration shows how to set the index numbers correctly.

由于有八种可能的方式来赢得比赛,因此我们需要八条不同的线来显示比赛中的胜利。 您将使用相同的技术来创建另一个控件数组。 首先,绘制线条,将其命名为linWin,并将Index属性设置为零。 然后使用复制粘贴技术再产生七行。 下图显示了如何正确设置索引号。

In addition to the label and line objects, you need some command buttons to play the game and more labels to keep score. The steps to create these are not detailed here, but these are the objects you need.

除了标签和线条对象之外,您还需要一些命令按钮来玩游戏,还需要更多标签来保持得分。 创建这些步骤的步骤不在此处详述,但是这些是您需要的对象。

Two button objects:

两个按钮对象

  • cmdNewGame

    cmdNewGame
  • cmdResetScore

    cmdResetScore

Frame object fraPlayFirst containing two option buttons:

框架对象fraPlayFirst包含两个选项按钮:

  • optXPlayer

    optXPlayer
  • optOPlayer

    optOPlayer

Frame object fraScoreBoard containing six labels. Only lblXScore and lblOScore are changed in the program code.

框架对象fraScoreBoard包含六个标签。 程序代码中仅lblXScore和lblOScore被更改。

  • lblX

    bl
  • lblXScore

    lblXScore
  • lblO

    bl
  • lblOScore

    lblOScore
  • lblMinus

    lblMinus
  • lblColon

    lbl冒号

Finally, you also need the label object lblStartMsg to 'mask' the cmdNewGame button when it shouldn't be clicked. This isn't visible in the illustration below because it occupies the same space in the form as the command button. You may have to move the command button temporarily to draw this label on the form.

最后,当不应该单击cmdNewGame按钮时,还需要标签对象lblStartMsg对其进行“遮罩”。 这在下图中不可见,因为它在窗体中与命令按钮占据相同的空间。 您可能必须暂时移动命令按钮才能在表单上绘制此标签。

So far, no VB coding has been done, but we're finally ready to do that.

到目前为止,还没有完成VB编码,但是我们终于准备好了。

初始化 ( Initialization )

Now you get to finally start coding the program. If you haven't already, you might want to download the source code to follow along as the operation of the program is explained.

现在,您终于可以开始对程序进行编码了。 如果尚未下载源代码,则可能需要下载源代码以跟随程序的操作说明。

One of the first design decisions to make is how to keep track of the current 'state' of the game. In other words, what are the current Xs and Os on the playing grid and who moves next. The concept of 'state' is critical in a lot of programming, and in particular, it's important in programming ASP and ASP.NET for the web

首先要做出的设计决定之一就是如何跟踪游戏的当前“状态”。 换句话说,当前在游戏网格上的X和O是什么,接下来是谁。 “状态”的概念在许多编程中都至关重要,尤其是在为Web编程ASP和ASP.NET时很重要

There are several ways that this could be done, so it's a critical step in the analysis. If you were solving this problem on your own, you might want to draw a flowchart and try out different options with 'scratch paper' before starting any coding.

有几种方法可以完成此操作,因此这是分析中的关键步骤。 如果您自己解决此问题,则可能需要绘制流程图并尝试使用“草稿纸”尝试其他选项,然后再开始编码。

变数 ( Variables )

Our solution uses two "two-dimensional arrays" because that helps keep track of 'state' by simply changing the array indexes in program loops. The state of the top-left corner will be in the array element with index (1, 1), the top-right corner will be in (1, 3), the bottom-right in (3,3), and so forth. The two arrays that do this are:

我们的解决方案使用两个“二维数组”,因为这可以通过简单地在程序循环中更改数组索引来帮助跟踪“状态”。 左上角的状态将在索引为(1,1)的数组元素中,右上角将在(1,3)中,右下角在(3,3)中,依此类推。 执行此操作的两个数组是:

iXPos(x, y)
iXPos(x,y)

and

iOPos(x, y)
iOPos(x,y)

There are a lot of different ways this can be done and the final VB.NET solution in this series shows you how to do it with just a single one-dimensional array.

有很多不同的方法可以完成此操作,本系列的最终VB.NET解决方案将向您展示如何仅使用一个一维数组来完成此操作。

The programming to translate these arrays into player win decisions and visible displays in the form are on the next page.

在下一页上将这些数组转换为玩家获胜决定和表格中可见显示的程序。

You also need a few global variables as follows. Notice that these are in the General and Declarations code for the form. This makes them "module level" variables that can be referenced anywhere in the code for this form. For more on this, check Understanding the Scope of Variables in Visual Basic Help.

您还需要一些全局变量,如下所示。 请注意,这些在表单的“常规”和“声明”代码中。 这使它们成为“模块级”变量,可以在此表单的代码中的任何地方引用。 有关此的更多信息,请在Visual Basic帮助中选中“了解变量的范围”。

There are two areas where variables are initialized in our program. First, a few variables are initialized while the form frmTicTacToe is loading.

在我们的程序中有两个初始化变量的区域。 首先,在加载frmTicTacToe表单时初始化一些变量。

Private Sub Form_Load()
私人子Form_Load()

Second, before each new game, all variables that need to be reset to starting values are assigned in an initialization subroutine.

其次,在每个新游戏之前,所有需要重置为初始值的变量都在初始化子例程中分配。

Sub InitPlayGround()
子InitPlayGround()

Note that the form load initialization also calls the playground initialization.

请注意,表单加载初始化也称为游乐场初始化。

One of the critical skills of a programmer is the ability to use the debugging facilities to understand what the code is doing. You can use this program to try:

程序员的一项关键技能是能够使用调试工具来了解代码在做什么。 您可以使用此程序尝试:

  • Stepping through the code with the F8 key

    使用F8键单步执行代码
  • Setting a watch on key variables, such as sPlaySign or iMove

    监视关键变量,例如sPlaySign或iMove

    Setting a breakpoint and querying the value of variables. For example, in the inner loop of the initialization:

    设置断点并查询变量的值。 例如,在初始化的内部循环中:

lblPlayGround((i - 1) * 3 + j - 1).Caption = ""
lblPlayGround((i-1)* 3 + j-1).Caption =“”

Note that this program clearly shows why it's a good programming practice to keep data in arrays whenever possible. If you did not have arrays in this program, you would have to write code something like this:

请注意,该程序清楚地说明了为什么尽可能将数据保留在数组中是一种好的编程习惯。 如果此程序中没有数组,则必须编写如下代码:


Line1.Visible = False
Line1.Visible = False
Line2.Visible = False
Line2.Visible = False
Line3.Visible = False
Line3.Visible = False
Line4.Visible = False
Line4.Visible = False
Line5.Visible = False
Line5.Visible = False
Line6.Visible = False
Line6.Visible = False
Line7.Visible = False
Line7.Visible = False

instead of this:

代替这个:


linWin(i).Visible = False
linWin(i).Visible = False
Next i
接下来我

采取行动 ( Making a Move )

If any part of the system can be thought of as 'the heart', it's subroutine lblPlayGround_Click. This subroutine is called every time a player clicks the playing grid. (Clicks must be inside one of the nine lblPlayGround elements.) Notice that this subroutine has an argument: (Index As Integer). Most of the other 'event subroutines', like cmdNewGame_Click() do not. Index indicates which label object has been clicked. For example, index would contain the value zero for the top-left corner of the grid and the value eight for the bottom-right corner.

如果系统的任何部分都可以视为“心脏”,则它是子例程lblPlayGround_Click。 每当玩家单击播放网格时,都会调用此子例程。 (单击必须在9个lblPlayGround元素之一之内。)请注意,此子例程具有一个参数:(整数索引)。 其他大多数“事件子例程”(例如cmdNewGame_Click())都没有。 索引指示已单击的标签对象。 例如,索引将包含网格左上角的值零和右下角的值八。

After a player clicks a square in the game grid, the command button to start another game, cmdNewGame, is "turned on' by making it visible. The state of this command button does double duty because it's also used as a boolean decision variable later in the program. Using a property value as a decision variable is usually discouraged because if it ever becomes necessary to change the program (say, for example, to make the cmdNewGame command button visible all the time), then the program will unexpectedly fail because you might not remember that it's also used as part of the program logic. For this reason, it's always a good idea to search through program code and check the use of anything you change when doing program maintenance, even property values. This program violates the rule partly to make this point and partly because this is a relatively simple piece of code where it's easier to see what is being done and avoid problems later.

玩家单击游戏网格中的正方形后,通过使其可见而“打开”了用于启动另一个游戏cmdNewGame的命令按钮。此命令按钮的状态起着双重作用,因为稍后它也用作布尔决策变量通常不建议使用属性值作为决策变量,因为如果有必要更改程序(例如,使cmdNewGame命令按钮始终可见),则程序将意外失败,因为您可能不记得它也被用作程序逻辑的一部分,因此,搜索程序代码并检查在进行程序维护时更改的内容(甚至是属性值)的使用始终是一个好主意。规则部分是为了说明这一点,部分是因为这是一段相对简单的代码,在其中更容易看到正在执行的操作并避免以后出现问题。

A player selection of a game square is processed by calling the GamePlay subroutine with Index as the argument.

通过调用以Index为参数的GamePlay子例程来处理游戏方的玩家选择。

处理移动 ( Processing the Move )

First, you check to see if an unoccupied square was clicked.

首先,您检查是否单击了未占用的正方形。

If lblPlayGround(xo_Move).Caption = "" Then
如果lblPlayGround(xo_Move).Caption =“”然后

Once we're sure this is a legitimate move, the move counter (iMove) is incremented. The next two lines are very interesting since they translate the coordinates from the one-dimensional If lblPlayGround component array to two-dimensional indexes that you can use in either iXPos or iOPos. Mod and integer division (the 'backslash') are mathematical operations that you don't use every day, but here's a great example showing how they can be very useful.

一旦确定这是合法的移动,移动计数器(iMove)就会增加。 接下来的两行非常有趣,因为它们将坐标从一维If lblPlayGround组件数组转换为可以在iXPos或iOPos中使用的二维索引。 Mod和整数除法(“反斜杠”)是您每天都不会使用的数学运算,但是,这是一个很好的示例,展示了它们如何非常有用。


iMove = iMove + 1
iMove = iMove +1
x = Int(xo_Move / 3) + 1
x =整数(xo_Move / 3)+ 1
y = (xo_Move Mod 3) + 1
y =(xo_Move Mod 3)+ 1

The xo_Move value 0 will be translated to (1, 1), 1 to (1, 2) ... 3 to (2, 1) ... 8 to (3, 3).

xo_Move值0将转换为(1,1),1转换为(1,2)... 3转换为(2,1)... 8转换为(3,3)。

The value in sPlaySign, a variable with module scope, keeps track of which player made the move. Once the move arrays are updated, the label components in the playing grid can be updated with the appropriate sign.

sPlaySign中的值(具有模块范围的变量)会跟踪哪个玩家进行了移动。 一旦更新了移动数组,就可以使用适当的符号来更新播放网格中的标签组件。


iOPos(x, y) = 1
iOPos(x,y)= 1
iWin = CheckWin(iOPos())
iWin = CheckWin(iOPos())
Else
其他
iXPos(x, y) = 1
iXPos(x,y)= 1
iWin = CheckWin(iXPos())
iWin = CheckWin(iXPos())
End If
万一
lblPlayGround(xo_Move).Caption = sPlaySign
lblPlayGround(xo_Move).Caption = sPlaySign

For example, when the X player clicks the top left corner of the grid, variables will have the following values:

例如,当X播放器单击网格的左上角时,变量将具有以下值:

The user screen shows only an X in the upper left box, while the iXPos has a 1 in the upper left box and 0 in all of the others. The iOPos has 0 in every box.

用户屏幕在左上方的框中仅显示一个X,而iXPos在左上方的框中显示为1,其他所有字段均为0。 iOPos在每个框中都有0。

The values changes when the O player clicks the center square of the grid. Now th iOPos shows a 1 in the center box while the user screen shows an X in the upper left and an O in the center box. The iXPos shows only the 1 in the upper left corner, with 0 in all of the other boxes.

当O播放器单击网格的中心正方形时,值会更改。 现在,iOPos在中心框中显示1,而用户屏幕在左上方显示X,在中心框中显示O。 iXPos在左上角仅显示1,在所有其他框中显示0。

Now that you know where a player clicked, and which player did the clicking (using the value in sPlaySign), all you have to do is find out if someone won a game and figure out how to show that in the display.

既然您知道了某个玩家单击的位置以及该玩家单击的位置(使用sPlaySign中的值),那么您所要做的就是找出某人是否赢得了一场比赛,并弄清楚如何在屏幕上显示出来。

寻找赢家 ( Finding a Winner )

After each move, the CheckWin function checks for the winning combination. CheckWin works by adding down each row, across each column and through each diagonal. Tracing the steps through CheckWin using Visual Basic's Debug feature can be very educational. Finding a win is a matter of first, checking whether three 1's were found in each of the individual checks in the variable iScore, and then returning a unique "signature" value in Checkwin that is used as the array index to change the Visible property of one element in the linWin component array. If there is no winner, CheckWin will contain the value -1. If there is a winner, the display is updated, the scoreboard is changed, a congratulation message is displayed, and the game is restarted.

每次移动后,CheckWin函数都会检查获胜组合。 CheckWin的工作原理是将每一行,每一列和每个对角线相加。 使用Visual Basic的“调试”功能通过CheckWin跟踪步骤可能非常有教育意义。 首先要找到一个胜利,首先检查在变量iScore的每个检查中是否都找到三个1,然后在Checkwin中返回一个唯一的“签名”值,该值用作更改数组的Visible属性的数组索引。 linWin组件数组中的一个元素。 如果没有获胜者,CheckWin将包含值-1。 如果有赢家,则更新显示,更改记分牌,显示祝贺消息,然后重新开始比赛。

Let's go through one of the checks in detail to see how it works. The others are similar.

让我们详细检查其中一项以了解其工作原理。 其他类似。


For i = 1 To 3
对于i = 1至3
iScore = 0
iScore = 0
CheckWin = CheckWin + 1
CheckWin = CheckWin +1
For j = 1 To 3
对于j = 1到3
iScore = iScore + iPos(i, j)
iScore = iScore + iPos(i,j)
Next j
下一个j
If iScore = 3 Then
如果iScore = 3,则
Exit Function
退出功能
End If
万一
Next i
接下来我

The first thing to notice is that the first index counter i counts down the rows while the second j counts across the columns. The outer loop, then simply moves from one row to the next. The inner loop counts the 1's in the current row. If there are three, then you have a winner.

首先要注意的是,第一个索引计数器i对行进行递减计数,而第二个索引计数器i对列进行计数。 然后,外循环仅从一行移到下一行。 内部循环在当前行中计数1。 如果有三个,那么您就有赢家。

Notice that you also keep track of the total number of squares tested in the variable CheckWin, which is the value passed back when this function terminates. Each winning combination will end up with a unique value in CheckWin from 0 to 7 which is used to select one of the elements in the linWin() component array. This makes the order of the code in function CheckWin important too! If you moved one of the blocks of loop code (like the one above), the wrong line would be drawn on the playing grid when someone wins. Try it and see!

请注意,您还可以在CheckWin变量中跟踪测试的平方总数,该变量是此函数终止时传回的值。 每个获胜组合最终将在CheckWin中从0到7拥有一个唯一值,该唯一值用于选择linWin()组件数组中的元素之一。 这也使功能CheckWin中的代码顺序很重要! 如果您移动了循环代码块之一(如上面的代码块),则当有人获胜时,会在播放网格上画错线。 试试看!

整理细节 ( Finishing Details )

The only code not yet discussed is the subroutine for a new game and the subroutine that will reset the score. The rest of the logic in the system makes creating these quite easy. To start a new game, you have only to call the InitPlayGround subroutine. As a convenience for players since the button could be clicked in the middle of a game, you ask for confirmation before going ahead. You also ask for confirmation before restarting the scoreboard.

尚未讨论的唯一代码是新游戏的子例程和将重置得分的子例程。 系统中的其余逻辑使创建这些逻辑变得非常容易。 要开始新游戏,您只需调用InitPlayGround子例程。 为了方便玩家,因为可以在游戏过程中单击按钮,因此在进行下一步操作之前,您需要先进行确认。 您还需要在重新启动记分板之前进行确认。

翻译自: https://www.thoughtco.com/programming-the-tic-tac-toe-game-4079040

python编写井字棋

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值