关于井字游戏的建议

发布于 2024-10-01 19:38:34 字数 223 浏览 2 评论 0原文

我正在设计 Tic-Tac-Toe 游戏的实施策略。由于这是我的第一个游戏实现,我有点困惑,需要一些一般性的指导。

现在,井字棋中的获胜组合总数为 8 个。目前,我计划将这些获胜组合存储在一个数组中。一旦最终用户至少进行了 3 次移动,我将开始通过将玩家使用的当前位置与该数组进行比较来检查玩家是否赢得了游戏。然而,我确信这不是检查玩家是否有获胜组合的有效方法。

有人可以建议我如何设计游戏的逻辑吗?

I am designing my implementation strategy for Tic-Tac-Toe game. Since this is my 1st game implementation, I am a bit confused and need some general pointers.

Now, the total number of winning combinations in a Tic-Tac-Toe are 8. Currently, I plan to store these winning combinations in an array. Once the end user has made at least 3 moves, I would start checking if the Player has won the game by comparing the current positions used by a Player against this array. However, I am sure this is not an efficient way to check if the player has a winning combination.

Can anyone please suggest me on how to go about with design the logic for the game?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

随波逐流 2024-10-08 19:38:34

不用担心效率。我写了一个回溯解决方案,只有 549,945 种可能的游戏状态。在我的笔记本电脑上运行这些只需不到 0.25 秒。这是我查看游戏是否结束的逻辑 - 显然效率不高,但这并不重要:

private boolean isWinningMove(int row, int col) {
    int piece = board[row][col];

    // check current row
    boolean w = true;
    for (int x = 0; x < 3; x++) { w = w && (board[row][x] == piece); }
    if (w) { return true; }

    // check current column
    w = true;
    for (int x = 0; x < 3; x++) { w = w && (board[x][col] == piece); }
    if (w) { return true; }

    // check 0,0 diagonal
    w = true;
    for (int x = 0; x < 3; x++) { w = w && (board[x][x] == piece); }
    if (w) { return true; }

    // check 0,2 diagonal
    w = true;
    for (int x = 0; x < 3; x++) { w = w && (board[x][2 - x] == piece); }
    return w;
}

这是我的结果,它与 tic-tac-toe 维基百科页面上的数据相匹配:

Moves Simulated: 549945
Draws=46080   Player1-Wins=131184   Player2-Wins=77904
Perfect Strategy Implies: Always a tie.

Games won in 0 moves? 0
Games won in 1 moves? 0
Games won in 2 moves? 0
Games won in 3 moves? 0
Games won in 4 moves? 0
Games won in 5 moves? 1440
Games won in 6 moves? 5328
Games won in 7 moves? 47952
Games won in 8 moves? 72576
Games won in 9 moves? 81792

Don't worry about efficiency. I wrote a backtracking solution and there are only 549,945 possible game states. It takes less than 0.25 seconds to run through these on my laptop. Here was my logic to see if the game was over - clearly not very efficient, but it doesn't matter:

private boolean isWinningMove(int row, int col) {
    int piece = board[row][col];

    // check current row
    boolean w = true;
    for (int x = 0; x < 3; x++) { w = w && (board[row][x] == piece); }
    if (w) { return true; }

    // check current column
    w = true;
    for (int x = 0; x < 3; x++) { w = w && (board[x][col] == piece); }
    if (w) { return true; }

    // check 0,0 diagonal
    w = true;
    for (int x = 0; x < 3; x++) { w = w && (board[x][x] == piece); }
    if (w) { return true; }

    // check 0,2 diagonal
    w = true;
    for (int x = 0; x < 3; x++) { w = w && (board[x][2 - x] == piece); }
    return w;
}

Here were my results, which match data on the Wikipedia page for tic-tac-toe:

Moves Simulated: 549945
Draws=46080   Player1-Wins=131184   Player2-Wins=77904
Perfect Strategy Implies: Always a tie.

