public bool Equals(BotMove other) => Figure == other.Figure && Sequence == other.Sequence;
// NegaMax algorithm with alpha/beta, source: https://en.wikipedia.org/wiki/Negamax //TODO: append transposition tables private BotMove Negamax(SquareBoard board, int depth, int alpha, int beta, Side side, CancellationToken branchToken) { if (cancellation.IsCancellationRequested || !CanSearchDeeper(board, depth)) { return(BotMove.Empty(Estimate(board))); } var states = GetStates(board, side); if (depth == options.MaxDepth && states.Count == 1) { var singleState = states[0]; return(new BotMove(singleState.Figure, singleState.MoveSequence, 0)); } BotMove bestMove = new BotMove(Int32.MinValue); var noMoves = true; var cts = new CancellationTokenSource(); var workers = new List <Task>(); foreach (var state in states) { noMoves = false; if (cts.IsCancellationRequested) { break; } if (branchToken.IsCancellationRequested) { cts.Cancel(); break; } if (states.Count > 1 && _runningWorkersCount < options.DegreeOfParallelism && depth <= options.MaxDepth - 1) { Interlocked.Increment(ref _runningWorkersCount); workers.Add(Task.Run(() => { DoNegamax(state); Interlocked.Decrement(ref _runningWorkersCount); })); } else { DoNegamax(state); } } Task.WaitAll(workers.ToArray()); if (noMoves) { return(BotMove.Empty(Estimate(board))); } return(bestMove); void DoNegamax(State state) { if (options.IsDebug) { Log("before", board, side, state, bestMove.Score, depth, alpha, beta); } var boardAfterMove = new MoveCommandChain(state.Figure, state.Board, state.MoveSequence).Execute(); var score = -Negamax(boardAfterMove, depth - 1, -beta, -alpha, SideUtil.Opposite(side), cts.Token).Score; var shouldPrun = false; lock (locker) { if (score > bestMove.Score) { bestMove = new BotMove(state.Figure, state.MoveSequence, score); } alpha = Math.Max(alpha, bestMove.Score); //let's move cts.Cancel out of the lock scope shouldPrun = options.AllowPrunning && alpha >= beta; } if (options.IsDebug) { Log("after", board, side, state, score, depth, alpha, beta); } if (shouldPrun) { cts.Cancel(); } } }