我为国际象棋游戏编写了单线程最小 - 最大算法,工作正常。现在我正在尝试重写它以使用所有可用的cpu核心,但我无法让它正常工作。
我的想法是生成尽可能多的线程,因为系统上有核心(在我的情况下为4),并让线程添加和删除队列中的工作项。这些工作项中的每一项都是一个“CalculateState”,它在棋盘上移动x次后保存有关棋盘的信息。
当工作项目在maxDepth产生时,它将评估棋盘并“返回”其值。返回是通过在检查的移动树中向上推进其值(以模拟递归)来完成的。
算法开始:
private readonly ConcurrentPriorityQueue<int, CalculateState> _calculateStates = new ConcurrentPriorityQueue<int, CalculateState>();
private Thread[] _threads = new Thread[Environment.ProcessorCount];
private const int MaxDepth = 3;
private PlayerColor _maxPlayer;
public Move CalculateMoveMultithreaded(ChessBoard board)
{
_maxPlayer = board.TurnToMove;
var parentState = new CalculateState(null, null, 0, null, int.MaxValue, int.MinValue, board.TurnToMove);
foreach (var move in board.GetPossibleMoves())
{
move.MakeMove(board);
var newState = ChessStateTransforms.TransformChessBoardToState(board);
move.UnMakeMove(board);
_calculateStates.Enqueue(MaxDepth, new CalculateState(move, newState, 1, parentState, int.MaxValue, int.MinValue, Player.OppositeColor(board.TurnToMove)));
}
for (var i = 0; i < _threads.Length; i++)
{
var calculationThread = new Thread(DoWork);
_threads[i] = calculationThread;
calculationThread.Start();
}
foreach (var thread in _threads)
{
thread.Join();
}
return parentState.MoveToMake;
}
线程执行:
private void DoWork()
{
while (true)
{
KeyValuePair<int, CalculateState> queueItem;
if (!_calculateStates.TryDequeue(out queueItem))
break;
var calculateState = queueItem.Value;
var board = ChessStateTransforms.TransformChessStateIntoChessBoard(calculateState.ChessState);
if (calculateState.Depth == MaxDepth)
{
var boardValue = board.ValueOfBoard(_maxPlayer);
calculateState.PropergateValue(boardValue);
continue;
}
foreach (var move in board.GetPossibleMoves())
{
move.MakeMove(board);
var newState = ChessStateTransforms.TransformChessBoardToState(board);
move.UnMakeMove(board);
_calculateStates.Enqueue(MaxDepth - calculateState.Depth, new CalculateState(calculateState.MoveToMake, newState, calculateState.Depth + 1, calculateState, calculateState.MinValue, calculateState.MaxValue, Player.OppositeColor(board.TurnToMove)));
}
}
}
工作项上下文。
private class CalculateState
{
public readonly PlayerColor Turn;
public int MaxValue;
public int MinValue;
public readonly int Depth;
public readonly ChessState ChessState;
public Move MoveToMake;
private readonly CalculateState _parentState;
public CalculateState(Move moveToMake, ChessState chessState, int depth, CalculateState parentState, int minValue, int maxValue, PlayerColor turn)
{
Depth = depth;
_parentState = parentState;
MoveToMake = moveToMake;
ChessState = chessState;
MaxValue = maxValue;
Turn = turn;
MinValue = minValue;
}
public void PropergateValue(int value, Move firstMove = null)
{
lock (this)
{
if (Turn == _maxPlayer)
{
if (value > MaxValue)
{
MaxValue = value;
if (Depth == 0)
{
MoveToMake = firstMove;
return;
}
_parentState.PropergateValue(MaxValue, MoveToMake);
}
}
else
{
if (value < MinValue)
{
MinValue = value;
if (Depth == 0)
{
MoveToMake = firstMove;
return;
}
_parentState.PropergateValue(MinValue, MoveToMake);
}
}
}
}
}
因为它是算法将返回带有敌人碎片的动作,但根本不保护它自己。 我相信棋盘,移动,valueofboard等中的代码是正确的。问题必须在多线程/ propegate值代码中。我已经把头发撕了一个多星期了,非常感谢任何帮助。
由于
答案 0 :(得分:1)
很抱歉没有给出你所问的确切答案(实际上你的问题不明确,根据你所提供的内容进行调查非常困难),但我建议更好地实施 alpha-你的min-max中的beta修剪。它可能会帮助您超过数百个CPU核心。您想了解相关内容,请参阅http://www.cs.utah.edu/~hal/courses/2009S_AI/Walkthrough/AlphaBeta/和http://cs.ucla.edu/~rosen/161/notes/alphabeta.html
PS:关于你的问题,很难实现多线程递归(有效地使用所有线程而不是仅在顶层拆分移动树)。我几乎可以肯定你在那里做了一个bug。我建议你使用计算(扩展)所需的额外状态队列。每个线程都应从队列中获取项目并计算它将clild节点添加到树中。因此,您的算法将不再是DFS,但会转换为BFS(广度优先搜索),这在此类移动计算任务中更有效。