All functions should be in place, now it's a question of iterating

This commit is contained in:
flying-scorpio 2019-11-30 17:04:43 +01:00
parent fa967fd320
commit 7e28aff342
4 changed files with 93 additions and 39 deletions

110
board.py
View file

@ -11,7 +11,6 @@ class Board:
def __init__(self, string): def __init__(self, string):
self.lines = [""] * 9 self.lines = [""] * 9
i = 0 i = 0
line = 0 line = 0
while line < 9: while line < 9:
@ -22,11 +21,13 @@ class Board:
i += 1 i += 1
self.columns = [""] * 9 self.columns = [""] * 9
for col in range(9): for col in range(9):
for line in self.lines: for line in self.lines:
self.columns[col] += line[col] self.columns[col] += line[col]
self.grids = self.__compute_grid_values()
self.squares = []
self.__assert_lines_and_columns_have_same_values() self.__assert_lines_and_columns_have_same_values()
def __str__(self): def __str__(self):
@ -39,8 +40,8 @@ class Board:
else: else:
output += '.' output += '.'
elif isinstance(item, Square): elif isinstance(item, Square):
if len(item.content) == 1: if len(item.values) == 1:
output += str(item.content)[1:-1] output += str(item.values)[2:-2]
else: else:
output += '.' output += '.'
else: else:
@ -55,17 +56,43 @@ class Board:
for lin in range(len(self.lines)): for lin in range(len(self.lines)):
for col in range(len(self.columns)): for col in range(len(self.columns)):
if isinstance(self.lines[lin][col], Square): if isinstance(self.lines[lin][col], Square):
line_content = self.lines[lin][col].content line_values = self.lines[lin][col].values
column_content = self.columns[col][lin].content column_values = self.columns[col][lin].values
else: else:
line_content = self.lines[lin][col] line_values = self.lines[lin][col]
column_content = self.columns[col][lin] column_values = self.columns[col][lin]
if line_content != column_content: if line_values != column_values:
differences.append('X') differences.append('X')
assert len(differences) == 0 assert len(differences) == 0
def __compute_grid_values(self):
grids = [""] * 9
for lin, line in enumerate(self.lines):
for col in range(9):
if line[col] != '.':
if lin // 3 == 0 and col // 3 == 0:
grids[0] += line[col]
elif lin // 3 == 0 and col // 3 == 1:
grids[1] += line[col]
elif lin // 3 == 0 and col // 3 == 2:
grids[2] += line[col]
elif lin // 3 == 1 and col // 3 == 0:
grids[3] += line[col]
elif lin // 3 == 1 and col // 3 == 1:
grids[4] += line[col]
elif lin // 3 == 1 and col // 3 == 2:
grids[5] += line[col]
elif lin // 3 == 2 and col // 3 == 0:
grids[6] += line[col]
elif lin // 3 == 2 and col // 3 == 1:
grids[7] += line[col]
elif lin // 3 == 2 and col // 3 == 2:
grids[8] += line[col]
return [set(values) for values in grids]
def __compute_possible_values_from_lines(self): def __compute_possible_values_from_lines(self):
"""Compute possible values for each number in a line. """Compute possible values for each number in a line.
@ -74,16 +101,16 @@ class Board:
aren't already present in that same line. aren't already present in that same line.
""" """
for line_index, line in enumerate(self.lines): for lin, line in enumerate(self.lines):
self.lines[line_index] = list(line) self.lines[lin] = list(line)
for column in range(9): for column in range(9):
if line[column] == '.': if line[column] == '.':
self.lines[line_index][column] = { self.lines[lin][column] = {
number for number in range(1, 10) str(number) for number in range(1, 10)
if str(number) not in line if str(number) not in line
} }
else: else:
self.lines[line_index][column] = {int(line[column])} self.lines[lin][column] = {line[column]}
def __compute_possible_values_from_columns(self): def __compute_possible_values_from_columns(self):
"""Compute possible values for each number in a column. """Compute possible values for each number in a column.
@ -93,16 +120,16 @@ class Board:
they aren't already present in that same column. they aren't already present in that same column.
""" """
for column_index, column in enumerate(self.columns): for col, column in enumerate(self.columns):
self.columns[column_index] = list(column) self.columns[col] = list(column)
for line in range(9): for line in range(9):
if column[line] == '.': if column[line] == '.':
self.columns[column_index][line] = { self.columns[col][line] = {
number for number in range(1, 10) str(number) for number in range(1, 10)
if str(number) not in column if str(number) not in column
} }
else: else:
self.columns[column_index][line] = {int(column[line])} self.columns[col][line] = {column[line]}
def compute_possible_values(self): def compute_possible_values(self):
"""Compute possible values for each square in the board.""" """Compute possible values for each square in the board."""
@ -110,7 +137,23 @@ class Board:
self.__compute_possible_values_from_lines() self.__compute_possible_values_from_lines()
self.__compute_possible_values_from_columns() self.__compute_possible_values_from_columns()
def intersect_possible_values(self): def create_squares(self):
"""Make each square of the board a Square object."""
self.squares = []
if isinstance(self.lines[0][0], set):
print("SQUARES CREATED")
for lin, line in enumerate(self.lines):
for col in range(len(self.columns)):
square = Square(lin, col, line[col])
self.lines[lin][col] = square
self.columns[col][lin] = square
self.squares.append(square)
self.__assert_lines_and_columns_have_same_values()
def intersect_lines_and_columns(self):
"""Remove impossible values from possibilities in lines and columns. """Remove impossible values from possibilities in lines and columns.
Go through each line and for each square intersect the set with the set Go through each line and for each square intersect the set with the set
@ -123,17 +166,24 @@ class Board:
for lin, line in enumerate(self.lines): for lin, line in enumerate(self.lines):
for col, column in enumerate(self.columns): for col, column in enumerate(self.columns):
self.lines[lin][col] = line[col].intersection(column[lin]) if isinstance(self.lines[lin][col], Square):
self.columns[col][lin] = column[lin].intersection(line[col]) self.lines[lin][col].values = line[col].values.intersection(column[lin].values)
self.columns[col][lin].values = column[lin].values.intersection(line[col].values)
else:
self.lines[lin][col] = line[col].intersection(column[lin])
self.columns[col][lin] = column[lin].intersection(line[col])
self.__assert_lines_and_columns_have_same_values() self.__assert_lines_and_columns_have_same_values()
def create_squares(self): def discard_with_grids(self):
"""Make each square of the board a Square object.""" """Remove impossible values in squares, by discarding from grids."""
for lin, line in enumerate(self.lines): for square in self.squares:
for col, column in enumerate(self.columns): # print(square.values)
self.lines[lin][col] = Square(lin, col, line[col]) # print(self.grids[square.grid_id])
self.columns[col][lin] = Square(lin, col, column[lin]) if len(square.values) > 1:
for value in self.grids[square.grid_id]:
self.__assert_lines_and_columns_have_same_values() if value in square.values:
square.values.discard(value)
# print(square.values)
# print()

