I redid a Python Reversi move preview routine that is online. I could use comments about using Python better and/or better OOP design since the original did not use classes. There are three main classes: Board for the Reversi board, Location to hold an x,y coordinate (0-7,0-7), and Move to store a location to put a disc on a board and how many discs would be affected by that move. I separated out Move because I would like to keep a sequence of Moves in memory to play forward or reverse. Board knows about where the discs are in two bitfields, one for each player, and knows how to turn over discs and return if a disc is at a location.
import unittest
import copy
from bitarray import bitarray
INITIAL_BOARD = """
|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|
|_|_|_|B|W|_|_|_|
|_|_|_|W|B|_|_|_|
|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|
"""
def other_player(playerId):
return 'B' if playerId == 'W' else 'W'
class Location:
def __init__(self,x=0,y=0):
self.offset = self.convertToOffset(x,y)
def convertToOffset(self,x,y):
self.invalid = (x < 0 or x > 7 or y < 0 or y > 7)
return x + y * 8
def translate(self,dx,dy):
self.invalid = ((self.offset < 8 and dy == -1) or
(self.offset > 56 and dy == 1) or
(self.offset % 8 == 0 and dx == -1) or
(self.offset % 8 == 7 and dx == 1))
self.offset = self.offset + dx + dy * 8
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
class Board:
def __init__(self, board=INITIAL_BOARD):
self.discs = self.parse_board(board)
def make_bitstring(self,board,color_current):
other_color = other_player(color_current)
def parse_to_bit(square):
if square == color_current:
return '1'
elif square == '_' or square == other_color:
return '0'
return ''
return bitarray(''.join(map(parse_to_bit,board)))
def parse_board(self,board):
return (self.make_bitstring(board,'B'), self.make_bitstring(board,'W'))
def show(self):
print self.discs
def at(self, location, player):
return not location.invalid and self.discs[int(player=='W')][location.offset]
def empty(self, location):
return location.invalid or (not self.at (location,'B') and not self.at (location,'W'))
def turnoverDisc(self, location):
offset = location.offset
self.discs[0][offset], self.discs[1][offset] = self.discs[1][offset], self.discs[0][offset]
def placeDisc(self, location, player):
self.discs[int(player=='W')][location.offset] = 1
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
class Move:
def __init__(self, board, location, color_player):
self.location = location
self.player = color_player
self.board = board
self.discDelta = self.makeDiscDelta()
def makeDiscDelta(self):
result = []
other = other_player(self.player)
if not self.board.empty(self.location):
return result
next_location = Location()
for dir in [(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1),(1,1)]:
next_location.offset = self.location.offset
next_location.translate(dir[0],dir[1])
dirDelta = []
while self.board.at(next_location,other):
dirDelta.append(copy.copy(next_location))
next_location.translate(dir[0],dir[1])
if self.board.at(next_location,self.player):
result.extend(dirDelta)
return result
def makeBoard(self):
result = copy.deepcopy(self.board)
for location in self.discDelta:
result.turnoverDisc(location)
result.placeDisc(self.location,self.player)
return result
class ReversiTest(unittest.TestCase):
def testMovePreviewWhite(self):
board = Board()
location = Location(4,5)
move = Move(board,location,'W')
self.assertEquals([Location(4,4)], move.discDelta)
def testMovePreviewBlack(self):
board = Board()
location = Location(3,2)
move = Move(board,location,'W')
self.assertEquals([Location(3,3)], move.discDelta)
def testMove(self):
board = Board()
expected_board = """
|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|
|_|_|_|B|W|_|_|_|
|_|_|_|W|W|_|_|_|
|_|_|_|_|W|_|_|_|
|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|
"""
expected = Board(expected_board)
location = Location(4,5)
move = Move(board,location,'W')
result = move.makeBoard()
self.assertEquals(expected, result)
self.assertEquals(board, Board())
self.assertEquals(location, Location(4,5))