Tic Tac Toe完美的AI算法:更深入的“创建分叉”步骤

时间:2011-11-27 16:34:37

标签: c# java algorithm tic-tac-toe

我已经在StackOverflow上阅读了许多Tic Tac Toe主题。我发现维基百科上的策略适合我的演示项目:

  

如果玩家选择移动,玩家可以玩完美的井字游戏   下表中最高优先级[3]。

     

1)胜利:如果你连续两次,那就打三分之一得到三分   行。

     

2)阻挡:如果对手连续两次,则发挥第三个阻挡   它们。

     

3)福克斯:创造一个可以通过两种方式获胜的机会。

     

4)阻挡对手的分叉:

     

选项1:连续创建两个以强制对手进行防守,如   只要它不会导致他们创造一个分叉或获胜。对于   例如,如果“X”有一个角,“O”有中心,“X”有   对角也是,“O”一定不能在角落里取胜。   (在这种情况下扮演一个角落会为“X”赢得一个分叉。)

     

选项2:如果有对手可以分叉的配置,   阻止那个分叉。

     

5)中心:玩中心。

     6)相反的角落:如果对手在角落里,那就玩吧   对面的角落。

     

7)空角:空角落。

     

8)空边:空洞边。

我已经按照这一步,计算机永远不会丢失。但是,它的攻击方式并不完美。因为我不知道如何做第3步。这是我在第3步中所做的:扫描每个单元格,检查是否在该单元格上放置标记创建了一个fork,然后将其放在那里。

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;
    }

}

请给我一些关于这一步的建议。

EDIT1:计数分叉将计算计算机有多少分叉(计算机的代币为2,玩家代币为1,因为我也在步骤4中使用了该方法,因此countFork中有一个代码参数功能)。

EDIT2:我说它不完美的原因是(CPU先行,其细胞为蓝色,人体细胞为红色)。 enter image description here 正如您所看到的,如果我放入顶部单元格,计算机就会获胜。但是,如果我放入右侧单元格,它就是一个平局,虽然计算机仍然可以获胜。

EDIT3:不知道为什么,但我评论了第3步,电脑正在玩......完美!我真的很惊讶!这是我的countFork函数(我需要将此代码移植到Alice,它不支持二维数组,因此我使用getNumberFromXY将二维数组转换为一维) :

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修复了错误,并在编辑3更新了代码,现在它完美无缺!

1 个答案:

答案 0 :(得分:8)

我不确定这是最优雅的方式,但这是一个两步查看分叉的方法。

如果计算机无法在下一回合获胜,并且它不是第一轮或第二轮,则可能有一个分叉(这不涉及为分叉创建设置,只是找到一个分叉)。

对于每个空单元格,填充它,然后运行步骤1函数(查看连续两行)。如果找到两个地方,恭喜你,你有一个叉子。如果没有,你就不会。