protected override double CalculateValue(SimulationState state, int pieceIndex, PieceDefinition piece) { return(_calculator.CalculateValueOfPurchasing(state, pieceIndex, piece)); }
//https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning#Pseudocode private int AlphaBeta(SimulationState parentState) { var maximizingPlayer = parentState.ActivePlayer; int alpha = int.MinValue; int beta = int.MaxValue; int bestMove = -1; int bestValue = Int32.MinValue; //0-2 + -1 for advance var possibleMoves = new FixedArray4Int(); var possibleMoveValues = new FixedArray4Double(); int possibleMovesAmount = 0; //Insertion sort the possible moves in to possibleMoves(Weights) //Buying stuff for (var i = 0; i < 3; i++) { var piece = Helpers.GetNextPiece(parentState, i); if (Helpers.ActivePlayerCanPurchasePiece(parentState, piece)) { var value = _calculator.CalculateValueOfPurchasing(parentState, parentState.NextPieceIndex + i, piece); InsertionSort(ref possibleMoves, ref possibleMoveValues, ref possibleMovesAmount, i, value); } } //Advance { var value = _calculator.CalculateValueOfAdvancing(parentState); InsertionSort(ref possibleMoves, ref possibleMoveValues, ref possibleMovesAmount, -1, value); } //foreach child var state = _thisThreadPool.Get(); for (var m = 0; m < possibleMovesAmount; m++) { var move = possibleMoves[m]; if (beta <= alpha && move != -1) { continue; } parentState.CloneTo(state); if (_placementMaker == null) { state.Fidelity = SimulationFidelity.NoPiecePlacing; } int v; if (move == -1) { state.PerformAdvanceMove(); if (_placementMaker != null) { while (state.PieceToPlace != null) { _placementMaker.PlacePiece(state); } } //Decrease alpha by 1 (if possible) so we can identify draws. We favor doing an advance in a draw, but we evaluate purchases first //This gives us the same results as if we were evaluating advance first, but performance is better //This makes us stronger than favoring buying pieces in a draw (Which probably implies our Evaluate method is incorrect?), but costs slight runtime performance v = AlphaBeta(state, _maxSearchDepth - 1, (alpha == int.MinValue ? int.MinValue : alpha - 1), beta, maximizingPlayer); if (v >= bestValue) { bestMove = move; } } else { state.PerformPurchasePiece(state.NextPieceIndex + move); if (_placementMaker != null) { while (state.PieceToPlace != null) { _placementMaker.PlacePiece(state); } } v = AlphaBeta(state, _maxSearchDepth - 1, alpha, beta, maximizingPlayer); if (v > bestValue) { bestMove = move; } } //Console.WriteLine($"Considering {m.ToString().PadLeft(2)} = {v}"); bestValue = Math.Max(bestValue, v); alpha = Math.Max(alpha, bestValue); } _thisThreadPool.Return(state); return(bestMove); }