mirror of
https://git.cs.ou.nl/joshua.moerman/puzzels.git
synced 2025-05-13 16:27:44 +02:00
121 lines
3 KiB
Python
121 lines
3 KiB
Python
# Veranderingen t.o.v. v1 en v2:
|
|
# * Ik maak nu gebruik van CardEnc om formules te maken (ipv met de hand)
|
|
# * Het script kan sudokus inlezen (alleen voor 4x4, 9x9 en FxF (hexadecimaal))
|
|
# * Het script kan ook grotere sudokus oplossen
|
|
|
|
from pysat.solvers import Solver
|
|
from pysat.formula import IDPool
|
|
from pysat.card import CardEnc, EncType
|
|
|
|
# Dit is voor een 9x9 bord
|
|
small_size = 3
|
|
full_size = small_size*small_size
|
|
|
|
|
|
# *******
|
|
# PARSING
|
|
# *******
|
|
|
|
def is_nonzero_digit(c):
|
|
return ('1' <= c and c <= '9') or ('a' <= c and c <= 'f')
|
|
|
|
# sudocue.net format
|
|
def read_input():
|
|
givens = []
|
|
line = input()
|
|
chunks = [line[i:i+full_size] for i in range(0, full_size*full_size, full_size)]
|
|
for row in range(full_size):
|
|
for column in range(full_size):
|
|
c = chunks[row][column]
|
|
if is_nonzero_digit(c):
|
|
givens.append((row, column, int(c, base=16)))
|
|
return givens
|
|
|
|
known = read_input()
|
|
|
|
|
|
# ************
|
|
# SOLVER SETUP
|
|
# ************
|
|
|
|
vpool = IDPool()
|
|
solver = Solver()
|
|
|
|
# mapping van variabeles: x_{r,c,d} -> x_i
|
|
def var(r, c, d):
|
|
return(vpool.id(("sudoku", r, c, d)))
|
|
|
|
# maakt de constraint dat precies een van de literals waar moet zijn
|
|
def unique(lits):
|
|
# deze werken goed: pairwise, seqcounter, bitwise, mtotalizer, kmtotalizer
|
|
# anderen geven groter aantal oplossingen
|
|
# alles behalve pairwise introduceert meer variabelen
|
|
cnf = CardEnc.equals(lits, 1, vpool=vpool, encoding=EncType.seqcounter)
|
|
solver.append_formula(cnf.clauses)
|
|
|
|
|
|
# **********
|
|
# SPELREGELS
|
|
# **********
|
|
|
|
# 0: in elk hokje precies 1 cijfer
|
|
for row in range(full_size):
|
|
for column in range(full_size):
|
|
unique([var(row, column, digit) for digit in range(1, full_size+1)])
|
|
|
|
# 1. in elke rij staat elk cijfer precies 1 keer
|
|
for row in range(full_size):
|
|
for digit in range(1, full_size+1):
|
|
unique([var(row, column, digit) for column in range(full_size)])
|
|
|
|
# 2. in elke kolom staat elk cijfer precies 1 keer
|
|
for column in range(full_size):
|
|
for digit in range(1, full_size+1):
|
|
unique([var(row, column, digit) for row in range(full_size)])
|
|
|
|
# 3. in elk blok staat elk cijfer precies 1 keer
|
|
block_ranges = [range(i, i+small_size) for i in range(0, full_size, small_size)]
|
|
|
|
for row_block in block_ranges:
|
|
for column_block in block_ranges:
|
|
for digit in range(1, full_size+1):
|
|
unique([var(row, column, digit) for row in row_block for column in column_block])
|
|
|
|
|
|
# ****************
|
|
# GEGEVEN GETALLEN
|
|
# ****************
|
|
|
|
for (r, c, d) in known:
|
|
solver.add_clause([var(r, c, d)])
|
|
|
|
|
|
# ********
|
|
# OPLOSSEN
|
|
# ********
|
|
|
|
num_sols = 0
|
|
for m in solver.enum_models():
|
|
model = {}
|
|
for l in m:
|
|
if l < 0:
|
|
model[-l] = False
|
|
else:
|
|
model[l] = True
|
|
|
|
for row in range(full_size):
|
|
if row % small_size == 0:
|
|
print("")
|
|
for column in range(full_size):
|
|
if column % small_size == 0:
|
|
print(" ", end="")
|
|
for digit in range(1, full_size+1):
|
|
if model[var(row, column, digit)]:
|
|
print(digit, end="")
|
|
print("")
|
|
print("")
|
|
|
|
num_sols = num_sols + 1
|
|
|
|
print("Aantal oplossingen:")
|
|
print(num_sols)
|