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, 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 a bitfield 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_bitstclass ReversiTest(unittest.TestCase):ring(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))