`1# Reversi1# Reversi223import random3import random4import sys4import sys55 6# NEW SAVE FEATURE CODE: 7import shelve 86def drawBoard(board):9def drawBoard(board):7    # This function prints out the board that it was passed. Returns None.10    # This function prints out the board that it was passed. Returns None.8    HLINE = '  +---+---+---+---+---+---+---+---+'11    HLINE = '  +---+---+---+---+---+---+---+---+'9    VLINE = '  |   |   |   |   |   |   |   |   |'12    VLINE = '  |   |   |   |   |   |   |   |   |'101311    print('    1   2   3   4   5   6   7   8')14    print('    1   2   3   4   5   6   7   8')12    print(HLINE)15    print(HLINE)13    for y in range(8):16    for y in range(8):14        print(VLINE)17        print(VLINE)15        print(y+1, end=' ')18        print(y+1, end=' ')16        for x in range(8):19        for x in range(8):17            print('| %s' % (board[x][y]), end=' ')20            print('| %s' % (board[x][y]), end=' ')18        print('|')21        print('|')19        print(VLINE)22        print(VLINE)20        print(HLINE)23        print(HLINE)2124222523def resetBoard(board):26def resetBoard(board):24    # Blanks out the board it is passed, except for the original starting position.27    # Blanks out the board it is passed, except for the original starting position.25    for x in range(8):28    for x in range(8):26        for y in range(8):29        for y in range(8):27            board[x][y] = ' '30            board[x][y] = ' '283129    # Starting pieces:32    # Starting pieces:30    board[3][3] = 'X'33    board[3][3] = 'X'31    board[3][4] = 'O'34    board[3][4] = 'O'32    board[4][3] = 'O'35    board[4][3] = 'O'33    board[4][4] = 'X'36    board[4][4] = 'X'3437353836def getNewBoard():39def getNewBoard():37    # Creates a brand new, blank board data structure.40    # Creates a brand new, blank board data structure.38    board = []41    board = []39    for i in range(8):42    for i in range(8):40        board.append([' '] * 8)43        board.append([' '] * 8)414442    return board45    return board4346444745def isValidMove(board, tile, xstart, ystart):48def isValidMove(board, tile, xstart, ystart):46    # Returns False if the player's move on space xstart, ystart is invalid.49    # Returns False if the player's move on space xstart, ystart is invalid.47    # If it is a valid move, returns a list of spaces that would become the player's if they made a move here.50    # If it is a valid move, returns a list of spaces that would become the player's if they made a move here.48    if board[xstart][ystart] != ' ' or not isOnBoard(xstart, ystart):51    if board[xstart][ystart] != ' ' or not isOnBoard(xstart, ystart):49        return False52        return False505351    board[xstart][ystart] = tile # temporarily set the tile on the board.54    board[xstart][ystart] = tile # temporarily set the tile on the board.525553    if tile == 'X':56    if tile == 'X':54        otherTile = 'O'57        otherTile = 'O'55    else:58    else:56        otherTile = 'X'59        otherTile = 'X'576058    tilesToFlip = []61    tilesToFlip = []59    for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:62    for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:60        x, y = xstart, ystart63        x, y = xstart, ystart61        x += xdirection # first step in the direction64        x += xdirection # first step in the direction62        y += ydirection # first step in the direction65        y += ydirection # first step in the direction63        if isOnBoard(x, y) and board[x][y] == otherTile:66        if isOnBoard(x, y) and board[x][y] == otherTile:64            # There is a piece belonging to the other player next to our piece.67            # There is a piece belonging to the other player next to our piece.65            x += xdirection68            x += xdirection66            y += ydirection69            y += ydirection67            if not isOnBoard(x, y):70            if not isOnBoard(x, y):68                continue71                continue69            while board[x][y] == otherTile:72            while board[x][y] == otherTile:70                x += xdirection73                x += xdirection71                y += ydirection74                y += ydirection72                if not isOnBoard(x, y): # break out of while loop, then continue in for loop75                if not isOnBoard(x, y): # break out of while loop, then continue in for loop73                    break76                    break74            if not isOnBoard(x, y):77            if not isOnBoard(x, y):75                continue78                continue76            if board[x][y] == tile:79            if board[x][y] == tile:77                # There are pieces to flip over. Go in the reverse direction until we reach the original space, noting all the tiles along the way.80                # There are pieces to flip over. Go in the reverse direction until we reach the original space, noting all the tiles along the way.78                while True:81                while True:79                    x -= xdirection82                    x -= xdirection80                    y -= ydirection83                    y -= ydirection81                    if x == xstart and y == ystart:84                    if x == xstart and y == ystart:82                        break85                        break83                    tilesToFlip.append([x, y])86                    tilesToFlip.append([x, y])848785    board[xstart][ystart] = ' ' # restore the empty space88    board[xstart][ystart] = ' ' # restore the empty space86    if len(tilesToFlip) == 0: # If no tiles were flipped, this is not a valid move.89    if len(tilesToFlip) == 0: # If no tiles were flipped, this is not a valid move.87        return False90        return False88    return tilesToFlip91    return tilesToFlip8992909391def isOnBoard(x, y):94def isOnBoard(x, y):92    # Returns True if the coordinates are located on the board.95    # Returns True if the coordinates are located on the board.93    return x >= 0 and x <= 7 and y >= 0 and y <=796    return x >= 0 and x <= 7 and y >= 0 and y <=79497959896def getBoardWithValidMoves(board, tile):99def getBoardWithValidMoves(board, tile):97    # Returns a new board with . marking the valid moves the given player can make.100    # Returns a new board with . marking the valid moves the given player can make.98    dupeBoard = getBoardCopy(board)101    dupeBoard = getBoardCopy(board)99102100    for x, y in getValidMoves(dupeBoard, tile):103    for x, y in getValidMoves(dupeBoard, tile):101        dupeBoard[x][y] = '.'104        dupeBoard[x][y] = '.'102    return dupeBoard105    return dupeBoard103106104107105def getValidMoves(board, tile):108def getValidMoves(board, tile):106    # Returns a list of [x,y] lists of valid moves for the given player on the given board.109    # Returns a list of [x,y] lists of valid moves for the given player on the given board.107    validMoves = []110    validMoves = []108111109    for x in range(8):112    for x in range(8):110        for y in range(8):113        for y in range(8):111            if isValidMove(board, tile, x, y) != False:114            if isValidMove(board, tile, x, y) != False:112                validMoves.append([x, y])115                validMoves.append([x, y])113    return validMoves116    return validMoves114117115118116def getScoreOfBoard(board):119def getScoreOfBoard(board):117    # Determine the score by counting the tiles. Returns a dictionary with keys 'X' and 'O'.120    # Determine the score by counting the tiles. Returns a dictionary with keys 'X' and 'O'.118    xscore = 0121    xscore = 0119    oscore = 0122    oscore = 0120    for x in range(8):123    for x in range(8):121        for y in range(8):124        for y in range(8):122            if board[x][y] == 'X':125            if board[x][y] == 'X':123                xscore += 1126                xscore += 1124            if board[x][y] == 'O':127            if board[x][y] == 'O':125                oscore += 1128                oscore += 1126    return {'X':xscore, 'O':oscore}129    return {'X':xscore, 'O':oscore}127130128131129def enterPlayerTile():132def enterPlayerTile():130    # Let's the player type which tile they want to be.133    # Let's the player type which tile they want to be.131    # Returns a list with the player's tile as the first item, and the computer's tile as the second.134    # Returns a list with the player's tile as the first item, and the computer's tile as the second.132    tile = ''135    tile = ''133    while not (tile == 'X' or tile == 'O'):136    while not (tile == 'X' or tile == 'O'):134        print('Do you want to be X or O?')137        print('Do you want to be X or O?')135        tile = input().upper()138        tile = input().upper()136139137    # the first element in the tuple is the player's tile, the second is the computer's tile.140    # the first element in the tuple is the player's tile, the second is the computer's tile.138    if tile == 'X':141    if tile == 'X':139        return ['X', 'O']142        return ['X', 'O']140    else:143    else:141        return ['O', 'X']144        return ['O', 'X']142145143146144def whoGoesFirst():147def whoGoesFirst():145    # Randomly choose the player who goes first.148    # Randomly choose the player who goes first.146    if random.randint(0, 1) == 0:149    if random.randint(0, 1) == 0:147        return 'computer'150        return 'computer'148    else:151    else:149        return 'player'152        return 'player'150153151154152def playAgain():155def playAgain():153    # This function returns True if the player wants to play again, otherwise it returns False.156    # This function returns True if the player wants to play again, otherwise it returns False.154    print('Do you want to play again? (yes or no)')157    print('Do you want to play again? (yes or no)')155    return input().lower().startswith('y')158    return input().lower().startswith('y')156159157160158def makeMove(board, tile, xstart, ystart):161def makeMove(board, tile, xstart, ystart):159    # Place the tile on the board at xstart, ystart, and flip any of the opponent's pieces.162    # Place the tile on the board at xstart, ystart, and flip any of the opponent's pieces.160    # Returns False if this is an invalid move, True if it is valid.163    # Returns False if this is an invalid move, True if it is valid.161    tilesToFlip = isValidMove(board, tile, xstart, ystart)164    tilesToFlip = isValidMove(board, tile, xstart, ystart)162165163    if tilesToFlip == False:166    if tilesToFlip == False:164        return False167        return False165168166    board[xstart][ystart] = tile169    board[xstart][ystart] = tile167    for x, y in tilesToFlip:170    for x, y in tilesToFlip:168        board[x][y] = tile171        board[x][y] = tile169    return True172    return True170173171174172def getBoardCopy(board):175def getBoardCopy(board):173    # Make a duplicate of the board list and return the duplicate.176    # Make a duplicate of the board list and return the duplicate.174    dupeBoard = getNewBoard()177    dupeBoard = getNewBoard()175178176    for x in range(8):179    for x in range(8):177        for y in range(8):180        for y in range(8):178            dupeBoard[x][y] = board[x][y]181            dupeBoard[x][y] = board[x][y]179182180    return dupeBoard183    return dupeBoard181184182185183def isOnCorner(x, y):186def isOnCorner(x, y):184    # Returns True if the position is in one of the four corners.187    # Returns True if the position is in one of the four corners.185    return (x == 0 and y == 0) or (x == 7 and y == 0) or (x == 0 and y == 7) or (x == 7 and y == 7)188    return (x == 0 and y == 0) or (x == 7 and y == 0) or (x == 0 and y == 7) or (x == 7 and y == 7)186189187190188def getPlayerMove(board, playerTile):191def getPlayerMove(board, playerTile):189    # Let the player type in their move.192    # Let the player type in their move.190    # Returns the move as [x, y] (or returns the strings 'hints' or 'quit')193    # Returns the move as [x, y] (or returns the strings 'hints' or 'quit')191    DIGITS1TO8 = '1 2 3 4 5 6 7 8'.split()194    DIGITS1TO8 = '1 2 3 4 5 6 7 8'.split()192    while True:195    while True:193    print('Enter your move, or type quit to end the game, or hints to turn off/on hints.') 196 197        # NEW SAVE FEATURE CODE: 198        print('Enter your move, or type quit to end the game, or hints to turn off/on hints, or save to save the current game and load to load the previously saved game.')194        move = input().lower()199        move = input().lower() 200        if move == 'save': 201            return 'save' 202        if move == 'load': 203            return 'load' 204195        if move == 'quit':205        if move == 'quit':196            return 'quit'206            return 'quit'197        if move == 'hints':207        if move == 'hints':198            return 'hints'208            return 'hints'199209200        if len(move) == 2 and move[0] in DIGITS1TO8 and move[1] in DIGITS1TO8:210        if len(move) == 2 and move[0] in DIGITS1TO8 and move[1] in DIGITS1TO8:201            x = int(move[0]) - 1211            x = int(move[0]) - 1202            y = int(move[1]) - 1212            y = int(move[1]) - 1203            if isValidMove(board, playerTile, x, y) == False:213            if isValidMove(board, playerTile, x, y) == False:204                continue214                continue205            else:215            else:206                break216                break207        else:217        else:208            print('That is not a valid move. Type the x digit (1-8), then the y digit (1-8).')218            print('That is not a valid move. Type the x digit (1-8), then the y digit (1-8).')209            print('For example, 81 will be the top-right corner.')219            print('For example, 81 will be the top-right corner.')210220211    return [x, y]221    return [x, y]212222213223214def getComputerMove(board, computerTile):224def getComputerMove(board, computerTile):215    # Given a board and the computer's tile, determine where to225    # Given a board and the computer's tile, determine where to216    # move and return that move as a [x, y] list.226    # move and return that move as a [x, y] list.217    possibleMoves = getValidMoves(board, computerTile)227    possibleMoves = getValidMoves(board, computerTile)218228219    # randomize the order of the possible moves229    # randomize the order of the possible moves220    random.shuffle(possibleMoves)230    random.shuffle(possibleMoves)221231222    # always go for a corner if available.232    # always go for a corner if available.223    for x, y in possibleMoves:233    for x, y in possibleMoves:224        if isOnCorner(x, y):234        if isOnCorner(x, y):225            return [x, y]235            return [x, y]226236227    # Go through all the possible moves and remember the best scoring move237    # Go through all the possible moves and remember the best scoring move228    bestScore = -1238    bestScore = -1229    for x, y in possibleMoves:239    for x, y in possibleMoves:230        dupeBoard = getBoardCopy(board)240        dupeBoard = getBoardCopy(board)231        makeMove(dupeBoard, computerTile, x, y)241        makeMove(dupeBoard, computerTile, x, y)232        score = getScoreOfBoard(dupeBoard)[computerTile]242        score = getScoreOfBoard(dupeBoard)[computerTile]233        if score > bestScore:243        if score > bestScore:234            bestMove = [x, y]244            bestMove = [x, y]235            bestScore = score245            bestScore = score236    return bestMove246    return bestMove237247238248239def showPoints(playerTile, computerTile):249def showPoints(playerTile, computerTile):240    # Prints out the current score.250    # Prints out the current score.241    scores = getScoreOfBoard(mainBoard)251    scores = getScoreOfBoard(mainBoard)242    print('You have %s points. The computer has %s points.' % (scores[playerTile], scores[computerTile]))252    print('You have %s points. The computer has %s points.' % (scores[playerTile], scores[computerTile]))243253244254245255246print('Welcome to Reversi!')256print('Welcome to Reversi!')247257248while True:258while True:249    # Reset the board and game.259    # Reset the board and game.250    mainBoard = getNewBoard()260    mainBoard = getNewBoard()251    resetBoard(mainBoard)261    resetBoard(mainBoard)252    playerTile, computerTile = enterPlayerTile()262    playerTile, computerTile = enterPlayerTile()253    showHints = False263    showHints = False254    turn = whoGoesFirst()264    turn = whoGoesFirst()255    print('The ' + turn + ' will go first.')265    print('The ' + turn + ' will go first.')256266257    while True:267    while True:258        if turn == 'player':268        if turn == 'player':259            # Player's turn.269            # Player's turn.260            if showHints:270            if showHints:261                validMovesBoard = getBoardWithValidMoves(mainBoard, playerTile)271                validMovesBoard = getBoardWithValidMoves(mainBoard, playerTile)262                drawBoard(validMovesBoard)272                drawBoard(validMovesBoard)263            else:273            else:264                drawBoard(mainBoard)274                drawBoard(mainBoard)265            showPoints(playerTile, computerTile)275            showPoints(playerTile, computerTile)266            move = getPlayerMove(mainBoard, playerTile)276            move = getPlayerMove(mainBoard, playerTile)267            if move == 'quit':277            if move == 'quit':268                print('Thanks for playing!')278                print('Thanks for playing!')269                sys.exit() # terminate the program279                sys.exit() # terminate the program270            elif move == 'hints':280            elif move == 'hints':271                showHints = not showHints281                showHints = not showHints272                continue282                continue 283 284            # NEW SAVE FEATURE CODE: 285            elif move == 'save': 286                saveGameShelfFile = shelve.open('reversiSavedGames') 287                saveGameShelfFile['mainBoardVariable'] = mainBoard 288                saveGameShelfFile['playerTileVariable'] = playerTile 289                saveGameShelfFile['computerTileVariable'] = computerTile 290                saveGameShelfFile['turnVariable'] = turn 291                saveGameShelfFile['showHintsVariable'] = showHints 292                saveGameShelfFile.close() 293                print('Game saved.') 294                continue 295            elif move == 'load': 296                saveGameShelfFile = shelve.open('reversiSavedGames') 297                if 'mainBoardVariable' in saveGameShelfFile: 298                    mainBoard = saveGameShelfFile['mainBoardVariable'] 299                    playerTile = saveGameShelfFile['playerTileVariable'] 300                    computerTile = saveGameShelfFile['computerTileVariable'] 301                    turn = saveGameShelfFile['turnVariable'] 302                    showHints = saveGameShelfFile['showHintsVariable'] 303                    print('Game loaded.') 304                else: 305                    print('\n\nThere is no previously saved game.\n') 306                continue 307273            else:308            else:274                makeMove(mainBoard, playerTile, move[0], move[1])309                makeMove(mainBoard, playerTile, move[0], move[1])275310276            if getValidMoves(mainBoard, computerTile) == []:311            if getValidMoves(mainBoard, computerTile) == []:277                break312                break278            else:313            else:279                turn = 'computer'314                turn = 'computer'280315281        else:316        else:282            # Computer's turn.317            # Computer's turn.283            drawBoard(mainBoard)318            drawBoard(mainBoard)284            showPoints(playerTile, computerTile)319            showPoints(playerTile, computerTile)285            input('Press Enter to see the computer\'s move.')320            input('Press Enter to see the computer\'s move.')286            x, y = getComputerMove(mainBoard, computerTile)321            x, y = getComputerMove(mainBoard, computerTile)287            makeMove(mainBoard, computerTile, x, y)322            makeMove(mainBoard, computerTile, x, y)288323289            if getValidMoves(mainBoard, playerTile) == []:324            if getValidMoves(mainBoard, playerTile) == []:290                break325                break291            else:326            else:292                turn = 'player'327                turn = 'player'293328294    # Display the final score.329    # Display the final score.295    drawBoard(mainBoard)330    drawBoard(mainBoard)296    scores = getScoreOfBoard(mainBoard)331    scores = getScoreOfBoard(mainBoard)297    print('X scored %s points. O scored %s points.' % (scores['X'], scores['O']))332    print('X scored %s points. O scored %s points.' % (scores['X'], scores['O']))298    if scores[playerTile] > scores[computerTile]:333    if scores[playerTile] > scores[computerTile]:299        print('You beat the computer by %s points! Congratulations!' % (scores[playerTile] - scores[computerTile]))334        print('You beat the computer by %s points! Congratulations!' % (scores[playerTile] - scores[computerTile]))300    elif scores[playerTile] < scores[computerTile]:335    elif scores[playerTile] < scores[computerTile]:301        print('You lost. The computer beat you by %s points.' % (scores[computerTile] - scores[playerTile]))336        print('You lost. The computer beat you by %s points.' % (scores[computerTile] - scores[playerTile]))302    else:337    else:303        print('The game was a tie!')338        print('The game was a tie!')304339305    if not playAgain():340    if not playAgain():306        break341        break`