def otherMove(board: str, emptyCells: int) -> Set[int]: """ Special case moves. :param board: :param emptyCells: number of empty cells :return: Selected move """ availableCorners = {pos for pos in CORNERS if isAvailable(board, pos)} if emptyCells == 9: return availableCorners if emptyCells == 8: return {CENTER} if isAvailable(board, CENTER) else availableCorners # The following is for X's second move. It applies only if X's first move was to a corner. if emptyCells == 7 and board.index(XMARK) in CORNERS: oFirstMove = board.index(OMARK) # If O's first move is a side cell, X should take the center. # Otherwise, X should take the corner opposite its first move. if oFirstMove in SIDES: return {CENTER} if oFirstMove == CENTER: opCorner = oppositeCorner(board.index(XMARK)) return {opCorner} return availableCorners # If this is O's second move and X has diagonal corners, O should take a side move. # If X has two adjacent corners, O blocked (above). So, if there are 2 available corners # they are diagonal. if emptyCells == 6 and len(availableCorners) == 2: return {pos for pos in SIDES if isAvailable(board, pos)} # If none of the special cases apply, take the center if available, # otherwise a corner, otherwise any valid move. return ({CENTER} if isAvailable(board, CENTER) else availableCorners if availableCorners else validMoves(board))
def _makeAMove(self, board: str) -> int: c = '?' while c not in LABELLEDBOARD or not isAvailable(board, int(c)): print() if c in LABELLEDBOARD and not isAvailable(board, int(c)): print(f'Cell {c} is taken.') if c != '?' and c not in LABELLEDBOARD: print(f'Invalid move: "{c}".') print(formatBoard(board)) # Keep only last character. c = input(f'{self.myMark} to move > ') c = c[-1] if len(c) > 0 else '?' move = int(c) return move
def printQValuesForPattern(qBoard: str, qValuesDicts: Dict[str, Dict[int, float]]) -> NoReturn: print(f'\n{formatBoard(qBoard)}') print(f'{whoseMove(qBoard)} to move') for (typeName, qValueDict) in sorted(qValuesDicts.items()): availableQValues = {i: val for (i, val) in qValueDict.items() if isAvailable(qBoard, i)} bestQMoves = argmaxList(availableQValues) print(f'{f"{typeName}" + ": ":<25}{roundDict(availableQValues)}. Best moves: {bestQMoves}')
def _makeAMove(self, prev_move, board: str) -> int: c = '?' while c not in LABELLEDBOARD or not isAvailable(board, int(c)): print() if c in LABELLEDBOARD and not isAvailable(board, int(c)): print(f'Cell {c} is taken.') if c != '?' and c not in LABELLEDBOARD: print(f'Invalid move: "{c}".') if prev_move is not None: print(f'{otherMark(self.myMark)} -> {prev_move}') print(formatBoard(board)) c = input(f'{self.myMark} to move > ') # Keep only the last character typed by user. c = c[-1] if len(c) > 0 else '?' move = int(c) return move
def getBestQMovesFromQBoard(self, qBoard: str, typeName: str) -> List[int]: qValues = self.qTable[qBoard][typeName] availableQValues = { i: val for (i, val) in qValues.items() if isAvailable(qBoard, i) } bestQMoves = argmaxList(availableQValues) return bestQMoves
def findEmptyCell(board: str, threeInRow: Tuple[int, int, int]) -> int: """ Return a choice of the EMPTYCELL positions in threeInRow. (There is guaranteed to be one.) :param board: :param threeInRow: :return: """ emptyCells = [ index for index in threeInRow if isAvailable(board, index) ] return choice(emptyCells)
def _makeAMove(self, board: str) -> int: (myWins, otherWins, myForks, otherForks) = self.winsBlocksForks(board) availCorners = [pos for pos in CORNERS if isAvailable(board, pos)] availCenter = [pos for pos in [CENTER] if isAvailable(board, pos)] availSides = [pos for pos in SIDES if isAvailable(board, pos)] myOppositeCorners = [ pos for pos in CORNERS if isAvailable(board, pos) and board[oppositeCorner(pos)] == self.myMark ] move = choice( [self.findEmptyCell(board, myWin) for myWin in myWins] if myWins else [self.findEmptyCell(board, otherWin) for otherWin in otherWins] if otherWins else myForks if myForks else # If emptyCellsCount(board) == 6, I'm playing 'O'. If a diagonal is XOX, don't take corner. availSides if board[CENTER] == self.myMark and emptyCellsCount(board) == 6 else otherForks if otherForks else availCorners if availCorners and self.myMark == 'X' and len(availCorners) % 2 == 0 else availCenter if availCenter else myOppositeCorners if board[CENTER] == self.opMark and myOppositeCorners else availCorners if availCorners else validMoves(board)) return move
def fieldcheck(field, value, token): """ Checks that fields are unique during the registration process. This function takes in a field and the value. All requests are validated with a temporary session token. The token must be unique for each request and cannot be reused within a 60 second cycle. """ if validateTemporarySession(token): if request.method == 'GET': return jsonify(available=isAvailable(field=field, value=value), success=True) else: return jsonify(success=False, statusCode=401) else: return jsonify(success=False, statusCode=401, cause='session auth expired')
def findForks(board: str, singletons: List[Tuple[int, int, int]]) -> List[int]: """ Finds moves that create forks :param board: :param singletons: A list of triples containing one non-empty cell for a given player. :return: """ countSingletons = len(singletons) forkCells = { pos for idx1 in range(countSingletons - 1) for idx2 in range(idx1 + 1, countSingletons) for pos in singletons[idx1] if isAvailable(board, pos) and pos in singletons[idx2] } return list(forkCells)
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)