10
main.py
View file

@ -6,6 +6,8 @@ from board import Board
def main(): def main():
"""Provided a string representing a Sudoku game, solve it."""
string = ( string = (
"..4...618\n" "..4...618\n"
"9...8....\n" "9...8....\n"
@ -20,13 +22,15 @@ def main():
board = Board(string) board = Board(string)
print(board) print(f"Original board:\n{board}")
board.compute_possible_values() board.compute_possible_values()
board.intersect_possible_values() board.intersect_lines_and_columns()
board.create_squares() board.create_squares()
print(board) board.discard_with_grids()
print(f"Solved board:\n{board}")
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -4,17 +4,17 @@
class Square: class Square:
"""A Square object represents a square on the board. """A Square object represents a square on the board.
The square's content will consist of a set of possible values for that The square's values will consist of a set of possible values for that
square. square.
The grid attribute identifies in which 9x9 grid the square belongs to. The grid_id attribute identifies in which 9x9 grid the square belongs to.
""" """
def __init__(self, line, column, content): def __init__(self, line, column, values):
self.line = line self.line = line
self.column = column self.column = column
self.grid = self.compute_grid() self.grid_id = self.compute_grid()
self.coordinates = (self.line, self.column) self.coordinates = (self.line, self.column)
self.content = content self.values = values
def compute_grid(self): def compute_grid(self):
"""Given the coordinates, determine which grid the Square belongs to. """Given the coordinates, determine which grid the Square belongs to.

View file

@ -33,7 +33,7 @@ class TestSquare(unittest.TestCase):
) )
for i, square in enumerate(self.board): for i, square in enumerate(self.board):
self.assertEqual(square.grid, int(correct_grids[i])) self.assertEqual(square.grid_id, int(correct_grids[i]))
if __name__ == '__main__': if __name__ == '__main__':