Games won in 0 moves? 0
Games won in 1 moves? 0
Games won in 2 moves? 0
Games won in 3 moves? 0
Games won in 4 moves? 0
Games won in 5 moves? 1440
Games won in 6 moves? 5328
Games won in 7 moves? 47952
Games won in 8 moves? 72576
Games won in 9 moves? 81792
錯遇了你 2024-10-08 19:38:34

由于井字游戏的状态空间很小,您可以存储所有可能的最终游戏位置,并使用旋转,但我认为您想得太多了。

不要为游戏板存储 3x3 数组,而是使用 7x7 数组,最里面的 3x3 为游戏板。每个方块至少应该有三个可以代表的值 - 例如 PLAYER_1PLAYER_2NONE。最初,所有值都应设置为 NONE。然后,在每个玩家移动之后,检查被选择用于三连的方格周围;上面 2 个、下面 2 个、左 2 个、右 2 个、左上 2 个、右下 2 个、右上 2 个、左下 2 个。

为什么是 7x7 阵列?使用 7x7 数组,您可以安全地从 3x3 区域中的任何方格向各个方向搜索,而无需使用 if 语句来查看是否走出了数组的边缘。棋盘将如下所示:

  0 1 2 3 4 5 6
0 * * * * * * *

1 * * * * * * *

2 * * * * * * *

3 * * * * * * *

4 * * * * * * *

5 * * * * * * *

6 * * * * * * *

例如,如果第一个玩家在 tic-tac-toe 棋盘上移动到 0,0,则与在 7x7 棋盘上移动到 2,2 相同。移动完成后,您将检查 2,2 方格周围是否有连续的三个方格具有相同的值(

  • 上面:2,0 和 2,1 以及 2,2,
  • 下面:2,2)和 2,3 和 2,4
  • 左:0,2 和 1,2 和 2,2
  • 右:2,2、2,3 和 2,4
  • 左上角:0,0 和 1,1 和 2,2
  • 右上:2,2 和 3,1 和 4,0
  • 左下:0,4 和 1,3 和 2,2
  • 右下:2,2 和 3,3 和 4,4

由于正方形带3x3 棋盘周围的值始终为 NONE,它们永远不会触发获胜条件。

如果其中任何一个都匹配相同的玩家值(例如第一个玩家的 PLAYER_1),则游戏以胜利结束。否则,如果所有方格都被占据,则游戏为平局。

我过去曾在其他类似游戏中使用过它,效果非常好。

Since the state space for tic-tac-toe is so small, you could store all the possible end game positions, and use rotations, but I think you're overthinking it a little.

Instead of storing a 3x3 array for the game board, use a 7x7 array, with the inner-most 3x3 for the game board. You should have at least three values that each square can represent -- something like PLAYER_1, PLAYER_2 and NONE. Initially, all values should be set to NONE. Then, after every player's move, check all around the square that was chosen for for 3-in-a-row; 2 above, 2 below, 2 left, 2 right, 2 upper left, 2 lower right, 2 upper right, 2 lower left.

Why the 7x7 array? With a 7x7 array, you can safely search in all directions from any square in the 3x3 area without requiring if statements to see if you're walking off the edge of the array. The board will look like this:

  0 1 2 3 4 5 6
0 * * * * * * *

1 * * * * * * *

2 * * * * * * *

3 * * * * * * *

4 * * * * * * *

5 * * * * * * *

6 * * * * * * *

For example, if the first player moves to, 0,0 on the tic-tac-toe board, that is the same as moving to 2,2 on the 7x7 board. When the move is made, you run a check all around the 2,2 square to see if there are three squares in a row that have the same value

  • above: 2,0 and 2,1 and 2,2
  • below: 2,2 and 2,3 and 2,4
  • left: 0,2 and 1,2 and 2,2
  • right: 2,2, and 2,3 and 2,4
  • upper-left: 0,0 and 1,1 and 2,2
  • upper-right: 2,2 and 3,1 and 4,0
  • lower-left: 0,4 and 1,3 and 2,2
  • lower-right: 2,2 and 3,3 and 4,4

