Beispiel #1
0
    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))
Beispiel #2
0
 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
Beispiel #3
0
 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}')
Beispiel #4
0
 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
Beispiel #6
0
 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)
Beispiel #7
0
 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')
Beispiel #9
0
 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)