private int[] minmaxVal(Board b, int d, float alpha, float beta, ref Stopwatch timer) { if (d == 0 || b.gameOver()) { return(new int[] { 0, evaluate(b) }); } int[] bestMove = { 0, int.MinValue }; if (b.whoseMove() == us) { int bestVal = int.MinValue; for (int move = 0; move <= 13; move++) { if (b.legalMove(move) && timer.ElapsedMilliseconds < (max_time - 200)) { Board b1 = new Board(b); b1.makeMove(move, false); int[] val = minmaxVal(b1, d - 1, alpha, beta, ref timer); if (val[1] > bestVal) { bestVal = val[1]; bestMove[0] = move; bestMove[1] = bestVal; } alpha = Math.Max(alpha, bestMove[1]); if (beta <= alpha) { break; } } } } else { int bestVal = int.MaxValue; bestMove = new int[] { 0, bestVal }; for (int move = 0; move <= 13; move++) { if (b.legalMove(move) && timer.ElapsedMilliseconds < (max_time - 200)) { Board b1 = new Board(b); b1.makeMove(move, false); int[] val = minmaxVal(b1, d - 1, alpha, beta, ref timer); if (val[1] < bestVal) { bestVal = val[1]; bestMove[0] = move; bestMove[1] = bestVal; } beta = Math.Min(beta, bestMove[1]); if (beta <= alpha) { break; } } } } return(bestMove); }
public override int evaluate(Board b) { int score = b.stonesAt(last + 1) - b.stonesAt(p2last + 1); int stones = 0; for (int i = first; i <= last; i++) { stones += b.stonesAt(i); } int totalStones = stones; for (int i = p2first; i <= p2last; i++) { totalStones += b.stonesAt(i); } int stonePercent = 0; if (totalStones != 0) { stonePercent = stones / totalStones; } int finalScore = score * 100 + stonePercent * 2; if (b.gameOver()) { finalScore += 10000; } return(finalScore); }
/* This is the minimax function with alpha beta pruning * * Inputs: board, the current state; depth, the desired search depth; * a, the alpha value; b, the beta value; finalDepth, the total search depth * Output: Tuple<int,int> Result: Item1 = The heuristic of the move. Item2 = the move number */ private Tuple <int, int> alphaBeta(Board board, int depth, int a, int b, int finalDepth) { int v = 0; int v2 = 0; int move = -1; if (board.gameOver() || depth == 0) { return(new Tuple <int, int>(0, evaluate(board, finalDepth))); } if (board.whoseMove() == Position.Top) // Max player's turn { v = -99999; for (int i = 12; i >= 7; i--) // Try each of the possible moves { if (board.legalMove(i) && (move == -1 || timer.ElapsedMilliseconds < getTimePerMove() - 1)) { Board board1 = new Board(board); board1.makeMove(i, false); v2 = alphaBeta(board1, depth - 1, a, b, finalDepth).Item2; // Find value of that move if (v2 > v) // pick the best move { v = v2; move = i; } a = Math.Max(a, v); if (b <= a) { break; } } } return(new Tuple <int, int>(move, v)); // Return the result } else // Minimizing player's turn { v = 99999; for (int i = 5; i >= 0; i--) // Try each of the possible moves { if (board.legalMove(i) && (move == -1 || timer.ElapsedMilliseconds < getTimePerMove() - 1)) { Board board1 = new Board(board); board1.makeMove(i, false); v2 = alphaBeta(board1, depth - 1, a, b, finalDepth).Item2; // Find value of that move if (v2 < v) // Pick the best move { v = v2; move = i; } b = Math.Min(b, v); if (b <= a) { break; } } } return(new Tuple <int, int>(move, v)); // Return the result } }
/* * minimax function * based on the psuedocode from Prof Plantiga's lecture slides */ private Result minimax(Board b, int depth, int alpha, int beta) { int bestNum = 0; int currentBestMove = 0; if (b.gameOver() || depth == 0) // game is over or depth is 0 { return(new Result(0, evaluate(b))); } if (b.whoseMove() == Position.Top) // searching moves for when top player { bestNum = Int32.MinValue; for (int move = 7; move <= 12; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); b1.makeMove(move, false); Result num = minimax(b1, depth - 1, alpha, beta); if (num.getBestScore() > bestNum) { bestNum = num.getBestScore(); currentBestMove = move; } if (bestNum > alpha) { alpha = bestNum; } } } return(new Result(currentBestMove, bestNum)); } else // search moves for when bottom player { bestNum = int.MaxValue; for (int move = 0; move <= 5; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); b1.makeMove(move, false); Result num = minimax(b1, (depth - 1), alpha, beta); if (num.getBestScore() < bestNum) { bestNum = num.getBestScore(); currentBestMove = move; } if (bestNum < beta) { beta = bestNum; } } } return(new Result(currentBestMove, bestNum)); } }
public override int chooseMove(Board b) { int tL = 4; Timer timer = new Timer(tL); timeOut = false; timer.Elapsed += timeOutTrigger; timer.Enabled = true; timer.AutoReset = false; timer.Start(); List <int> choices = new List <int>(); int choice = 0; int startDepth = 11; //Pass in timer to minimax? //If the elapsed time matches or exceeds the time limit, break the loop while (true) { if (timeOut || b.gameOver()) { break; } Console.WriteLine("Depth: " + startDepth); if (startDepth > 20) { Console.WriteLine("We might have a problem"); } //If the value of choice is not 0, add it to the list of choices. moveResult choiceResult = new moveResult(0, 0); if (!timeOut) { choiceResult = miniMax(b, startDepth, tL, 1, timer); } if (choiceResult.getScore() != 0 && b.legalMove(choiceResult.getMove())) { //TODO: Adding stuff to a list seems to not work choices.Add(choiceResult.getMove()); } //choice = miniMax(b, startDepth, tL).getScore(); //TODO: Modify timer and Stop, store and print time elapsed //update depth. startDepth += 1; if (timeOut == true) { timer.Stop(); } } //Close the timer and return the move from the last item in the list of choices timer.Close(); return(choices.Last()); }
/* Minimax search * top player is max and bottom is min * returns a resulting move from recursive calculations */ private Result minimax(Board board, int depth, int alpha, int beta) { if (board.gameOver() || depth == 0) { return(new Result(0, evaluate(board))); } int bestVal = int.MinValue; int bestMove = 0; if (board.whoseMove() == Position.Top) { for (int move = 7; move < 13 && alpha < beta; move++) { if (board.legalMove(move)) { Board b1 = new Board(board); b1.makeMove(move, false); Result val = minimax(b1, depth - 1, alpha, beta); if (val.getBestScore() > bestVal) { bestVal = val.getBestScore(); bestMove = move; } if (bestVal > alpha) { alpha = bestVal; } } } } else { bestVal = Int32.MaxValue; for (int move = 0; move < 6 && alpha < beta; move++) { if (board.legalMove(move)) { Board b1 = new Board(board); b1.makeMove(move, false); Result val = minimax(b1, depth - 1, alpha, beta); if (val.getBestScore() < bestVal) { bestVal = val.getBestScore(); bestMove = move; } if (bestVal < beta) { beta = bestVal; } } } } return(new Result(bestMove, bestVal)); }
/* minimax() * @param: b, a Board type && d, and int type * returns new Result w/ bestMove && bestValue * this code follows the pseudocode given in class */ private Result minimaxVal(Board b, int d, int alpha, int beta) //d is depth { int bestValue = 0; int bestMove = 0; if (b.gameOver() || d == 0) //if the game is over or depth is 0 { return(new Result(0, evaluate(b))); } if (b.whoseMove() == Position.Top) { //if TOP is max bestValue = Int32.MinValue; for (int move = 7; move <= 12 && alpha < beta; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); //duplicate board b1.makeMove(move, false); Result val = minimaxVal(b1, d - 1, alpha, beta); if (val.GetScore() > bestValue) { bestValue = val.GetScore(); bestMove = move; } if (bestValue > alpha) { alpha = bestValue; } } } return(new Result(bestMove, bestValue)); } else { bestValue = int.MaxValue; for (int move = 0; move <= 5 && alpha < beta; move++) //bottom row { if (b.legalMove(move)) { Board b1 = new Board(b); b1.makeMove(move, false); Result val = minimaxVal(b1, d - 1, alpha, beta); if (val.GetScore() < bestValue) { bestValue = val.GetScore(); bestMove = move; } if (bestValue < beta) { beta = bestValue; } } } } return(new Result(bestMove, bestValue)); }
/* miniMaxVal implements the minimax search of a board for a given depth * @param: board, the Board whose child boards will be evaluated * @param: depth, how many generations will be analyzed * @return: a Result that is the optimal move and its value */ private Result miniMaxVal(Board board, int depth) { int bestMove = -1; // dummy value int bestVal = 0; // dummy value if (board.gameOver() || (depth == 0)) { return(new Result(0, evaluate(board))); } if (board.whoseMove() == Position.Top) // max { bestVal = -9999999; // impossible to get less than this for (int move = 7; move <= 12; move++) { if (board.legalMove(move) && !timeUp) // stop if the timer has finished { Board boardCopy = new Board(board); boardCopy.makeMove(move, false); Result moveResult = miniMaxVal(boardCopy, depth - 1); if (moveResult.value > bestVal) { bestVal = moveResult.value; bestMove = move; } } } } else // BOTTOM'S MOVE (min) { bestVal = 9999999; // impossible to get more than this for (int move = 0; move <= 5; move++) { if (board.legalMove(move) && !timeUp) // stop if the timer has finished { Board boardCopy = new Board(board); boardCopy.makeMove(move, false); Result moveResult = miniMaxVal(boardCopy, depth - 1); if (moveResult.value < bestVal) { bestVal = moveResult.value; bestMove = move; } } } } return(new Result(bestMove, bestVal)); }
/// <summary> /// MiniMax algorithm for mankalah. Uses the moveResult class, probably doesn't need to and /// could use it much more efficiently, but for times sake... /// This method finds returns the best move based on its rating. /// </summary> /// <param name="b"></param> which mankalah board /// <param name="d"></param> depth of which algotithm will recurse /// <returns></returns> private moveResult miniMax(Board b, int d) { moveResult moveVal = new moveResult(0, 0); //uses result class at bottom moveResult bestMoveVal = new moveResult(0, 0); // ^^ if (b.gameOver() || d == 0) { return(new moveResult(0, evaluate(b))); //evaluate function is simple. Bad thing? } if (b.whoseMove() == Position.Bottom) { bestMoveVal.val = 999; for (int move = 0; move <= 5; move++) //for all possible moves, { if (b.legalMove(move) && myWatch.ElapsedMilliseconds < timePerMove) /*and time not expired*/ { Board b1 = new Board(b); //dup board b b1.makeMove(move, false); //make move moveVal = miniMax(b1, d - 1); //recurse and return value if (moveVal.val < bestMoveVal.val) { bestMoveVal = moveVal; bestMoveVal.move = move; } //overwrite if better move } } return(bestMoveVal); //return best move found. } else //similar comments as above... { bestMoveVal.val = -999; for (int move = 7; move <= 12; move++) { if (b.legalMove(move) && myWatch.ElapsedMilliseconds < timePerMove) /*and time not expired*/ { Board b1 = new Board(b); //dup board b b1.makeMove(move, false); moveVal = miniMax(b1, d - 1); if (moveVal.val > bestMoveVal.val) { bestMoveVal = moveVal; bestMoveVal.move = move; } } } return(bestMoveVal); } }
/* * Evaluate function to evaluate the nodes of my tree. I added a variable to keep track of how many turns players got. * if each player got equal number of turns then the number is 0, if top got more is <0 and opposite for bottom * Note: my Max is for the bottom because I could visualize it better that way. * */ public int evaluate(Board b, int playsinarow) { int score = b.stonesAt(6) - b.stonesAt(13); int stones = 0; int distributedstones = 0; for (int i = 0; i < 6; i++) { stones = stones + (b.stonesAt(i) - b.stonesAt(12 - i));//who has controll of more stones } if (b.gameOver()) { score += stones; //if game is over add the extra stones to the scores } int goagains = 0; //figure out how many go agains may be possible at the begininng of the next turn if (b.whoseMove() == Position.Top) { for (int i = 12; i >= 7; i--) { // try first go-again if (b.stonesAt(i) == 13 - i) { goagains--; } distributedstones = i - 13 + b.stonesAt(i); //find the distribution of the stones } } else { for (int i = 5; i >= 0; i--) { if (b.stonesAt(i) == 6 - i) { goagains++; } distributedstones = 6 - i - b.stonesAt(i); } } //note that plays and go agains are raised to the power of three to conserve signs and help promote sprees. return(40 * score + 2 * Convert.ToInt32(Math.Pow((playsinarow + goagains), 3)) + 30 * distributedstones);//weights were found be tweeking to find the most defensive and offensive bot. }
private Result minimaxVal(Board b, int d, Stopwatch w, int alpha, int beta) { if (w.ElapsedMilliseconds > getTimePerMove()) { throw new MoveTimedOutException(); } int bestMove = 0; int bestVal; bool gameCompleted = false; if (b.gameOver() || d == 0) { return(new Result(0, evaluate(b), b.gameOver())); } if (b.whoseMove() == Position.Top) { bestVal = Int32.MinValue; for (int move = 7; move <= 12; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); b1.makeMove(move, false); Result val = minimaxVal(b1, d - 1, w, alpha, beta); if (val.getScore() > bestVal) { bestVal = val.getScore(); bestMove = move; gameCompleted = val.isEndGame(); } if (bestVal > alpha) { alpha = bestVal; } } } } else { bestVal = Int32.MaxValue; for (int move = 0; move <= 5; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); b1.makeMove(move, false); Result val = minimaxVal(b1, d - 1, w, alpha, beta); if (val.getScore() < bestVal) { bestVal = val.getScore(); bestMove = move; gameCompleted = val.isEndGame(); } if (bestVal < beta) { beta = bestVal; } } } } return(new Result(bestMove, bestVal, gameCompleted)); }
public Result miniMax(Board b, int d, int alpha, int beta) { if (cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(); } int bestMove = first; int bestVal = 0; Result res; Position opp; if (b.gameOver() || d == 0) { return(new Result(0, evaluate(b))); } if (b.whoseMove() == p) { //top is max bestVal = -int.MaxValue; for (int move = first; move <= last; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); //duplicate board b1.makeMove(move, false); //make the move res = miniMax(b1, d - 1, alpha, beta); //find its value alpha = Math.Max(alpha, res.val); if (res.val > bestVal) { bestVal = res.val; bestMove = move; } if (alpha >= beta) { break; } } } } else { // similarly for bottom’s move bestVal = int.MaxValue; for (int move = p2first; move <= p2last; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); //duplicate board b1.makeMove(move, false); //make the move res = miniMax(b1, d - 1, alpha, beta); //find its value beta = Math.Min(beta, res.val); if (res.val < bestVal) //remember if best { bestVal = res.val; bestMove = move; } if (alpha >= beta) { break; } } } } return(new Result(bestMove, bestVal)); }
//Minimax function. Note that the Max is for the bottom play, It just made more sense to me that way. returns best score and best move. private int[] prunedminimax(Board b, int depth, Stopwatch timer, int lastbestscore, Position lastplayer, int playsinarow) { int[] value = new int[2]; int bestscore; if (b.whoseMove() == Position.Top) //set the infinity max or infinity min depending on whose turn it is. { bestscore = 1111111; } else { bestscore = -1111111; } int bestmove = -1; //returns this number if DFS doesnt finish int[] tempvalue = new int[2]; //temporary value that will be used to find the best score and value if ((depth > 0 && !b.gameOver())) //as long as game isnt over, or you reached the depth { bool firstturn = true; //used to keep track of the first turn for (int i = 0; i < 6; i++) { if (b.whoseMove() == Position.Top) { if (b.legalMove(i + 7)) { Board currentBoard = new Board(b); currentBoard.makeMove(i + 7, false); if (lastplayer == Position.Top)//this is used to keep track of who has had the most turns { tempvalue = prunedminimax(currentBoard, depth - 1, timer, bestscore, Position.Top, playsinarow - 1); } else { tempvalue = prunedminimax(currentBoard, depth - 1, timer, bestscore, Position.Top, playsinarow); } if (firstturn) { bestscore = tempvalue[0]; bestmove = i + 7; firstturn = false;//no longer first turn so set to false } //for non-first turns, check to see if tempvalue is lower then best score. else if (tempvalue[0] < bestscore) { bestscore = tempvalue[0]; bestmove = i + 7; } if ((lastplayer == Position.Bottom) && (bestscore < lastbestscore)) { break; //prunes the branches if necesary. } } } else { if (b.legalMove(i)) { Board currentBoard = new Board(b); currentBoard.makeMove(i, false); if (lastplayer == Position.Bottom) { tempvalue = prunedminimax(currentBoard, depth - 1, timer, bestscore, Position.Bottom, playsinarow + 1); } else { tempvalue = prunedminimax(currentBoard, depth - 1, timer, bestscore, Position.Bottom, playsinarow); } if (firstturn) { bestscore = tempvalue[0]; bestmove = i; firstturn = false; } else if (tempvalue[0] > bestscore) { bestscore = tempvalue[0]; bestmove = i; } if ((lastplayer == Position.Top) && (bestscore > lastbestscore)) { break; } } } if (!(timer.ElapsedMilliseconds < getTimePerMove() - 10))//if there are only 10 ms till the turn is up, return -1 for the best move { value[1] = -1; return(value); } } } else //reaches this if it is a node. { bestscore = evaluate(b, playsinarow); } value[0] = bestscore; value[1] = bestmove; return(value); }
public int[] minmaxVal(Board b, int depth, float alpha, float beta, ref Stopwatch sw) { if (depth == 0) { return new int[] { 0, evaluate(b) } } ; if (b.gameOver()) { maxDepth = depth; return(new int[] { 0, endGameEval(b) }); } int[] bestMove = { 0, int.MinValue }; //our turn -> maximize if (b.whoseMove() == us) { bestMove = new int[] { 0, int.MinValue }; for (int i = 0; i < 13; i++) { if (b.legalMove(i)) { if (sw.ElapsedMilliseconds >= timeLimit - 100) { //return new int[] { 0, int.MinValue }; return new int[] { i, evaluate(b) } } ; Board modified = SimulateBoard(b, i); int[] newMove = minmaxVal(modified, depth - 1, alpha, beta, ref sw); if (newMove[1] > bestMove[1]) { bestMove[0] = i; bestMove[1] = newMove[1]; //Console.WriteLine($"Changed bestMove in max to move {i} with value {bestMove[1]}"); } alpha = Math.Max(alpha, bestMove[1]); if (beta <= alpha) { break; } } } } //Their turn -> minimize else { bestMove = new int[] { 0, int.MaxValue }; for (int i = 0; i < 13; i++) { if (b.legalMove(i)) { if (sw.ElapsedMilliseconds >= timeLimit - 100) { //return new int[] { 0, int.MaxValue }; return new int[] { i, evaluate(b) } } ; Board modified = SimulateBoard(b, i); int[] newMove = minmaxVal(modified, depth - 1, alpha, beta, ref sw); if (newMove[1] < bestMove[1]) { bestMove[0] = i; bestMove[1] = newMove[1]; //Console.WriteLine($"Changed bestMove in min to move {i} with value {bestMove[1]}"); } beta = Math.Min(beta, bestMove[1]); if (beta <= alpha) { break; } } } } if (maxDepth != 50) { maxDepth = depth; } return(bestMove); }
/* minimaxVal with Alpha-Beta Pruning * returns evaluation if it reaches maxDepth, else calculates min or max if it is opponent's turn or my turn respectively * */ public DataWrapper minimaxVal(Board b, int d, int alpha, int beta) { //when time runs out, this evaluates to true //when the cancelled execption is thrown, the last best move will be returned in the chooseMove() try->catch statement if (cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(); } //return if reached gameover or max depth if (b.gameOver() || d == 0) { return(new DataWrapper(0, evaluate(b), 0, 0)); } DataWrapper data; //look through right positions for moves int start = 0; int end = 5; if (b.whoseMove() == Position.Top) { start = 7; end = 12; } //initialize variables int bestMove = -1; int secondBestMove = -1; int bestValue = int.MinValue; int secondBestValue = int.MinValue; //loop through all possible moves for (int move = start; move <= end; move++) { //if it is a legal move, check the value of it if (b.legalMove(move)) { //create copy of board, make move, and evaluate it by recursing Board newBoard = new Board(b); newBoard.makeMove(move, false); data = minimaxVal(newBoard, d - 1, alpha, beta); //if value, move pair is best or worst, set bestValue and bestMove to it //also set second best value, move pair to previous value if (isBetterMove(b.whoseMove(), bestValue, data.getValue(), d)) { secondBestValue = bestValue; bestValue = data.getValue(); secondBestMove = bestMove; bestMove = move; //alpha beta pruning if (b.whoseMove() == this.position) { alpha = Math.Max(alpha, bestValue); } else { beta = Math.Min(beta, bestValue); } } //if alpha is greater than or equal to beta, we can skip other moves (pruning part of alpha beta pruning) if (alpha >= beta) { break; } } } //return move pairs return(new DataWrapper(bestMove, bestValue, secondBestMove, secondBestValue)); }
/* * Play one Kalah game with the two given players, with firstPlayer * starting. This function returns TOP's score. */ public static int playGame(Player pTop, Player pBot, Position firstPlayer) { b = new Board(firstPlayer); if (firstPlayer == Position.Top) { Console.WriteLine("Player " + pTop.getName() + " starts."); } else { Console.WriteLine("Player " + pBot.getName() + " starts."); } b.display(); while (!b.gameOver()) { Console.WriteLine(); if (b.whoseMove() == Position.Top) { move = pTop.chooseMove(b); Console.WriteLine(pTop.getName() + " chooses move " + move); } else { move = pBot.chooseMove(b); Console.WriteLine(pBot.getName() + " chooses move " + move); } b.makeMove(move, true); // last parameter says to be chatty b.display(); if (b.gameOver()) { if (b.winner() == Position.Top) { Console.WriteLine("Player " + pTop.getName() + " (TOP) wins " + b.scoreTop() + " to " + b.scoreBot()); } else if (b.winner() == Position.Bottom) { Console.WriteLine("Player " + pBot.getName() + " (BOTTOM) wins " + b.scoreBot() + " to " + b.scoreTop()); } else { Console.WriteLine("A tie!"); } } else if (b.whoseMove() == Position.Top) { Console.WriteLine(pTop.getName() + " to move."); } else { Console.WriteLine(pBot.getName() + " to move."); } } return(b.scoreTop()); }
// The function minimax() calculates the best possible move that Chappie can make by // recursing as far as it can within the time limit and keeping track of what the best move and // score are as it recurses. It is also implemented with AB pruning, meaning that if it // starts to go down a path where the outcome will inevitable be worse that our best values, // it prunes off that path, therefore saving time to go down a better path. private MoveResult minimax(Board b, int d, Stopwatch w, int alpha, int beta) { // check to see if the time limit is up if (w.ElapsedMilliseconds > getTimePerMove()) { throw new MoveTimedOutException(); } // base case if (b.gameOver() || d == 0) { return(new MoveResult(0, evaluate(b), b.gameOver())); } // initialization of trackers int bestMove = 0; int bestVal; bool gameCompleted = false; // check all the the moves that top could make, and act as if it is the MAX in minimax if (b.whoseMove() == Position.Top) // TOP is MAX { // smallest possible value so that it can only get better bestVal = Int32.MinValue; for (int move = 7; move <= 12 && alpha < beta; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); // duplicate board b1.makeMove(move, false); // make the move MoveResult val = minimax(b1, d - 1, w, alpha, beta); // find its value if (val.getScore() > bestVal) // remember if best { bestVal = val.getScore(); bestMove = move; // track the current condition of the game gameCompleted = val.isEndGame(); } // prune if (bestVal > alpha) { alpha = bestVal; } } } } // check all the the moves that bottom could make, and act as if it is the MIN in minimax else // BOTTOM is MIN { // lergest possible value so that it can only get better bestVal = Int32.MaxValue; for (int move = 0; move <= 5 && alpha < beta; move++) { if (b.legalMove(move)) { Board b1 = new Board(b); // duplicate board b1.makeMove(move, false); // make the move MoveResult val = minimax(b1, d - 1, w, alpha, beta); // find its value if (val.getScore() < bestVal) // remember if best { bestVal = val.getScore(); bestMove = move; // track the current condition of the game gameCompleted = val.isEndGame(); } // prune if (bestVal < beta) { beta = bestVal; } } } } return(new MoveResult(bestMove, bestVal, gameCompleted)); }
public int[] minmaxVal(Board b, int d, float alpha, float beta, ref Stopwatch sw) //d is depth { if (depth == 0 || b.gameOver()) { return new int[] { 0, evaluate(b) } } ; int[] bestMove = { 0, int.MinValue }; //our turn -> maximize if (b.whoseMove() == us) { bestMove = new int[] { 0, int.MinValue }; for (int i = 0; i < 13; i++) { if (b.legalMove(i)) { if (sw.ElapsedMilliseconds >= timeLimit - 500) { return new int[] { 0, int.MinValue } } ; Board modified = SimulateBoard(b, i); int[] newMove = minmaxVal(modified, depth - 1, alpha, beta, ref sw); if (newMove[1] > bestMove[1]) { bestMove[0] = i; bestMove[1] = newMove[1]; } alpha = Math.Max(alpha, bestMove[1]); if (beta <= alpha) { break; } } } } //Their turn -> minimize else { bestMove = new int[] { 0, int.MaxValue }; for (int i = 0; i < 13; i++) { if (!b.legalMove(i)) { if (sw.ElapsedMilliseconds >= timeLimit - 500) { return new int[] { 0, int.MaxValue } } ; Board modified = SimulateBoard(b, i); int[] newMove = minmaxVal(modified, depth - 1, alpha, beta, ref sw); if (newMove[1] > bestMove[1]) { bestMove[0] = i; bestMove[1] = newMove; } beta = Math.Min(beta, bestMove[1]); if (beta <= alpha) { break; } } } } return(bestMove); } }
//int d is depth of miniMax search. int max should be 1 for getting the max result, -1 for min. Should return moveResult object public moveResult miniMax(Board b, int d, double timeLimit, int max, Timer timer) { //Set up variables int bestVal = int.MinValue; int bestMove = 0; moveResult bestMR = new moveResult(bestMove, bestVal); //Base case: depth is 0, time is up or game is over. Returns a moveResult with the best score value and 0 move value if (d == 0 || timeOut || b.gameOver()) { bestVal = evaluate(b); //Console.WriteLine("Best score found to be " + bestVal); moveResult bestMoveResult = new moveResult(bestMove, bestVal); return(bestMoveResult); } if (b.whoseMove() == Position.Top) { for (int move = 7; move <= 12; move++) { //Console.WriteLine(""); //Recurse if the move is legal, time hasn't expired and game isn't over int val = 0; if (b.legalMove(move) && !timeOut && !b.gameOver()) { Board b1 = new Board(b); b1.makeMove(move, true); int newDepth = d - 1; //Switch the value of max to be the opposite for the recursion int mnOrMx = max * -1; //Console.WriteLine("Recursing from " + d + " to " + newDepth + "..."); val = miniMax(b1, newDepth, timeLimit, mnOrMx, timer).getScore(); //If at "max" level, prioritize and return maximum value if (max == 1) { //If there's a new max, update bestValue and bestMove if (val > bestVal) { bestVal = val; bestMove = move; } } //If at "min" level, prioritize and return minimum value if (max == -1) { //If there's a new min, update bestValue and bestMove if (val < bestVal || val != int.MinValue) { bestVal = val; bestMove = move; } } } } bestMR = new moveResult(bestMove, bestVal); return(bestMR); } else { for (int move = 0; move <= 5; move++) { //Console.WriteLine(""); //Recurse if the move is legal, time hasn't expired and game isn't over if (b.legalMove(move) && !timeOut && !b.gameOver()) { Board b1 = new Board(b); b1.makeMove(move, true); int newDepth = d - 1; //Switch the value of max to be the opposite for the recursion int mnOrMx = max * -1; // Console.WriteLine("Recursing from " + d + " to " + newDepth + "..."); int val = miniMax(b1, newDepth, timeLimit, mnOrMx, timer).getScore(); // Console.WriteLine("New Value: " + val); //If at "max" level, prioritize and return maximum value if (max == 1) { //If there's a new max, update bestValue and bestMove if (val > bestVal) { bestVal = val; bestMove = move; } } //If at "min" level, prioritize and return minimum value if (max == -1) { //If there's a new min, update bestValue and bestMove if (val < bestVal || val != int.MinValue) { bestVal = val; bestMove = move; } } } } bestMR = new moveResult(bestMove, bestVal); return(bestMR); } //Console.WriteLine("Best move: " + bestMove + " Best score: " + bestVal); bestMR = new moveResult(bestMove, bestVal); return(bestMR); }
// b = current board state, d = depth, w = clock, alpha and beta to keep track of max/min values for pruning private Result minimax(Board b, int d, Stopwatch w, int alpha, int beta) { // throw exception if time limit has reached if (w.ElapsedMilliseconds > getTimePerMove()) { throw new MoveTimedOutException(); } // initialize variables int bestMove = 0; int bestVal; bool gameCompleted = false; if (b.gameOver() || d == 0) { return(new Result(0, evaluate(b), b.gameOver())); } // if it's my move, maximize if (b.whoseMove() == mypos) // TOP is MAX { bestVal = Int32.MinValue; // set bestVal to lowest possible number to update later on for (int move = myfirst; move <= mylast; move++) // loop through all possible moves for this player { if (b.legalMove(move)) // if it's a legal move, make that move, look at board and see if it is best move { Board b1 = new Board(b); // duplicate board b1.makeMove(move, false); // make the move Result val = minimax(b1, d - 1, w, alpha, beta); // find its value if (val.getScore() > bestVal) // remember if best maximum { bestVal = val.getScore(); // update best move, value, and game state after this move bestMove = move; gameCompleted = val.isEndGame(); } // update alpha if (bestVal > alpha) { alpha = bestVal; } } } } // else minimize else { bestVal = Int32.MaxValue; // set bestVal to highest possible number to update later on for (int move = myfirstOP; move <= mylastOP; move++) // loop through all possible moves for this player { if (b.legalMove(move)) // if it's a legal move, make that move, look at board and see if it is best move { Board b1 = new Board(b); // duplicate board b1.makeMove(move, false); // make the move Result val = minimax(b1, d - 1, w, alpha, beta); // find its value if (val.getScore() < bestVal) // remember if best minimum { bestVal = val.getScore(); // update best move, value, and game state after this move bestMove = move; gameCompleted = val.isEndGame(); } // update beta if (bestVal < beta) { beta = bestVal; } } } } // return the Result set with best move, score, and game status return(new Result(bestMove, bestVal, gameCompleted)); }