1# Reversi1# Reversi
22
3import random3import random
4import sys4import sys
55
 6# NEW SAVE FEATURE CODE:
 7import shelve
 8
6def 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 = '  |   |   |   |   |   |   |   |   |'
1013
11    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)
2124
2225
23def 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] = ' '
2831
29    # 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'
3437
3538
36def 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)
4144
42    return board45    return board
4346
4447
45def 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 False
5053
51    board[xstart][ystart] = tile # temporarily set the tile on the board.54    board[xstart][ystart] = tile # temporarily set the tile on the board.
5255
53    if tile == 'X':56    if tile == 'X':
54        otherTile = 'O'57        otherTile = 'O'
55    else:58    else:
56        otherTile = 'X'59        otherTile = 'X'
5760
58    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, ystart
61        x += xdirection # first step in the direction64        x += xdirection # first step in the direction
62        y += ydirection # first step in the direction65        y += ydirection # first step in the direction
63        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 += xdirection
66            y += ydirection69            y += ydirection
67            if not isOnBoard(x, y):70            if not isOnBoard(x, y):
68                continue71                continue
69            while board[x][y] == otherTile:72            while board[x][y] == otherTile:
70                x += xdirection73                x += xdirection
71                y += ydirection74                y += ydirection
72                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 loop
73                    break76                    break
74            if not isOnBoard(x, y):77            if not isOnBoard(x, y):
75                continue78                continue
76            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 -= xdirection
80                    y -= ydirection83                    y -= ydirection
81                    if x == xstart and y == ystart:84                    if x == xstart and y == ystart:
82                        break85                        break
83                    tilesToFlip.append([x, y])86                    tilesToFlip.append([x, y])
8487
85    board[xstart][ystart] = ' ' # restore the empty space88    board[xstart][ystart] = ' ' # restore the empty space
86    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 False
88    return tilesToFlip91    return tilesToFlip
8992
9093
91def 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 <=7
9497
9598
96def 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)
99102
100    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 dupeBoard
103106
104107
105def 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 = []
108111
109    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 validMoves
114117
115118
116def 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 = 0
119    oscore = 0122    oscore = 0
120    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 += 1
124            if board[x][y] == 'O':127            if board[x][y] == 'O':
125                oscore += 1128                oscore += 1
126    return {'X':xscore, 'O':oscore}129    return {'X':xscore, 'O':oscore}
127130
128131
129def 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()
136139
137    # 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']
142145
143146
144def 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'
150153
151154
152def 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')
156159
157160
158def 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)
162165
163    if tilesToFlip == False:166    if tilesToFlip == False:
164        return False167        return False
165168
166    board[xstart][ystart] = tile169    board[xstart][ystart] = tile
167    for x, y in tilesToFlip:170    for x, y in tilesToFlip:
168        board[x][y] = tile171        board[x][y] = tile
169    return True172    return True
170173
171174
172def 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()
175178
176    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]
179182
180    return dupeBoard183    return dupeBoard
181184
182185
183def 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)
186189
187190
188def 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'
 204
195        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'
199209
200        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]) - 1
202            y = int(move[1]) - 1212            y = int(move[1]) - 1
203            if isValidMove(board, playerTile, x, y) == False:213            if isValidMove(board, playerTile, x, y) == False:
204                continue214                continue
205            else:215            else:
206                break216                break
207        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.')
210220
211    return [x, y]221    return [x, y]
212222
213223
214def 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 to
216    # 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)
218228
219    # randomize the order of the possible moves229    # randomize the order of the possible moves
220    random.shuffle(possibleMoves)230    random.shuffle(possibleMoves)
221231
222    # 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]
226236
227    # Go through all the possible moves and remember the best scoring move237    # Go through all the possible moves and remember the best scoring move
228    bestScore = -1238    bestScore = -1
229    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 = score
236    return bestMove246    return bestMove
237247
238248
239def 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]))
243253
244254
245255
246print('Welcome to Reversi!')256print('Welcome to Reversi!')
247257
248while 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 = False
254    turn = whoGoesFirst()264    turn = whoGoesFirst()
255    print('The ' + turn + ' will go first.')265    print('The ' + turn + ' will go first.')
256266
257    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 program
270            elif move == 'hints':280            elif move == 'hints':
271                showHints = not showHints281                showHints = not showHints
272                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
 307
273            else:308            else:
274                makeMove(mainBoard, playerTile, move[0], move[1])309                makeMove(mainBoard, playerTile, move[0], move[1])
275310
276            if getValidMoves(mainBoard, computerTile) == []:311            if getValidMoves(mainBoard, computerTile) == []:
277                break312                break
278            else:313            else:
279                turn = 'computer'314                turn = 'computer'
280315
281        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)
288323
289            if getValidMoves(mainBoard, playerTile) == []:324            if getValidMoves(mainBoard, playerTile) == []:
290                break325                break
291            else:326            else:
292                turn = 'player'327                turn = 'player'
293328
294    # 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!')
304339
305    if not playAgain():340    if not playAgain():
306        break341        break