// Gets the move from minimax, then makes the move, returns the board at every stage of the move public List <Board> MakeMove(Board board, BackgroundWorker worker, ref int CountSinceLastTake) { CalculatedMove Move = NegaMax(DepthOfSearch, IsWhite ? 1 : -1, float.MinValue, float.MaxValue, board, worker); if (Move.MoveFrom.CouldBeTakeMove(Move.Moveset.First())) { CountSinceLastTake = 0; } else { CountSinceLastTake++; } // if the search has failed, this should never run if (!Move.MoveFrom.InBoard()) { return(new List <Board>()); } else { // Generate and return boards List <Board> Boardstates = new List <Board>(); Piece MovingPiece = board.GetPiece(Move.MoveFrom); foreach (Position move in Move.Moveset.Moves) { board.MovePeice(MovingPiece.CurrentPosition, move); Boardstates.Add(board.MakeNewCopyOf()); } return(Boardstates); } }
// Searches Depth moves ahead in the game and finds the best move private CalculatedMove NegaMax(int Depth, int PlayerColour, float alpha, float beta, Board board, BackgroundWorker worker) { // Setup float BestValue = float.MinValue; bool FoundTakeMove = false; bool AppliedTakeMove = false; List <MoveSet> possibleMovesets; List <Position> usablepieces = new List <Position>(); if (PlayerColour == 1) { usablepieces = board.GetWhitePositions(); } else { usablepieces = board.GetBlackPositions(); } //Show the tree (to the console) if (Debug && Depth > 0) { Console.WriteLine(); for (int p = 0; p < (DepthOfSearch - Depth); p++) { Console.Write("\t"); } if ((DepthOfSearch % 2 == Depth % 2 && IsWhite) || (DepthOfSearch % 2 != Depth % 2 && !IsWhite)) { Console.Write("> MAX | "); } else { Console.Write("> MIN | "); } Console.Write("Depth: " + Depth.ToString()); Console.WriteLine(); } if (Debug) { ShowBoard(board, Depth + 1); } // detect if we should stop if (Depth == 0 || usablepieces.Count == 0 || board.WhiteHasWon() || board.BlackHasWon()) { return(new CalculatedMove(PlayerColour * board.EvaluateBoard(), new Position(-1, -1), new MoveSet())); } // If we can carry on searching, setup BestPiece and BestMoveset with first valid move we come accross Position BestPiecePosition = new Position(-1, -1); MoveSet BestMoveset = new MoveSet(); foreach (Position p in usablepieces) { BestPiecePosition = p; if (board.GetPiece(p).GetMoves(board).Count > 0) { BestMoveset = board.GetPiece(p).GetMoves(board).First(); break; } } // Counts the number of pieces we have searched int piececount = 0; foreach (Position pieceposition in usablepieces) { if (Debug) { Console.WriteLine(); for (int p = -1; p < (DepthOfSearch - Depth); p++) { Console.Write("\t"); } Console.WriteLine("NEW PIECE at " + pieceposition.ToString()); } // generate all possible take movesets possibleMovesets = board.GetPiece(pieceposition).GetTakeMovesOnly(board); if (possibleMovesets.Count > 0) { if (!FoundTakeMove && !AppliedTakeMove) { // if this is the first piece that has take movesets, mark it so that every move is better than what we had. FoundTakeMove = true; } } else if (!FoundTakeMove) { possibleMovesets = board.GetPiece(pieceposition).GetMoves(board); } if (Debug) { for (int p = 0; p < (DepthOfSearch - Depth); p++) { Console.Write("\t"); } Console.WriteLine("Take Moves found: " + FoundTakeMove); } bool BreakOuterLoop = false; foreach (MoveSet moveset in possibleMovesets) { // Make test board Board testboard = board.MakeNewCopyOf(); // Make move Position oldpos = pieceposition; foreach (Position move in moveset.Moves) { testboard.MovePeice(oldpos, move); oldpos = move; } // Run the search CalculatedMove Move = NegaMax(Depth - 1, -PlayerColour, -beta, -alpha, testboard, worker); // Change the best result if we need to if (BestValue < Move.Value || (FoundTakeMove && !AppliedTakeMove)) { if (FoundTakeMove && !AppliedTakeMove) { AppliedTakeMove = true; } BestMoveset = moveset; BestPiecePosition = pieceposition; if (Debug && Move.Moveset.NumberOfStages() > 0) { for (int p = 0; p < (DepthOfSearch - Depth); p++) { Console.Write("\t"); } Console.WriteLine("New best move from " + Move.MoveFrom.ToString() + " to " + Move.Moveset.Last().ToString() + " with score of: " + Move.Value.ToString() + " breating previous of: " + BestValue.ToString()); } BestValue = Move.Value; } alpha = Math.Max(alpha, BestValue); if (Debug) { for (int p = -1; p < (DepthOfSearch - Depth); p++) { Console.Write("\t"); } Console.WriteLine(" | Current Board Score: " + (PlayerColour * testboard.EvaluateBoard()).ToString() + " | Best Value: " + BestValue.ToString()); } if (alpha >= beta && UsePruning) { if (Debug) { for (int p = 0; p < (DepthOfSearch - Depth); p++) { Console.Write("\t"); } Console.WriteLine(alpha.ToString() + " " + beta.ToString() + "Broke"); } BreakOuterLoop = true; break; } } // If its the first call (highest depth) and we have finished a piece, report back to the worker our progress if (Depth == DepthOfSearch) { worker.ReportProgress((int)(100f * piececount) / usablepieces.Count); } piececount++; if (BreakOuterLoop) { break; } } return(new CalculatedMove(BestValue, BestPiecePosition, BestMoveset)); }