对手寻找麻烦
我正在使用敌对搜索技术与AI对手写一篇Connect4游戏,而且我已经有点碰壁了。 我觉得我离解决方案并不遥远,但是也许存在一个问题,那就是我在切换观点(例如:参与者的角度是基于我的评估分数),在某处丢失了一个负号或类似的东西那。
问题是,在我尝试过的AI中,当玩家有三连胜时,AI选择不阻止玩家,否则AI会玩完美的游戏,或者他更喜欢阻止玩家,即使他有机会赢得比赛。 这似乎也很重要,搜索深度是否是偶数或不均匀的数字,因为人工智能在六层搜索时头脑迟钝,这相当明显地说明了某些错误。
搜索
使用的算法与alpha-beta修剪是一样的,具体实现如下:
private int Negamax(int depth, int alpha, int beta, Player player)
{
Player winner;
if (Evaluator.IsLeafNode(game, out winner))
{
return winner == player ? (10000 / depth) : (-10000 / depth);
}
if (depth == Constants.RecursionDepth)
{
return Evaluator.Evaluate(game, depth, player);
}
foreach (var move in moves)
{
int row;
if (board.DoMove(move, player, out row))
{
var value = -Negamax(depth + 1, -beta, -alpha, (Player)1 - (int)player);
board.UndoMove(move, row, player);
if (value > alpha)
{
alpha = value;
if (player == Player.AI)
{
bestColumn = move;
}
}
if (alpha >= beta)
{
return alpha;
}
}
}
return alpha;
}
我不怀疑问题出在这个功能上,但可能是这样。
评估
我已经将评估功能基于这样一个事实,即在7x6板上只有69种可能的方式可以获得四连胜。 我有一个约350个项目的查找表,其中包含每个列和行的硬编码信息,其中row +列是其组成部分。 例如,对于第0行和第0列,表格如下所示:
//c1r1
table[0][0] = new int[3];
table[0][0][0] = 21;
table[0][0][1] = 27;
table[0][0][2] = 61;
这意味着第0列第0行是赢得组合21,27和61的一部分。
我有第二个表格,这个表格包含了两个玩家在每个胜利组合中有多少宝石。 当我采取行动时,我会做以下工作:
public bool DoMove(int column, Player p, out int row)
{
row = moves[column];
if (row >= 0)
{
Cells[column + row * Constants.Columns] = p;
moves[column]--;
var combinations = this.Game.PlayerCombinations[p];
foreach (int i in TerminalPositionsTable.Get(column,row))
{
combinations[i]++;
}
return true;
}
else
{
return false;
}
}
相反,当然是为UndoMove
完成。
因此,在Player.Human
第0列第0行上移动之后,该表将在索引Player.Human
和61处填充1值。如果我在也是赢得组合的一部分的单元格中再次移动27,那么玩家组合表在索引27处增加到2。
我希望我已经清楚地表明了这一点,因为它在评估功能中用于很快确定玩家在四连胜中的得分有多近。
评估职能,我怀疑问题在于,如下所示:
public static int Evaluate(Game game, int depth, Player player)
{
var combinations = game.PlayerCombinations[player];
int score = 0;
for (int i = 0; i < combinations.Length; i++)
{
switch (combinations[i])
{
case 1:
score += 1;
break;
case 2:
score += 5;
break;
case 3:
score += 15;
break;
}
}
return score;
}
所以我只需循环69个可能的胜利组合,并根据它是一块石头,两个一排还是三个,给分数加分。
我在整个对抗性搜索中仍然感到困惑的部分是我是否应该关心哪个球员正在采取行动? 我的意思是,我应该像在这里一样通过球员,还是应该从AI球员的角度来评估委员会? 我已经尝试了aiScore - humanScore
多种组合,或者只是从Player.AI
的角度来看, Player.AI
。 但我已经走到了死胡同,我尝试过的每一个组合都是相当有缺陷的。
所以:
任何帮助将非常感激。
更新
我已经在下面实施了Brennan的建议,虽然它有了很大的改进,但出于某种原因,它不会阻止任何列上的三行,而是左侧和最右侧的两行,并且只有当搜索深度是不平衡的。 人工智能在搜索深度上是无与伦比的,但只有深度8以上。 然后它拒绝再次阻止。 这很好说明我可能非常接近,但仍然有一些关键的缺陷。
也许这与我设置专栏时AI应该放下一块石头,正如Brennan所评论的那样,但我不知道什么时候才能设置它。 仅在深度0设置它不起作用。
更新2
编辑代码,因为它现在与Brennan的变化一样。
更新3
用完整的代码创建了一个Github回购。 如果你不知道如何使用Git,只需从这里下载一个zip文件即可。
这是一个.NET 4.0项目,运行它将在您的documents / logs目录中创建negamax算法的日志文件。 该解决方案还包含一个测试项目,该测试项目包含针对每个董事会专栏的测试,无论AI是否选择在玩家排在三位时阻止玩家。
这种东西让我的大脑受到伤害,所以我不确定这个答案是否正确,但这里就是这样。
在negamax中,分数总是相对于当前正在移动的玩家进行评估。 如果它是白色的移动,那么高分对白色是好的。 如果它是黑色的移动,那么高分对黑色有好处。 所以如果你有一个叶节点,得分是+ inf还是-inf不是取决于节点是白色还是黑色的胜利,而是取决于你当前评估的玩家的胜利。 替换为:
return winner == Player.AI ? (10000 / depth) : (-10000 / depth);
有了这个:
return winner == player ? (10000 / depth) : (-10000 / depth);
评估功能中存在类似的问题。 替换为:
return player == Player.AI ? score : -score;
有了这个:
return score;
再次,我不确定这是否正确。 但我希望你能尝试这两个改变,并让我知道它是否有效。 我很好奇!
如果它没有阻止某些组合,这听起来像是你的表中有可能获胜的缺陷。
我也在你的评估功能中看到一个问题:它给那些没有获胜希望的举动带来价值。 假设你有xoo.x,你在玩o。 你的例程说在这里玩这个游戏的价值是15点,实际上这个游戏的价值是0.任何一个已经包含来自两个玩家的牌的赢钱模式对任何人都是没有价值的。
我发现在调试这种类型的东西时,调试器没有什么价值,因为它不能让你看清楚整体情况。 尝试写入日志文件,检查每个模式 - 在日志中放入实际的绘图。
链接地址: http://www.djcxy.com/p/1651.html