我正在尝试实施NegaMax来进行跳棋游戏。我现在只是以0的深度测试它,这意味着当前的玩家只是评估他的所有动作而不考虑其他玩家接下来会做什么。它适用于大约一半的游戏(正确计算得分),然后通过它的一部分开始吐出无意义的答案。
例如,怀特可能剩下1件,而黑人则有5件,但是例如,当他们因为失败而将所有人都变为负数时,它会将怀特的动作评为7分。布莱克可能会在接下来的比赛中获胜,但是即使它应该是1000,它也会将获胜的动作评为-4。
我可以理解它一直输出垃圾,但为什么它会在前几个转弯时起作用而然后开始搞乱?
private static Move GetBestMove(Color color, Board board, int depth)
{
var bestMoves = new List<Move>();
IEnumerable<Move> validMoves = board.GetValidMoves(color);
int highestScore = int.MinValue;
Board boardAfterMove;
int tmpScore;
var rand = new Random();
Debug.WriteLine("{0}'s Moves:", color);
foreach (Move move in validMoves)
{
boardAfterMove = board.Clone().ApplyMove(move);
if (move.IsJump && !move.IsCrowned && boardAfterMove.GetJumps(color).Any())
tmpScore = NegaMax(color, boardAfterMove, depth);
else
tmpScore = -NegaMax(Board.Opposite(color), boardAfterMove, depth);
Debug.WriteLine("{0}: {1}", move, tmpScore);
if (tmpScore > highestScore)
{
bestMoves.Clear();
bestMoves.Add(move);
highestScore = tmpScore;
}
else if (tmpScore == highestScore)
{
bestMoves.Add(move);
}
}
return bestMoves[rand.Next(bestMoves.Count)];
}
private static int NegaMax(Color color, Board board, int depth)
{
return BoardScore(color, board);
}
private static int BoardScore(Color color, Board board)
{
if (!board.GetValidMoves(color).Any()) return -1000;
return board.OfType<Checker>().Sum(c => (c.Color == color ? 1 : -1) * (c.Class == Class.Man ? 2 : 3));
}
我已经在6x6主板上隔离了一个不喜欢的主板状态:
. . .
. w B
W . .
. . .
. w .
. . W
w = white, b = black, capital letter = king
看起来这不是一个时间或动作数量问题,它只是不喜欢特定的董事会状态。我不认为这种状态有什么特别之处。
在这种状态下,它将所有4个黑棋的动作评估为-13。如果你看看我是如何得分的,那就说每人2分,每分3分,如果另一名球员拥有则为负。它看起来好像把所有碎片都当作白色......这是获得13的唯一方法。
我找到了另一条线索。在董事会评分方法中,我得到它来打印它所看到的......这就是它告诉我的:
2: White
4: White
6: White
13: White
17: White
董事会方块的编号如下:
00 01 02
03 04 05
06 07 08
09 10 11
12 13 14
15 16 17
我认为这确实是说黑色的部分是白色的......现在要弄清楚造成这种情况的原因。
所以...现在我知道颜色是错误的,但仅限于BoardScore
函数。我的正常显示程序从未接受过,否则我会在几个小时前发现问题。我认为可能在ApplyMove
函数中,颜色会被切换..
public Board ApplyMove(Move m)
{
if (m.IsJump)
{
bool indented = m.Start % Width < _rowWidth;
int offset = indented ? 1 : 0;
int enemy = (m.Start + m.End) / 2 + offset;
this[m.Color, enemy] = Tile.Empty;
}
this[m.Color, m.End] = this[m.Color, m.Start];
this[m.Color, m.Start] = Tile.Empty;
var checker = this[m.Color, m.End] as Checker;
if (m.IsCrowned) checker.Class = Class.King;
return this;
}
但这也没有多大意义......这件作品只是从开始位置复制到结束位置。需要调查m.Color
是什么......也许它会提供更多线索!我觉得自己像个侦探。
答案 0 :(得分:2)
根据您的描述,我发现自己怀疑这件颜色数据。如果它以某种方式设置为错误,它会将所有内容评估为负面。
我发现自己对BoardScore功能不太满意 - 像这样的复杂公式擅长隐藏错误并且难以调试。
我会添加一个函数Checker.Value(Color)来简化BoardScore,让您更容易查看发生的事情。
您没有显示Color的数据类型,如果它允许的值超过黑白,则会导致您正在观察的行为。
鉴于您的最新更新,我会查看boardAfterMove并确保它正确生成。
再次编辑:那里有两个电话 - 它是否正确克隆?
答案 1 :(得分:0)
发现问题。
foreach (char ch in checkers)
{
switch (ch)
{
case 'w':
Add(new Checker(Color.White, Class.Man));
break;
case 'W':
Add(new Checker(Color.White, Class.King));
break;
case 'b':
Add(new Checker(Color.Black, Class.Man));
break;
case 'B':
Add(new Checker(Color.White, Class.King));
break;
default:
Add(Tile.Empty);
break;
}
}
只有Black Kings才会发生。愚蠢的克隆!!为什么深度复制不容易?