井字完美的AI算法:在&QUOT更深,创造叉"步骤算法、步骤、完美、AI

2023-09-11 03:04:07 作者:铮铮铁汉

我已经读计算器许多井字主题。而且我发现在维基百科上的策略是适合我的presentation项目:

I've already read many Tic Tac Toe topics on StackOverflow. And I found the strategy on Wikipedia is suitable for my presentation project:

一个播放器可以播放完美的井字棋,如果他们选择与移动   在下表中最高优先级[3]

A player can play perfect tic-tac-toe if they choose the move with the highest priority in the following table[3].

1)赢:如果你有两连胜,打第三拿到三一   行。

1) Win: If you have two in a row, play the third to get three in a row.

2)块:如果对手有两连胜,打第三阻止   它们。

2) Block: If the opponent has two in a row, play the third to block them.

3)货叉:创建一个机会,在这里,你可以赢得在两个方面

4)阻止对手的叉:

选项1:在连续创建两个迫使对手进入卫冕,为   只要它不会导致他们创建叉或获奖。对于   例如,如果X具有角,O的圆心,而X具有   对面的角落还有,O绝不能为了赢得起到一个角落。   (玩在这种情况下一个角落里创建了一个叉X取胜。)

Option 1: Create two in a row to force the opponent into defending, as long as it doesn't result in them creating a fork or winning. For example, if "X" has a corner, "O" has the center, and "X" has the opposite corner as well, "O" must not play a corner in order to win. (Playing a corner in this scenario creates a fork for "X" to win.)

选项2:如果有一个配置,其中对手可以派生,   阻止叉。

Option 2: If there is a configuration where the opponent can fork, block that fork.

5)中心:游戏中心

6)对面的角落:如果对手是在角落里,打   对面的角落。

6) Opposite Corner: If the opponent is in the corner, play the opposite corner.

7)空角:玩一个空的角落

7) Empty Corner: Play an empty corner.

8)空方:玩空方

我已经按照这些步骤,而电脑并不会丢失。然而,它的攻击方式是不完美的。因为我不知道该怎么办第3步这是我做的第3步:扫描每一个细胞,检查是否在该小区把令牌创建了一个岔路口,然后把它放在那里。

I've followed these step, and the computer never loses. However, the way it attacks is not perfect. Because I have no clue how to do step 3. Here is what I do in step 3: scan every cell, check if put token on that cell creates a fork, then put it there.

private void step3() // Create Fork.
{
    int[] dummyField = (int[])field.Clone();
    // Try Level 1 Dummy
    for (int i = 0; i < 9; i++)
    {
        if (dummyField[i] != 0) continue;
        dummyField[i] = 2;
        if (countFork(dummyField, 2) >= 2)
        {
            nextCell = i;
            return;
        }
        dummyField[i] = 0;
    }

}

请给我说说这一步一些建议。

Please give me some advice about this step.

EDIT1:计数叉会算多少叉的计算机(计算机标记为2,玩家标记为1,因为我用的方法步骤4中也是如此,因此对于令牌的参数 countFork 函数)。

The count fork will count how many forks that computer has (computer's tokens is 2, player tokens is 1, because I used that method for step 4 too, so there is a parameter for token in countFork function).

EDIT2:为什么我说这是不完美的原因是这样的(CPU先行,并且它的细胞是蓝色的,人体细胞是红色的)。 正如你所看到的,如果我把在顶侧单元,计算机获胜。但是,如果我把右侧的电池,这是一个平局,虽然计算机仍然可以取胜。

The reason why I say it is not perfect is this (CPU goes first, and its cells are blue, human cells are red). As you can see, if I put in the top-side cell, the computer wins. But if I put in the right-side cell, it's a tie, although the computer can still win.

EDIT3:不知道为什么,但我注释掉第3步,与电脑玩...完美!我真的很惊讶!这是我的countFork功能(我需要移植这个code翘,不支持2维数组,所以我用getNumberFromXY转换的二维数组1 - 尺寸):

Don't know why, but I commented out the step 3, and the computer plays... perfectly! I'm really surprised! Here is my countFork function (I need to port this code to Alice, which doesn't support 2-dimension array, so I use getNumberFromXY to convert 2-dimension array into 1-dimension):

private int countFork(int[] field, int token)
{
    int result = 0;

    // Vertical
    int cpuTokenCount;
    int spareCell;
    for (int x = 0; x < 3; x++)
    {
        cpuTokenCount = 0;
        spareCell = -1;
        for (int y = 0; y < 3; y++)
        {
            if (field[getNumberFromXY(x, y)] == token)
                cpuTokenCount++;
            else if (field[getNumberFromXY(x, y)] == 0)
                spareCell = getNumberFromXY(x, y);
        }
        if (cpuTokenCount == 2 && spareCell != -1) result++;
    }

    // Horizontal
    for (int y = 0; y < 3; y++)
    {
        cpuTokenCount = 0;
        spareCell = -1;
        for (int x = 0; x < 3; x++)
        {
            if (field[getNumberFromXY(x, y)] == token)
                cpuTokenCount++;
            else if (field[getNumberFromXY(x, y)] == 0)
                spareCell = getNumberFromXY(x, y);
        }
        if (cpuTokenCount == 2 && spareCell != -1) result++;
    }

    // Top-Left To Lower-Right Diagonal
    cpuTokenCount = 0;
    spareCell = -1;
    for (int i = 0; i < 3; i++)
    {
        if (field[getNumberFromXY(i, i)] == token)
            cpuTokenCount++;
        else if (field[getNumberFromXY(i, i)] == 0)
            spareCell = getNumberFromXY(i, i);
    }
    if (cpuTokenCount == 2 && spareCell != -1) result++;

    // Top-Right To Lower-Left Diagonal
    cpuTokenCount = 0;
    spareCell = -1;
    for (int i = 0; i < 3; i++)
    {
        if (field[getNumberFromXY(2 - i, i)] == token)
            cpuTokenCount++;
        else if (field[getNumberFromXY(2 - i, i)] == 0)
            spareCell = getNumberFromXY(2 - i, i);
    }
    if (cpuTokenCount == 2 && spareCell != -1) result++;

    return result;
}

EDIT4:根据soandos修复了这一错误,并更新了code在编辑3,现在,它完美的作品

推荐答案

我不知道这是最优雅的方式来做到这一点,但在这里是看叉两个步骤的方法。

I am not sure that it is the most elegant way to do it, but here is a two step way of looking at forks.

如果计算机无法赢下一轮,这是不是第一或第二回合,叉子是可能的(这不涉及创建安装了一个叉,只要找到一个叉)。

If the computer cannot win next turn, and it is not the first or second turn, a fork might be possible (this does not deal with creating the setup for a fork, just finding a fork).

对于每个空单元格中,填充它,然后运行您的第1步功能(看是否有两连胜)。如果它发现两个地方,恭喜,你有一个叉。如果没有,你不知道。

For each of the cells that are empty, fill it, then run your step 1 function (sees if there are two in a row). If it finds two places, congrats, you have a fork. If not, you don't.