sudoku-solver/board.py

190 lines
6.9 KiB
Python
Raw Normal View History

2019-11-30 13:55:22 +01:00
"""This module contains the Board class.
It is used to represent different elements for the whole board.
"""
from square import Square
class Board:
"""The Board class contains different elements of the board."""
def __init__(self, string):
self.lines = [""] * 9
i = 0
line = 0
while line < 9:
if string[i] == '\n':
line += 1
else:
self.lines[line] += string[i]
i += 1
self.columns = [""] * 9
for col in range(9):
for line in self.lines:
self.columns[col] += line[col]
self.grids = self.__compute_grid_values()
self.squares = []
2019-11-30 13:55:22 +01:00
self.__assert_lines_and_columns_have_same_values()
def __str__(self):
2019-11-30 15:21:33 +01:00
output = ""
for line in self.lines:
for item in line:
if isinstance(item, set):
if len(item) == 1:
output += str(item)[1:-1]
else:
output += '.'
elif isinstance(item, Square):
if len(item.values) == 1:
output += str(item.values)[2:-2]
2019-11-30 15:21:33 +01:00
else:
output += '.'
else:
output += item
output += ' '
output += '\n'
return output
2019-11-30 13:55:22 +01:00
def __assert_lines_and_columns_have_same_values(self):
2019-11-30 15:21:33 +01:00
differences = []
for lin in range(len(self.lines)):
for col in range(len(self.columns)):
if isinstance(self.lines[lin][col], Square):
line_values = self.lines[lin][col].values
column_values = self.columns[col][lin].values
2019-11-30 15:21:33 +01:00
else:
line_values = self.lines[lin][col]
column_values = self.columns[col][lin]
2019-11-30 15:21:33 +01:00
if line_values != column_values:
2019-11-30 15:21:33 +01:00
differences.append('X')
2019-11-30 13:55:22 +01:00
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]
2019-11-30 13:55:22 +01:00
def __compute_possible_values_from_lines(self):
"""Compute possible values for each number in a line.
Go through each line and add possible values to each square that hasn't
been found yet. The values are computed based on the fact that they
aren't already present in that same line.
"""
for lin, line in enumerate(self.lines):
self.lines[lin] = list(line)
2019-11-30 13:55:22 +01:00
for column in range(9):
if line[column] == '.':
self.lines[lin][column] = {
str(number) for number in range(1, 10)
2019-11-30 13:55:22 +01:00
if str(number) not in line
}
else:
self.lines[lin][column] = {line[column]}
2019-11-30 13:55:22 +01:00
def __compute_possible_values_from_columns(self):
"""Compute possible values for each number in a column.
Go through each column and add possible values to each square that
hasn't been found yet. The values are computed based on the fact that
they aren't already present in that same column.
"""
for col, column in enumerate(self.columns):
self.columns[col] = list(column)
2019-11-30 13:55:22 +01:00
for line in range(9):
if column[line] == '.':
self.columns[col][line] = {
str(number) for number in range(1, 10)
2019-11-30 13:55:22 +01:00
if str(number) not in column
}
else:
self.columns[col][line] = {column[line]}
2019-11-30 13:55:22 +01:00
def compute_possible_values(self):
"""Compute possible values for each square in the board."""
self.__compute_possible_values_from_lines()
self.__compute_possible_values_from_columns()
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):
2019-11-30 13:55:22 +01:00
"""Remove impossible values from possibilities in lines and columns.
Go through each line and for each square intersect the set with the set
in the same square in self.columns.
Go through each column and for each square intersect the set with the
set in the same square in self.lines.
Note: you need to run compute_possible_values first.
"""
for lin, line in enumerate(self.lines):
for col, column in enumerate(self.columns):
if isinstance(self.lines[lin][col], Square):
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])
2019-11-30 13:55:22 +01:00
self.__assert_lines_and_columns_have_same_values()
def discard_with_grids(self):
"""Remove impossible values in squares, by discarding from grids."""
for square in self.squares:
# print(square.values)
# print(self.grids[square.grid_id])
if len(square.values) > 1:
for value in self.grids[square.grid_id]:
if value in square.values:
square.values.discard(value)
# print(square.values)
# print()