def box(self, row, col): """Get the values of the box pertaining to the specified row and column of the Sudoku""" box = [] box_i = (row // self.order) * self.order box_j = (col // self.order) * self.order for i in utils.range_(box_i, box_i + self.order): for j in utils.range_(box_j, box_j + self.order): box.append(self[i][j]) return box
def __eq__(self, other): if isinstance(other, Sudoku): if self.order != other.order: return False for i in utils.range_(self.side): for j in utils.range_(self.side): if self[i][j] != other[i][j]: return False return True return False
def _parse_from_string(string_input): """Parses a Sudoku instance from string input. :param string_input: A string containing the Sudoku to parse. :type string_input: str :return: The parsed Sudoku. :rtype: :py:class:`dlxsudoku.sudoku.Sudoku` """ # Check if comment line is present. read_lines = list(filter(None, string_input.split('\n'))) if read_lines[0].startswith('#'): comment = read_lines.pop(0) else: comment = '' if len(read_lines) > 1: # Assume that Sudoku is defined over several rows. order = int(math.sqrt(len(read_lines))) else: # Sudoku is defined on one line. order = int(math.sqrt(math.sqrt(len(read_lines[0])))) read_lines = filter(lambda x: len(x) == (order ** 2), [read_lines[0][i:(i + order ** 2)] for i in utils.range_(len(read_lines[0])) if i % (order ** 2) == 0]) matrix = utils.get_list_of_lists( order ** 2, order ** 2, fill_with=0) for i, line in enumerate(read_lines): line = line.strip() for j, value in enumerate(line): if value.isdigit() and int(value): matrix[i][j] = int(value) else: matrix[i][j] = 0 return order, comment, matrix
def project_euler_sudokus(): try: r = urlrequest.urlopen("https://projecteuler.net/project/resources/p096_sudoku.txt") sudokus = r.readlines() sudokus = [sudokus[k:k+10] for k in range_(0, len(sudokus), 10)] except: sudokus = [] return sudokus
def _update(self): """Calculate remaining values for each row, column, box and finally cell.""" # Update possible values in each row, column and box. for i, (row, col, box) in enumerate(zip(self.row_iter(), self.col_iter(), self.box_iter())): self._poss_rows[i] = set(self._values).difference(set(row)) self._poss_cols[i] = set(self._values).difference(set(col)) self._poss_box[i] = set(self._values).difference(set(box)) # Iterate over the entire Sudoku and combine information about possible values # from rows, columns and boxes to get a set of possible values for each cell. for i in utils.range_(self.side): self._possibles[i] = {} for j in utils.range_(self.side): self._possibles[i][j] = set() if self[i][j] > 0: continue this_box_index = ((i // self.order) * self.order) + (j // self.order) self._possibles[i][j] = self._poss_rows[i].intersection( self._poss_cols[j]).intersection(self._poss_box[this_box_index])
def _fill_naked_singles(self): """Look for naked singles, i.e. cells with ony one possible value. :return: If any Naked Single has been found. :rtype: bool """ simple_found = False for i in utils.range_(self.side): for j in utils.range_(self.side): if self[i][j] > 0: continue p = self._possibles[i][j] if len(p) == 1: self.set_cell(i, j, list(p)[0]) self.solution_steps.append(self._format_step("NAKED", (i, j), self[i][j])) simple_found = True elif len(p) == 0: raise SudokuHasNoSolutionError("Error made! No possible value for ({0},{1})!".format(i + 1, j + 1)) return simple_found
def __init__(self, string_input): self.order, self.comment, self._matrix = \ self._parse_from_string(string_input) self.side = self.order ** 2 self.solution_steps = [] self._values = tuple(utils.range_(0, (self.order ** 2) + 1)) self._poss_rows = {} self._poss_cols = {} self._poss_box = {} self._possibles = {} self._check_sudoku_validity()
def _fill_hidden_singles(self): """Look for hidden singles, i.e. cells with only one unique possible value in row, column or box. :return: If any Hidden Single has been found. :rtype: bool """ for i in utils.range_(self.side): box_i = (i // self.order) * self.order for j in utils.range_(self.side): box_j = (j // self.order) * self.order # Skip if this cell is determined already. if self[i][j] > 0: continue # Look for hidden single in rows. p = self._possibles[i][j] for k in utils.range_(self.side): if k == j: continue p = p.difference(self._possibles[i][k]) if len(p) == 1: # Found a hidden single in a row! self.set_cell(i, j, p.pop()) self.solution_steps.append(self._format_step("HIDDEN-ROW", (i, j), self[i][j])) return True # Look for hidden single in columns p = self._possibles[i][j] for k in utils.range_(self.side): if k == i: continue p = p.difference(self._possibles[k][j]) if len(p) == 1: # Found a hidden single in a column! self.set_cell(i, j, p.pop()) self.solution_steps.append(self._format_step("HIDDEN-COL", (i, j), self[i][j])) return True # Look for hidden single in box p = self._possibles[i][j] for k in utils.range_(box_i, box_i + self.order): for kk in utils.range_(box_j, box_j + self.order): if k == i and kk == j: continue p = p.difference(self._possibles[k][kk]) if len(p) == 1: # Found a hidden single in a box! self.set_cell(i, j, p.pop()) self.solution_steps.append(self._format_step("HIDDEN-BOX", (i, j), self[i][j])) return True return False
def box_iter(self): """Get an iterator over all boxes in the Sudoku""" for i in utils.range_(self.order): for j in utils.range_(self.order): yield self.box(i * 3, j * 3)
def col_iter(self): """Get an iterator over all columns in the Sudoku""" for k in utils.range_(self.side): yield self.col(k)
def row_iter(self): """Get an iterator over all rows in the Sudoku""" for k in utils.range_(self.side): yield self.row(k)