# Reversegam: a clone of Othello/Reversi
import random
import sys
WIDTH = 8 # Board is 8 spaces wide
HEIGHT = 8 # Board is 8 spaces tall
def drawBoard(board):
# This function prints the board that it was passed. Returns None.
print(' 12345678')
print(' +--------+')
for y in range(HEIGHT):
print('%s|' % (y+1), end='')
for x in range(WIDTH):
print(board[x][y], end='')
print('|%s' % (y+1))
print(' +--------+')
print(' 12345678')
def getNewBoard():
# Creates a brand-new, blank board data structure.
board = []
for i in range(WIDTH):
board.append([' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '])
return board
def isValidMove(board, tile, xstart, ystart):
# Returns False if the player's move on space xstart, ystart is invalid.
# If it is a valid move, returns a list of spaces that would become the player's if they made a move here.
if board[xstart][ystart] != ' ' or not isOnBoard(xstart, ystart):
return False
if tile == 'X':
otherTile = 'O'
else:
otherTile = 'X'
tilesToFlip = []
for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:
x, y = xstart, ystart
x += xdirection # First step in the x direction
y += ydirection # First step in the y direction
while isOnBoard(x, y) and board[x][y] == otherTile:
# Keep moving in this x & y direction.
x += xdirection
y += ydirection
if isOnBoard(x, y) and board[x][y] == tile:
# There are pieces to flip over. Go in the reverse direction until we reach the original space, noting all the tiles along the way.
while True:
x -= xdirection
y -= ydirection
if x == xstart and y == ystart:
break
tilesToFlip.append([x, y])
if len(tilesToFlip) == 0: # If no tiles were flipped, this is not a valid move.
return False
return tilesToFlip
def isOnBoard(x, y):
# Returns True if the coordinates are located on the board.
return x >= 0 and x <= WIDTH - 1 and y >= 0 and y <= HEIGHT - 1
def getBoardWithValidMoves(board, tile):
# Returns a new board with periods marking the valid moves the player can make.
boardCopy = getBoardCopy(board)
for x, y in getValidMoves(boardCopy, tile):
boardCopy[x][y] = '.'
return boardCopy
def getValidMoves(board, tile):
# Returns a list of [x,y] lists of valid moves for the given player on the given board.
validMoves = []
for x in range(WIDTH):
for y in range(HEIGHT):
if isValidMove(board, tile, x, y) != False:
validMoves.append([x, y])
return validMoves
def getScoreOfBoard(board):
# Determine the score by counting the tiles. Returns a dictionary with keys 'X' and 'O'.
xscore = 0
oscore = 0
for x in range(WIDTH):
for y in range(HEIGHT):
if board[x][y] == 'X':
xscore += 1
if board[x][y] == 'O':
oscore += 1
return {'X':xscore, 'O':oscore}
def enterPlayerTile():
# Lets the player type which tile they want to be.
# Returns a list with the player's tile as the first item and the computer's tile as the second.
tile = ''
while not (tile == 'X' or tile == 'O'):
print('Do you want to be X or O?')
tile = input().upper()
# The first element in the list is the player's tile, and the second is the computer's tile.
if tile == 'X':
return ['X', 'O']
else:
return ['O', 'X']
def whoGoesFirst():
# Randomly choose who goes first.
if random.randint(0, 1) == 0:
return 'computer'
else:
return 'player'
def makeMove(board, tile, xstart, ystart):
# Place the tile on the board at xstart, ystart, and flip any of the opponent's pieces.
# Returns False if this is an invalid move; True if it is valid.
tilesToFlip = isValidMove(board, tile, xstart, ystart)
if tilesToFlip == False:
return False
board[xstart][ystart] = tile
for x, y in tilesToFlip:
board[x][y] = tile
return True
def getBoardCopy(board):
# Make a duplicate of the board list and return it.
boardCopy = getNewBoard()
for x in range(WIDTH):
for y in range(HEIGHT):
boardCopy[x][y] = board[x][y]
return boardCopy
def isOnCorner(x, y):
# Returns True if the position is in one of the four corners.
return (x == 0 or x == WIDTH - 1) and (y == 0 or y == HEIGHT - 1)
def getPlayerMove(board, playerTile):
# Let the player enter their move.
# Returns the move as [x, y] (or returns the strings 'hints' or 'quit').
DIGITS1TO8 = '1 2 3 4 5 6 7 8'.split()
while True:
print('Enter your move, "quit" to end the game, or "hints" to toggle hints.')
move = input().lower()
if move == 'quit' or move == 'hints':
return move
if len(move) == 2 and move[0] in DIGITS1TO8 and move[1] in DIGITS1TO8:
x = int(move[0]) - 1
y = int(move[1]) - 1
if isValidMove(board, playerTile, x, y) == False:
continue
else:
break
else:
print('That is not a valid move. Enter the column (1-8) and then the row (1-8).')
print('For example, 81 will move on the top-right corner.')
return [x, y]
def getCornerBestMove(board, computerTile):
# Given a board and the computer's tile, determine where to
# move and return that move as a [x, y] list.
possibleMoves = getValidMoves(board, computerTile)
random.shuffle(possibleMoves) # randomize the order of the moves
# Always go for a corner if available.
for x, y in possibleMoves:
if isOnCorner(x, y):
return [x, y]
# Find the highest-scoring move possible.
bestScore = -1
for x, y in possibleMoves:
boardCopy = getBoardCopy(board)
makeMove(boardCopy, computerTile, x, y)
score = getScoreOfBoard(boardCopy)[computerTile]
if score > bestScore:
bestMove = [x, y]
bestScore = score
return bestMove
def getWorstMove(board, tile):
# Return the move that flips the least number of tiles.
possibleMoves = getValidMoves(board, tile)
random.shuffle(possibleMoves) # Randomize the order of the moves.
# Go through all the possible moves and remember the best scoring move.
worstScore = 64
for x, y in possibleMoves:
boardCopy = getBoardCopy(board)
makeMove(boardCopy, tile, x, y)
score = getScoreOfBoard(boardCopy)[tile]
if score < worstScore:
worstMove = [x, y]
worstScore = score
return worstMove
def getRandomMove(board, tile):
possibleMoves = getValidMoves(board, tile)
return random.choice(possibleMoves)
def isOnSide(x, y):
return x == 0 or x == WIDTH - 1 or y == 0 or y == HEIGHT - 1
def getCornerSideBestMove(board, tile):
# Return a corner move, or a side move, or the best move.
possibleMoves = getValidMoves(board, tile)
random.shuffle(possibleMoves) # Randomize the order of the moves.
# Always go for a corner if available.
for x, y in possibleMoves:
if isOnCorner(x, y):
return [x, y]
# If there is no corner, return a side move.
for x, y in possibleMoves:
if isOnSide(x, y):
return [x, y]
return getCornerBestMove(board, tile) # Do what the normal AI would do.
def printScore(board, playerTile, computerTile):
scores = getScoreOfBoard(board)