最小 - 最大算法

时间:2012-07-31 08:35:28

标签: c# multithreading chess minmax

我为国际象棋游戏编写了单线程最小 - 最大算法,工作正常。现在我正在尝试重写它以使用所有可用的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值代码中。我已经把头发撕了一个多星期了,非常感谢任何帮助。

由于

1 个答案:

答案 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(广度优先搜索),这在此类移动计算任务中更有效。