我正在尝试实现一个使用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。
答案 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;
}
}