def _pointing_pairs_test(s, region, verbose=0): """ :type s: Sudoku :type region: set :type verbose: int """ changes = 0 freq = collections.defaultdict(set) taken_poss = set() for loc in region: for poss in s.fields[loc]: if len(s.fields[loc]) == 1: taken_poss = taken_poss | s.fields[loc] continue freq[poss].add(loc) for poss in taken_poss: try: freq.pop(poss) except KeyError: pass for poss, locs in freq.items(): for _, area in s.areas.items(): if not locs.issubset(area): continue for loc in region - locs: old_possibles = s.fields[loc] new_possibles = old_possibles - {poss} change = len(old_possibles - new_possibles) assert len(new_possibles) != 0 if change: changes += change if verbose: Colors.yellow(REMOVE.format(old_possibles, poss, loc)) s.fields[loc] = new_possibles return changes
def run(sudoku, verbose=0): changes = 0 if verbose: Colors.blue("[*] Technique: SimpleColouring".format(changes)) # sudoku.print() all_doubles = _get_doubles(sudoku, verbose) for poss, doubles in all_doubles.items(): changes += _colouring(sudoku, poss, doubles, verbose) return changes
def check_sudoku(sudoku, verbose=0): """ :type sudoku: Sudoku """ # verbose = True solved_fields = len([None for _, poss in sudoku.fields.items() if len(poss) == 1]) if solved_fields == 81: if verbose >= 2: Colors.green("[+] Sudoku solved!".format()) return True if verbose >= 2: Colors.red("[-] Solved fields: {}".format(solved_fields)) return False
def run(sudoku, verbose=0): """ :type sudoku: Sudoku :type verbose: int """ changes = 0 if verbose: Colors.blue("[*] Technique: PointingPairs".format(changes)) sudoku.print() for _, row in sudoku.rows.items(): changes += _pointing_pairs_test(sudoku, row, verbose) if changes: if verbose: Colors.green("[+] PointingPairs (Row) > Changes: {}".format(changes)) # sudoku.print() return changes for _, col in sudoku.cols.items(): changes += _pointing_pairs_test(sudoku, col, verbose) if changes: if verbose: Colors.green("[+] PointingPairs (Col) > Changes: {}".format(changes)) # sudoku.print() return changes if verbose: Colors.red("[+] PointingPairs > No changes".format(changes)) # sudoku.print() return changes
def _hidden_singles(s, region, verbose=0): changes = 0 freq = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] locations = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] for loc in region: for poss in s.fields[loc]: freq[poss] += 1 locations[poss] = loc for poss in range(1, 10): if freq[poss] == 1: loc = locations[poss] changes += len(s.fields[loc]) - 1 if verbose >= 3: Colors.yellow(REMOVE.format(loc, s.fields[loc], poss)) s.fields[loc] = {poss} return changes
def _eliminate_from_regions(s, source_loc, poss, verbose=0): """ :type s: Sudoku :type source_loc: int :type poss: set :type verbose: int :return: """ changes = 0 target_locs = s.matching_regions_single(s, source_loc) for loc in target_locs: if poss.issubset(s.fields[loc]): changes += 1 if verbose >= 3: Colors.yellow(REMOVE.format(source_loc, poss, loc, s.fields[loc])) s.fields[loc] = s.fields[loc] - poss return changes
def run(sudoku, verbose=0): changes = 0 if verbose >= 2: Colors.blue("[*] Technique: HiddenPairs".format(changes)) # s.print() for tag, row in sudoku.rows.items(): changes = _find_hidden_pairs(sudoku, row, verbose=verbose) for tag, col in sudoku.cols.items(): changes = _find_hidden_pairs(sudoku, col, verbose=verbose) for tag, area in sudoku.areas.items(): changes = _find_hidden_pairs(sudoku, area, verbose=verbose) if changes: if verbose >= 2: Colors.green("[+] HiddenPairs > Changes: {}".format(changes)) if verbose >= 4: sudoku.print() return changes
def solve_sudoku(self, sudoku, verbose=0): """ :type sudoku: Sudoku """ total_changes = 0 changes = 0 while True: total_changes += changes changes = 0 if self.check_sudoku(sudoku): if verbose >= 2: Colors.green("[+] Solved: {}".format(sudoku.line)) break sudoku.print() changes += technique.naked_singles(sudoku, verbose) if changes: continue sudoku.print() changes += technique.hidden_singles(sudoku, verbose) if changes: continue sudoku.print() changes += technique.hidden_pairs(sudoku, verbose) if changes: continue sudoku.print() changes += technique.pointing_pairs(sudoku, verbose) if changes: continue # changes += technique.fish(sudoku, verbose) # if changes: # continue # changes += technique.simple_colouring(sudoku, verbose) if verbose >= 2: Colors.red("[-] Not solved: {}".format(sudoku.line)) if verbose >= 4: sudoku.print() break # if total_changes: # print("Aaahh!") return total_changes
def _pointing_pairs(s, locs, verbose=0): """ :type s: Sudoku :type locs: set :type verbose: int """ changes = 0 freq = collections.defaultdict(list) for loc in locs: for poss in s.fields[loc]: freq[poss].append(loc) for poss, pair_locs in {p: set(ls) for p, ls in freq.items() if len(ls) in (2, 3)}.items(): target_locs = s.matching_regions(s, pair_locs) for loc in target_locs: if poss in s.fields[loc]: changes += 1 s.fields[loc].remove(poss) if verbose: Colors.yellow(REMOVE.format(pair_locs, poss, loc)) assert len(s.fields[loc]) > 0 return changes
def _hidden_singles_test(s, region, verbose=0): changes = 0 freq = collections.defaultdict(list) for loc in region: for poss in s.fields[loc]: freq[poss].append(loc) for poss, locs in freq.items(): if len(locs) > 1: continue for loc in region: if loc in locs: continue old_possibles = s.fields[loc] new_possibles = old_possibles - {poss} change = len(old_possibles - new_possibles) if change: changes += change if verbose >= 3: Colors.yellow(REMOVE.format(loc, s.fields[loc], poss)) s.fields[loc] = new_possibles return changes
def solve_current_puzzle_file(self, verbose=0, chucksize=42): changes = 0 count = 0 solved = 0 start = time() middle_time = time() try: Colors.yellow(self.update.format(0, 0, 0, 0, 0), end="\r") while True: su = self.next_sudoku() count += 1 if verbose >= 2: Colors.blue("[*] Puzzle #{}".format(count)) if verbose >= 4: su.print() changes += self.solve_current_sudoku(verbose) if verbose >= 4: su.print() if self.check_sudoku(su, verbose): solved += 1 if verbose == 1 and count % chucksize == 0: total_duration = time() - start duration = time() - middle_time middle_time = time() Colors.yellow(self.update.format(count, changes, (solved / count) * 100, total_duration, chucksize / duration), end="\r") # input("Press Enter to continue...") except StopIteration: if verbose >= 2: Colors.blue("[*] End of puzzle file.") pass end = time() total_time = end - start status_message = self.status.format(self.current_puzzles, changes, solved, count, (solved / count) * 100, total_time, count / total_time) if verbose >= 2: print(status_message) return changes, solved, count, status_message
def run(sudoku, verbose=0): changes = 0 if verbose >= 2: Colors.blue("[*] Technique: Hidden Singles".format(changes)) # sudoku.print() for _, region in sudoku.regions.items(): changes += _hidden_singles_test(sudoku, region, verbose) if changes: if verbose >= 2: Colors.green("[+] Hidden Singles > Changes: {}".format(changes)) # sudoku.print() return changes if verbose >= 2: Colors.red("[-] Hidden Singles > No change".format(changes)) # sudoku.print() return changes
def run(s, verbose=0): changes = 0 if verbose >= 2: Colors.blue("[*] Technique: NakedSingles".format(changes)) if verbose >= 4: s.print() for loc, poss in s.fields.items(): if len(poss) == 1: changes += _eliminate_from_regions(s, loc, poss, verbose) if changes: if verbose >= 2: Colors.green("[+] NakedSingles > Changes: {}".format(changes)) if verbose >= 4: s.print() return changes if verbose >= 2: Colors.red("[-] NakedSingles > No change".format(changes)) if verbose >= 4: s.print() return changes
def _find_hidden_pairs(s, region, verbose=0): changes = 0 freq = collections.defaultdict(set) already_set = 0 for loc in region: length = len(s.fields[loc]) if length == 1: already_set += 1 continue for poss in s.fields[loc]: freq[poss].add(loc) pairs = [] taken_poss = set() max_length = 9 - already_set for length in reversed(range(2, max_length + 1)): try: for candidates in itertools.combinations(freq, length): all_locs = set() all_poss = set() for p in candidates: all_locs = all_locs | freq[p] all_poss = all_poss | {p} if len(all_locs) == max_length: raise StopIteration for (poss, locs) in pairs: if all_poss.issubset(poss): raise StopIteration if len(all_poss) == len(all_locs): # Leaves a pair that is not a pair # So... # There are false hits, or freq should be redone for each iteration (meaning, return changes) changes += _remove_pairs(s, all_poss, all_locs, verbose) pairs.append((all_poss, all_locs)) taken_poss = taken_poss | all_poss except StopIteration: continue if len(pairs) == 0: return changes for poss, locs in pairs: for loc in region: old_possibles = s.fields[loc] if loc in locs: new_possibles = old_possibles & poss change = len(old_possibles - new_possibles) if change: if verbose >= 3: Colors.yellow(REMOVE.format(locs, old_possibles - poss, loc, old_possibles)) s.fields[loc] = new_possibles changes += change else: new_possibles = old_possibles - poss change = len(old_possibles - new_possibles) if change: if verbose >= 3: Colors.yellow(REMOVE.format(locs, poss, loc, old_possibles)) s.fields[loc] = new_possibles changes += change # change = len(old_possibles - new_possibles) # if change: # if verbose >= 3: # Colors.yellow(REMOVE.format(locs, poss, loc, old_possibles)) # s.fields[loc] = new_possibles # changes += change # if changes: # print("Boo!") return changes