Since the band of squares around the 3x3 board will always have the value NONE, they can never trigger a winning condition.

If any of those all match the same player value (e.g. PLAYER_1 for the first player), then the game is over with a win. Else, if all squares are taken, the game is a draw.

I've used this for other similar games in the past and it works quite well.

如梦亦如幻 2024-10-08 19:38:34

考虑用整数表示棋盘。

-1 = X
 0 = empty
 1 = O

现在,将 8 种可能性(3 个上下、3 个左右、2 个对角线)中每一种的方块值相加。

如果总和为 3,则 O 获胜
如果总和为 -3,则 X 获胜;

如果总和为 2,则 O 在这些位置之一有获胜的棋步
如果总和 i -2,则 X 在这些位置之一有获胜的举动。

人工智能可以使用它作为决策的基础。一步向前看就足以永不失败。

如果人工智能开始游戏,最好的棋步是角球。如果对手未能占据中心,则 AI 获胜。如果他确实占据了中心,那么人工智能要么获胜,要么平局。

Consider representing the board by integers.

-1 = X
 0 = empty
 1 = O

now, add up the value of the squares for each of the 8 possibilities (3 up and down, 3 left and right, 2 diagionals).

if the sum is 3, O wins
if the sum is -3, X wins

if the sum is 2, then O has a winning move in one of those positions
if the sum i -2, then X has a winning move in one of those positions.

The AI can use that as a basis for making decisions. A one move look ahead is sufficient to never lose.

If the AI starts the game, the best move is a corner. If the opponent fails to take the center, the AI wins. If he does take the center, then either the AI wins, or draws.

凶凌 2024-10-08 19:38:34

我没有迭代某些东西,而是只写了 8 种组合。

我的评估函数执行以下操作:如果 A 是移动的一侧,并且 A 是移动的一侧。如果所有组合之一中有两个 A 元素和 0(空),则获胜:

boolean player_can_win(int value) { //value is side's element*2
    return board[0] + board[1] + board[2] == value
            || board[3] + board[4] + board[5] == value
            || board[6] + board[7] + board[8] == value
            || board[0] + board[3] + board[6] == value
            || board[1] + board[4] + board[7] == value
            || board[2] + board[5] + board[8] == value
            || board[0] + board[4] + board[8] == value
            || board[2] + board[4] + board[6] == value;
}

Instead of iterating over something I just wrote the 8 combinations.

My evaluation func does the following: if A is side to move & if there is two A elements and a 0 (empty) in one of all combinations then it's a win:

boolean player_can_win(int value) { //value is side's element*2
    return board[0] + board[1] + board[2] == value
            || board[3] + board[4] + board[5] == value
            || board[6] + board[7] + board[8] == value
            || board[0] + board[3] + board[6] == value
            || board[1] + board[4] + board[7] == value
            || board[2] + board[5] + board[8] == value
            || board[0] + board[4] + board[8] == value
            || board[2] + board[4] + board[6] == value;
}
盛夏已如深秋| 2024-10-08 19:38:34

如果您玩的是广义的 NXN 井字棋,那么显式存储并与解决方案进行比较并不是最有效的,但由于它就像小棋盘,并且只有 8 个这样的组合,因此显式存储这样的解决方案没有任何问题。

更大的问题是,根据存储风格,与解决方案无关的空间可能会成为问题。

O - -        - O -
X X X   vs.  X X X
O - O        O - O

比较 3x3 状态数组,这些数组是不同的,因此这种方法需要超过 8 个最终状态,

我猜你保留了类似 gameState 3x3 数组的东西,其中有 Blank=0, X=1, O=2 ?

除了这些显式比较之外,您还可以执行类似的操作,

