Skip to content

Instantly share code, notes, and snippets.

@michalwa
Last active February 15, 2020 23:44
Show Gist options
  • Save michalwa/5d58f4c748ce680474bbed6d1c4e7c5c to your computer and use it in GitHub Desktop.
Save michalwa/5d58f4c748ce680474bbed6d1c4e7c5c to your computer and use it in GitHub Desktop.
from random import choice
from itertools import chain
def is_tuple(value, shape=None):
""" Checks if the given value is a tuple and optionally checks its shape
specified by the `shape` parameter """
if type(value) == tuple:
if shape is None:
return True
if type(shape) != tuple:
raise ValueError('Shape must be either None or a tuple of types')
if len(shape) == len(value):
check_type = lambda a, b: type(a) == b if type(b) != tuple else is_tuple(a, b)
if all(check_type(a, b) for a, b in zip(value, shape)):
return True
return False
class Board:
def __init__(self, size):
self.size = size
self.cells = [[None for _ in range(size)] for _ in range(size)]
@property
def cols(self):
return [*self.cells]
@property
def rows(self):
return [*zip(*self.cells)]
@property
def values(self):
return [*chain.from_iterable(self.cells)]
def __getitem__(self, index):
if is_tuple(index, (int, int)):
return self.cells[index[0]][index[1]]
else:
raise IndexError('Board index must be a pair of ints, ' + str(index) + ' given')
def __setitem__(self, index, value):
if is_tuple(index, (int, int)):
self.cells[index[0]][index[1]] = value
else:
raise IndexError('Board index must be a pair of ints, ' + str(index) + ' given')
def solve(self, col=0, row=0):
next_col, next_row = (col + 1) % self.size, row + 1 if col == self.size - 1 else row
is_last = next_row == self.size
if self[col, row] is None:
choices = set(range(1, self.size + 1)) - set(self.cols[col]) - set(self.rows[row])
for n in choices:
self[col, row] = n
if is_last or self.solve(next_col, next_row):
return True
else:
self[col, row] = None
return False
return is_last or self.solve(next_col, next_row)
def print(self):
# Find the longest string value of a cell for alignment
maxlen = max(map(lambda n: len(str(n)) if n is not None else 0, self.values))
cellstr = lambda c: str(c) if c is not None else '-'
for row in self.rows:
print(' '.join(cellstr(cell).rjust(maxlen) for cell in row))
if __name__ == '__main__':
size = int(input('Board size?: '))
if size <= 0:
raise ValueError('Size must be greater than 0')
num_random = int(input('Number of initially randomized cells?: '))
if num_random < 0:
raise ValueError('Number must not be negative')
board = Board(size)
empty = [(col, row) for col in range(size) for row in range(size)]
for _ in range(num_random):
col, row = choice(empty)
empty.remove((col, row))
vals = set(range(1, size + 1)) - set(board.cols[col]) - set(board.rows[row])
board[col, row] = choice(list(vals))
print('\nInitial board:')
board.print()
if board.solve():
print('\nSolved board:')
board.print()
else:
# This will be reached at some point if the board is initially invalid
# but after a tremendous amount of time...
print('\nBoard could not be solved.')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment