// read a board state and select a suitable move to return public string SelectMove(string fen) { var Board = new Board(fen); // load the board // store the enemy's last move if (LastBoard != null) { var LastMove = BoardDiff(LastBoard, Board); if (LastMove == null) { throw new Exception("could not determine last move using a board diff"); } if (PastMoves.Count == 8) { PastMoves.Dequeue(); } PastMoves.Enqueue(LastMove); } // assign engine's past moves to the board and search for the best move Board.PastMoves = PastMoves; MoveGen.MoveBoardPair Mbp = Searcher.Search(Board, Depth, QuiescentDepth, Alpha, Beta, MoveTimeLimit); LastBoard = Mbp.Board; return(Mbp.Move.ToString()); }
// perform a minimax search from the current board to try and select the best move public MoveGen.MoveBoardPair Search(Board root, int depth, int qDepth, int alpha, int beta, TimeSpan time) { // guards if (depth < 1 || qDepth < 1) { throw new ArgumentException("valid depth limits are greater than zero"); } if (alpha > beta) { throw new ArgumentException("alpha cannot be greater than beta"); } // start countdown Countdown.Interval = time.TotalMilliseconds; Countdown.Start(); Node RootNode = new Node(root); Node.TopLevelActivePlayerWhite = root.ActiveColorWhite; // store for utility eval MoveGen.MoveBoardPair Result = null; int Value = int.MinValue; int NextDepth; for (int d = 1; d <= depth; d++) // step through depths { NextDepth = d - 1; foreach (Node child in RootNode.Children) // step through children { if (Result == null) { Result = new MoveGen.MoveBoardPair(child.Board, child.LastMove); // default to first move } if (Timeout) { return(Result); // return best move if timed out } Value = MinV(child, NextDepth, qDepth, alpha, beta); // get minimized value from new board if (Value > alpha) { alpha = Value; Result = new MoveGen.MoveBoardPair(child.Board, child.LastMove); } // update alpha and best move if better val found } } return(Result); }