-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsudoku.py
102 lines (87 loc) · 3.62 KB
/
sudoku.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
from string import Template
from typing import Tuple
import yaml
from pulp import LpProblem, LpMinimize, LpStatus, COIN_CMD
from board import Board
from render import RenderGrid
from rule import ComposedRule, ValueRule, Rule
from variables import Variables
config = yaml.safe_load(open('config.yaml', 'r'))
solver = COIN_CMD(**config)
HTML_RESULTS_TEMPLATE = open('html_template.txt', 'r').read()
HTML_TABLE_TEMPLATE = open('html_table_template.txt', 'r').read()
class Sudoku:
def __init__(self):
self.name = self.__class__.__name__
self.problem = LpProblem(self.name, LpMinimize)
self.problem += self.objective
self.variables = Variables()
self.rules = ComposedRule(self.variables)
self.board = Board(self.variables.ROWS, self.variables.COLUMNS)
self.result = Board(self.variables.ROWS, self.variables.COLUMNS)
def write(self, filename):
self.problem.writeLP(filename)
@property
def objective(self) -> Tuple:
return 0, "Objective"
def solve(self):
self.problem.writeLP(self.__class__.__name__ + ".lp")
self.problem.solve(solver)
while True:
self.problem.solve(solver)
# The status of the solution is printed to the screen
if LpStatus[self.problem.status] == "Optimal":
# The rule is added that the same solution cannot be returned again
# self.problem += lpSum([self.choices[v][r][c] for v in self.VALUES
# If a new optimal solution cannot be found, we end the program
for row in self.variables.ROWS:
for col in self.variables.COLUMNS:
self.result.add(row, col, int(self.variables.values[row][col].varValue))
break
else:
break
def rule_text(self):
result = ""
for rule in self.rules.children:
if rule.description is None:
continue
result += f"<p>{rule.description}.</p>"
return result
def svg(self) -> str:
rg = RenderGrid("drawing.yaml")
for rule in self.rules.children:
rule.render(rg)
rg.draw_cells()
rg.draw_boxes()
rg.draw_border()
return rg.drawing.tostring()
def solution(self) -> str:
rg = RenderGrid("drawing.yaml")
rg.draw_cells()
rg.draw_boxes()
rg.draw_border()
for row in self.variables.ROWS:
for col in self.variables.COLUMNS:
known = self.board.knowns[row - 1][col - 1] is not None
rg.draw_number(row, col, self.result.knowns[row - 1][col - 1], known)
return rg.drawing.tostring()
def html(self, filename):
template = Template(HTML_RESULTS_TEMPLATE)
with open(filename, "w") as f:
f.write(template.substitute(rules=self.rule_text(), svg=self.svg(), solution=self.solution()))
@staticmethod
def load(filename: str) -> 'Sudoku':
sudoku = Sudoku()
problem = yaml.load(open(filename, "r"), Loader=yaml.FullLoader)
sudoku.rules.add(ValueRule(sudoku.variables))
for rule_config in problem['Constraints']:
if type(rule_config) == str:
rule = Rule.subclasses[rule_config](sudoku.variables)
sudoku.rules.add(rule)
elif type(rule_config) == dict:
key = next(iter(rule_config))
rule = Rule.subclasses[key](sudoku.variables, rule_config[key])
sudoku.rules.add(rule)
for constraint in sudoku.rules.constraints:
sudoku.problem += constraint
return sudoku