关于点和盒子游戏的Java minimax

时间:2014-03-29 22:50:43

标签: java artificial-intelligence minimax

我正在尝试实现一个使用Minimax进行点和盒游戏的人工智能(http://en.wikipedia.org/wiki/Dots_and_Boxes

这是我到目前为止所做的:

    public Line makeMove(GameState gs) {
    if (gs.getRemainingLines().size() == 1) {
        return gs.getRemainingLines().get(0);
    }

    if (gs.getPlayer() == 1) {
        int minscore = -1;
        GameState g = gs.clone();
        Line lnew = null;
        List<Line> l = gs.getRemainingLines();
        for (Line l2 : l) {
            g.addLine(l2);
            if (evaluate(g) > minscore) {
                minscore = (evaluate(g));
                lnew = l2;
            }
        }
        return lnew;
    } else {
        int maxscore = 999;
        GameState g = gs.clone();
        Line lnew = null;
        List<Line> l = gs.getRemainingLines();
        for (Line l2 : l) {
            g.addLine(l2);
            if (evaluate(g) < maxscore) {
                maxscore = (evaluate(g));
                lnew = l2;
            }
        }
        return lnew;
    }

}

然而,它一直在返回null,我认为我没有正确地对待极小极大。谁能给我一些指示。

getRemainingLines()会返回仍然可能的动作列表。

evaluate()返回得分的int。

2 个答案:

答案 0 :(得分:2)

我建议您完全重新考虑代码。查看代码的问题(以及为什么这里有许多响应)是难以遵循并且难以调试的问题。例如,gs.getRemainingLines是什么,它究竟做了什么? (为什么剩余的行而不是所有合法的行?)

但是,通过一些简化,可以更容易地弄清楚发生了什么并修复它。

在抽象层面上,minimax就是这个程序:

float minimax_max(GameState g)
{
    if (g is terminal or max depth reached)
        return eval(g);

    float bestVal = -inf;
    bestMove = null;

    moves = g->getLegalMoves();
    for (m : moves)
    {
        ApplyMove(m);
        if (g->nextPlayer == maxPlayer)
            nextVal = minimax_max(g);
        else
            nextVal = minimax_min(g);
        if (nextVal > bestVal)
        {
            bestVal = nextVal;
            bestMove = m;
        }
        UndoMove(m);
    }

    return bestVal;
}

我还没有确切地说明如何在最后获得/使用最后一步,但这并不难。您还需要minimax_min的另一个过程,或者您可以在代码中添加if语句。

如果您查看代码,您已将其编写得非常接近,但您在代码中留下了许多游戏特定的详细信息。但是,你不应该考虑那些使minimax正常工作的事情

特别是,如果您提供评估州的GetMoves()ApplyMove()UndoMove()eval()的功能,则可以抽象地推断大多数游戏。 (进一步的搜索增强功能需要更多功能,但这会让你有很长的路要走。)

您可能希望以这种方式重新考虑因素的一些原因:

  • 您现在可以单独测试minimax和其他代码。

  • 您可以通过验证所有合法移动是否已生成来测试您的点和方框代码,并且在应用移动后您将拥有合法状态,并且接下来会移动正确的玩家。 (您可以播放和撤消长时间的随机移动序列,以帮助确认您之后总是回到开始状态。)

  • 您可以轻松地在各个状态下测试您的评估功能,以确保其正常工作。 (实际上,您通常无法搜索游戏结束以确定获胜者。)

  • 您可以使用简单的评估函数测试minimax并进行测试以查看是否进行了正确的移动。 (例如,如果您喜欢在边缘移动,则单层搜索应返回边缘移动)

  • 其他人可以更轻松地阅读您的代码。我们可以查看每段代码并查看它是否正确,而不必将特定于游戏的实现细节混合到minimax特定的细节中。

  • 如果您可以正确应用和撤消动作,则无需复制游戏状态。这将使代码更有效。

虽然您可以尝试修复代码而不进行重构(例如,只需找到它返回null的第一个位置,并指出错误的位置),从长远来看,如果没有这些代码,您的代码将难以调试和改进变化。

答案 1 :(得分:1)

要检查的第一件事是gs.getRemainingLines()实际上还有行。

另一个问题是您要将每一行添加到GameState g进行检查。您需要在调用evaluate后删除每个添加的行,或者将克隆放在顶部的循环中,例如

int minscore = -1;
Line lnew = null;
List<Line> l = gs.getRemainingLines();
for (Line l2 : l) {
    GameState g = gs.clone();
    g.addLine(l2);
    if (evaluate(g) > minscore) {
        minscore = (evaluate(g));
        lnew = l2;
    }
}

int minscore = -1;
GameState g = gs.clone();
Line lnew = null;
List<Line> l = gs.getRemainingLines();
for (Line l2 : l) {
    g.addLine(l2);
    if (evaluate(g) > minscore) {
        minscore = (evaluate(g));
        lnew = l2;
    }
    g.removeLine(l2);
}

但是,如果您尝试使用minimax(http://en.wikipedia.org/wiki/Minimax),则需要更改代码以递归调用makeMove(除非您修改算法以使用循环结构确定min-max)。

public GameState makeMove(GameState gs) {
   if (gs.getRemainingLines().size() == 1) {
       GameState g = gs.clone();
       g.addLine(gs.getRemainingLines().get(0));
       return g;
   }

   if (gs.getPlayer() == 1) {
       GameState g = gs.clone();
       g.setPlayer(2);
       int bestValue = -1;
       Line lbest = null;
       List<Line> lines = gs.getRemainingLines();
       for (Line line : lines) {
           g.addLine(line);
           GameState val = makeMove(g);
           g.removeLine(line);
           if (evaluate(val) > bestValue) {
               bestValue = evaluate(g);
               lbest = line;
           }
       }
       g.addLine(lbest);
       return g;
   } else {
       GameState g = gs.clone();
       g.setPlayer(1);
       int bestValue = 999;
       Line lbest = null;
       List<Line> lines = gs.getRemainingLines();
       for (Line line : lines) {
           g.addLine(line);
           GameState val = makeMove(g);
           g.removeLine(line);
           if (evaluate(val) < bestValue) {
               bestValue = evaluate(g);
               lbest = line;
           }
       }
       g.addLine(lbest);
       return g;
   }

}