/* * 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()); }
/* 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)); }
//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); }
//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); }
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)); }