|
class Board: |
|
"""Store and render the state of a game played on a grid using simple tokens. |
|
""" |
|
def __init__(self, rows, columns): |
|
self.rows = rows |
|
self.columns = columns |
|
self.matrix = [[None for c in range(0, columns)] |
|
for r in range(0, rows)] |
|
|
|
def set_cell(self, coordinates, data): |
|
"""Set the contents of a cell |
|
|
|
Args: |
|
coordinates (tuple): x and y coordinates of the cell being set. |
|
data (str): data to store in the cell (usually a single character). |
|
""" |
|
self.matrix[coordinates[0]][coordinates[1]] = data |
|
|
|
def get_available_cells(self): |
|
"""Get a list of tuples representing cell coordinates which have not been set |
|
|
|
Returns: |
|
list: coordinates of empty cells on the board |
|
""" |
|
available_cells = [] |
|
for row_index, row in enumerate(self.matrix): |
|
for column_index, cell in enumerate(row): |
|
if cell is None: |
|
available_cells.append((row_index, column_index)) |
|
return available_cells |
|
|
|
def check_for_win(self): |
|
"""Checks the board for a horizontal, vertical, or diagonal unbroken line of characters. |
|
|
|
Returns: |
|
bool: Whether or not an unbroken line was detected in any direction. |
|
""" |
|
if self._check_for_horizontal_win(): |
|
return True |
|
if self._check_for_vertical_win(): |
|
return True |
|
if self._check_for_diagonal_win(): |
|
return True |
|
return False |
|
|
|
def render(self): |
|
"""Renders a representation of the board (including a coordinate system) to stdout |
|
""" |
|
print('\n\n') |
|
|
|
header = [' '] |
|
divider = ['─'] |
|
footer = ['─'] |
|
for column_index in range(1, self.columns + 1): |
|
header.append(str(column_index)) |
|
if column_index < self.columns : |
|
divider.append('─') |
|
footer.append('─') |
|
header.append('') |
|
print(' │ '.join(header)) |
|
|
|
for row_index, row in enumerate(self.matrix): |
|
print('──┼──'.join(divider + ['───┤'])) |
|
row_data = [chr(row_index+97)] + [' ' if c is None else c for c in row] |
|
print(' │ '.join(row_data + [''])) |
|
print('──┴──'.join(divider + ['───┘'])) |
|
|
|
def _rotated(self): |
|
"""Get a rotated representation of the board matrix (useful for checking for unbroken rows) |
|
|
|
Returns: |
|
Matrix: Clockwise rotated board matrix. |
|
""" |
|
return [list(x) for x in zip(*self.matrix)][::-1] |
|
|
|
def _check_for_horizontal_win(self): |
|
"""Checks the board for a horizontal unbroken line of characters. |
|
|
|
Returns: |
|
bool: Whether or not an unbroken line was detected horizontally. |
|
""" |
|
return self._check_rows_for_win(self.matrix) |
|
|
|
def _check_for_vertical_win(self): |
|
"""Checks the board for a vertical unbroken line of characters. |
|
|
|
Returns: |
|
bool: Whether or not an unbroken line was detected vertically. |
|
""" |
|
return self._check_rows_for_win(self._rotated()) |
|
|
|
def _check_for_diagonal_win(self): |
|
"""Checks the board for a diagonally unbroken line of characters. |
|
|
|
Returns: |
|
bool: Whether or not an unbroken line was detected diagonally. |
|
""" |
|
diagonals = [[], []] |
|
for row_index, row in enumerate(self.matrix): |
|
diagonals[0].append(row[row_index]) |
|
diagonals[1].append(row[len(self.matrix) - row_index - 1]) |
|
return self._check_rows_for_win(diagonals) |
|
|
|
def _check_rows_for_win(self, rows): |
|
"""Checks each row to see if there is an unbroken line of characters. |
|
|
|
Args: |
|
rows (list): Rows to check for an unbroken line of characters. |
|
|
|
Returns: |
|
bool: Whether or not an unbroken line was detected in the rows. |
|
""" |
|
for row in rows: |
|
if row[0] is None: |
|
# Since we know an unbroken row must begin with a non-null value, we can just skip this row saving |
|
# previous CPU cycles |
|
continue |
|
if ''.join([c for c in row if c]) == row[0] * self.rows: |
|
return True |