win = false   
// rows/columns
for i in 0,1,2
   if (state[i][0] != BLANK && state[i][0] == state[i][1] == state[i][2]) win = true
          #extensible to NxN - all(j == state[i][0] for j in state[i])
   if (state[0][i] != BLANK && state[0][i] == state[1][i] == state[2][i]) win = true
          #extensible to NxN - all(j == state[0][i] for j in zip(*state)[i])
//diagonals
if (state[0][0] != BLANK && state[0][0] == state[1][1] == state[2][2]) win = true
          #extensible to NxN - all(state[j][j] == state[0][0] for j in range(len(state))
if (state [2][0] != BLANK && state[2][0] == state[1][1] == state[0][2]) win = true

如果您希望 win 存储获胜者而不是标志,则将 win = BLANK 放在顶部,并设置为任何相关方块的值。不过没必要,获胜者显然是最近的一步!

我认为编写井字棋的部分可能是你觉得最具挑战性的部分,但人工智能会不会太难。编写一个不会输的人工智能并不是太困难,但也不是那么简单(至少总是能打平)。如果你想要一个相对较好的人工智能,偶尔会失败,你需要添加一些随机性或其他东西。

Explicitly storing and comparing to the solutions isnt the most efficient if you were playing generalized N X N tic-tac-toe, but since it's such as small board and there are only 8 such combos there is nothing wrong with explicitly storing solutions like this.

The bigger issue is that depending on storage style, spaces that aren't relevant to the solution might be an issue.

O - -        - O -
X X X   vs.  X X X
O - O        O - O

comparing 3x3 state arrays these are different and as such this method would require well over 8 end states

I presume you keep something like a gameState 3x3 array with blank=0, X=1, O=2 ?

Besides those explicit comparisons, you can do something like

win = false   
// rows/columns
for i in 0,1,2
   if (state[i][0] != BLANK && state[i][0] == state[i][1] == state[i][2]) win = true
          #extensible to NxN - all(j == state[i][0] for j in state[i])
   if (state[0][i] != BLANK && state[0][i] == state[1][i] == state[2][i]) win = true
          #extensible to NxN - all(j == state[0][i] for j in zip(*state)[i])
//diagonals
if (state[0][0] != BLANK && state[0][0] == state[1][1] == state[2][2]) win = true
          #extensible to NxN - all(state[j][j] == state[0][0] for j in range(len(state))
if (state [2][0] != BLANK && state[2][0] == state[1][1] == state[0][2]) win = true

If you want win to store the winner rather than flag, then make win = BLANK up top, and set to the value of any of the involved squares. Shouldn't be necessary tho, winner is obviously the most recent move!

I think the part of writing tic-tac-toe that you may find most challenging, but not too hard, would the AI. It is not too difficult, but not exactly trivial, to write an AI that wont lose (can always at least force a tie). If you want a relatively good AI that is capable of losing occasionally, youll need to add some randomness or something.

爱人如己 2024-10-08 19:38:34

就人工智能而言,实现井字游戏可能是最简单的问题
和搜索空间。

关键是用 Minimax 解决问题,迭代加深深度优先搜索Alpha-beta 剪枝算法。

这是我用 Python 实现的游戏实现,只有大约 200 行代码,并且能够玩人与人人与计算机计算机与计算机游戏。它还保留了导致最佳移动的深度和达到/修剪的节点数量的统计数据。

我强烈推荐 edX.org 人工智能 课程,提供当前人工智能主题和解决方案的基础知识。

Implementing the Tic Tac Toe game is probably the simplest problem to solve in terms of AI
and search space.

The key is to approach the problem with Minimax, Iterative deepening Depth-first search and Alpha-beta pruning algorithms.

Here's my implementation of the game in Python, which is only ~200 lines of code and has the capability to play a game as Human vs. Human, Human vs. Computer, and Computer vs. Computer. It also keeps the statistics on depths and number of nodes reached/pruned leading up to the best move.

I highly recommend edX.org Artificial Intelligence course, which gives the the fundamental knowledge on current AI topics and solutions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文