def minimax(self, board: str, count: int = 0) -> (int, int, int): """ Does a minimax search. :param board: :param count: The length of the game. A longer count is better. :return: (val, move, count): the best minimax val for current player with longest count. The move to achieve that. """ mark = whoseMove(board) # possMoves are [(val, move, count)] (val in [1, 0, -1]) for move in self.validMoves(board)] # These are the possible moves considering a full minimax analysis. # The recursive call to minimax is made in self.makeAndEvaluateMove(board, move, mark, count+1) possMoves = [ self.makeAndEvaluateMove(board, move, mark, count + 1) for move in validMoves(board) ] minOrMax = max if mark == XMARK else min (bestVal, _, _) = minOrMax(possMoves, key=lambda possMove: possMove[0]) bestMoves = [(val, move, count) for (val, move, count) in possMoves if val == bestVal] (_, _, longestBestMoveCount) = max(bestMoves, key=lambda possMove: possMove[2]) # Get all moves with best val and with longest count longestBestMoves = [(val, move, count) for (val, move, count) in bestMoves if count == longestBestMoveCount] return choice(longestBestMoves)
def minimax(self, board: str): """ Does a minimax search. :param board: :return: (move, (val, game_length)): the move to achieve the best val with the longest game for current player. """ """ Your Code Here """ # ----------------------------BEGIN CODE---------------------------- # disclaimer: I did only minimax (as said on the homework description https://drive.google.com/file/d/1JXBi_5JB8fwTWX34j0ZwN5YwjoIexh-Z/view) # my minimax does NOT try to prolong the game. It always goes for the best move! # initializing default values moveToMake = validMoves(board)[0] bestValue = -100 # this will find the best move to go to, since value only returns the best score # util's setMove creates a copy, so we can use that as the successor for validmove in validMoves(board): currentValue = self.value( setMove(board, validmove, whoseMove(board)), 0) if currentValue > bestValue: bestValue = currentValue moveToMake = validmove # currently the val and game_length tuple is arbitrary because i have not implemented depth! return (moveToMake)
def minvalue(self, board: str, score): # set v to positive infinity v = 9999 # min of value(successor) for validmove in validMoves(board): v = min( v, self.value(setMove(board, validmove, whoseMove(board)), score)) return v
def maxvalue(self, board: str, score): # set v to negative infinity v = -9999 # max of value(successor) for validmove in validMoves(board): v = max( v, self.value(setMove(board, validmove, whoseMove(board)), score)) return v
def value(self, board: str, score): # terminal state is when there is a winner or a tie # ----------terminal states------------- # maximizer gets 100 for winning if theWinner(board) == XMARK: score += 100 return score elif theWinner(board) == OMARK: score -= 100 return score # check for a tie if len(validMoves(board)) == 0: return score # ------terminal states end, begin recursion------- # if next agent is the maximizer, get the maxvalue if whoseMove(board) == XMARK: return self.maxvalue(board, score) # if next agent is minimizer, return min value if whoseMove(board) == OMARK: return self.minvalue(board, score)
def step(self, board: str, move: int) -> (Optional[PlayerDict], str): """ Make the move and return (winnerDict, updatedBoard). If no winner, winnerDict will be None. :param board: :param move: :return: (winnerDict, updatedBoard) """ currentPlayerDict: PlayerDict = self.XDict if whoseMove( board) is XMARK else self.ODict otherPlayerDict: PlayerDict = self.otherDict(currentPlayerDict) # The following are all game-ending cases. if not isAvailable(board, move): # Illegal move. currentPlayerDict loses. # Illegal moves should be blocked and should not occur. currentPlayerDict['cachedReward'] = -100 otherPlayerDict['cachedReward'] = 100 print(f'\n\nInvalid move by {currentPlayerDict["mark"]}: {move}.', end='') return (otherPlayerDict, board) updatedBoard = setMove(board, move, currentPlayerDict['mark']) if theWinner(updatedBoard): # The current player just won the game with its current move. currentPlayerDict['cachedReward'] = 100 otherPlayerDict['cachedReward'] = -100 return (currentPlayerDict, updatedBoard) if emptyCellsCount(updatedBoard) == 0: # The game is over. It's a tie. currentPlayerDict['cachedReward'] = 0 otherPlayerDict['cachedReward'] = 0 return (None, updatedBoard) # The game is not over. # Get a reward for extending the game. currentPlayerDict['cachedReward'] = 1 return (None, updatedBoard)