ultimate/board.py

155 lines
4.8 KiB
Python
Raw Normal View History

2022-07-06 23:39:23 -05:00
#!/usr/bin/env python3
# todo list:
# - fix all the todos in the document
# - move 'position' data to its own class with dynamic conversion between
class Board:
unplayed = 0
cross = 1
nought = 2
mappings = {
'a': 0,
'b': 1,
'c': 2,
'd': 3,
'e': 4,
'f': 5,
'g': 6,
'h': 7,
'i': 8,
}
reverse = {i[1]:i[0] for i in mappings.items()}
def __init__(self):
self.board = [[[
[Board.unplayed for i in range(3)] for j in range(3)
] for k in range(3) ] for l in range(3) ]
self.movestring = []
self.turn = Board.cross
def push(self, movestring):
pos = self.translateGlobal(movestring)
self.movestring.append(movestring)
self.board[pos[0]][pos[1]][pos[2]][pos[3]] = self.turn
if self.turn == Board.cross:
self.turn = Board.nought
else:
self.turn = Board.cross
def subboardWin(self, x, y):
b = self.board
for i in range(3):
if b[x][y][i][0] == b[x][y][i][1] == b[x][y][i][2]:
if b[x][y][i][0] != Board.unplayed:
return b[x][y][i][0]
if b[x][y][0][i] == b[x][y][1][i] == b[x][y][2][i]:
if b[x][y][0][i] != Board.unplayed:
return b[x][y][0][i]
if b[x][y][0][0] == b[x][y][1][1] == b[x][y][2][2]:
if b[x][y][0][0] != Board.unplayed:
return b[x][y][0][0]
if b[x][y][2][0] == b[x][y][1][1] == b[x][y][0][2]:
if b[x][y][2][0] != Board.unplayed:
return b[x][y][2][0]
return Board.unplayed
def allEmpty(self):
buf = []
for i in range(3):
for j in range(3):
for k in range(3):
for l in range(3):
if self.board[i][j][k][l] == Board.unplayed:
buf.append(self.translateLocal((i, j, k, l)))
def possibleMoves(self):
# get last move
try:
lastpos = self.movestring[-1]
except IndexError:
# if no moves have been made, return the whole board
ret = []
for i in range(9):
for j in range(9):
buf = []
buf.append(Board.reverse[i])
buf.append(str(j))
ret.append(''.join(buf))
return ret
# get global tuple
lastpos = self.translateGlobal(lastpos)
# check for a subboard win condition in the indicated
# position; if so, just return self.allEmpty()
# WARNING: this will select positions in already won boards that are empty
# needs to be fixed
if self.subboardWin(lastpos[0], lastpos[1]):
return self.allEmpty()
# get all squares in metasquare
# this doesn't work because it's getting the *current* square
# and not the square the move is referring to
# this needs to be fixed
square = self.returnInSquare(self.movestring[-1])
buf = []
for i in square:
# if empty, we can move
if self.isEmpty(i):
buf.append(i)
return buf
def translateGlobal(self, movestring):
globcoords = (
Board.mappings[movestring[0]],
int(movestring[1]) - 1,
)
subboard = (
int(globcoords[0] / 3),
int(globcoords[1] / 3),
)
localpos = (
globcoords[0] % 3,
globcoords[1] % 3,
)
return (subboard[0], subboard[1], localpos[0], localpos[1])
def translateLocal(self, globaltuple):
globcoords = (
globaltuple[0] * 3 + globaltuple[2],
globaltuple[1] * 3 + globaltuple[3] + 1,
)
movestring = []
movestring.append(Board.reverse[globcoords[0]])
movestring.append(str(globcoords[1]))
return ''.join(movestring)
def returnInSquare(self, movestring, convert=True):
orig = self.translateGlobal(movestring)
buf = []
for i in range(3):
for j in range(3):
buf.append((orig[0], orig[1], i, j))
if convert:
rbuf = []
for i in buf:
rbuf.append(Board.translateLocal(self, i))
return rbuf
return buf
def isEmpty(self, movestring):
orig = self.translateGlobal(movestring)
item = self.board[orig[0]][orig[1]][orig[2]][orig[3]]
if item == Board.unplayed:
return True
return False
def isFull(self, movestring):
return not self.isEmpty(movestring)
def getInnerBoard(self, x, y):
pass