-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathslitherlink_puzzle.py
More file actions
100 lines (85 loc) · 3.13 KB
/
slitherlink_puzzle.py
File metadata and controls
100 lines (85 loc) · 3.13 KB
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
# data is taken from this repo https://github.com/ctbo/slitherlink
import matplotlib.pyplot as plt
from ortools.sat.python import cp_model # CP-SAT solver
from base import Node, Cell
from tools import make_data, dist, neighbour
"""
Slitherlink (also known as Fences and Loop the Loop) is a logic puzzle with simple rules and challenging solutions.
The rules are simple. You have to draw lines between the dots to form a single loop without crossings or branches. The numbers indicate how many lines surround it.
Solved by alireza.soroudi@gmail.com
"""
file_name = "test5.txt"
file_name = "test30x45.txt"
file_name = "test30x20.txt"
with open(file_name, "r") as f:
puzzle_text = f.read().strip() # keeps newlines, removes leading/trailing whitespace
data_dict = make_data(puzzle_text)
rows = data_dict["rows"]
cols = data_dict["cols"]
nodes = data_dict["nodes"]
cells = data_dict["cells"]
cells_all = []
nodes_all = []
for n in nodes:
nodes_all.append(
Node(name=n,
position=nodes[n]
)
)
node_by_id = {n.name: n for n in nodes_all}
for i, c in cells.items():
cells_all.append(
Cell(name=i,
position=c['position'],
nodes=[node_by_id[n] for n in c['nodes']],
value=c['value']
)
)
plt.figure(figsize=(10, 8))
for n in nodes_all:
x, y = n.position
plt.scatter(x, y, s=5, c='k')
for c in cells_all:
x, y = c.position
nodes_in_cell = [n.name for n in c.nodes]
xm = sum([node.position[0] for node in c.nodes]) / len(c.nodes)
ym = -0.2 + sum([node.position[1] for node in c.nodes]) / len(c.nodes)
if c.value:
plt.text(xm, ym, s=str(c.value), fontsize=8)
model = cp_model.CpModel()
solver = cp_model.CpSolver()
U = {(i.name, j.name): model.NewBoolVar(f"connection_{i.name}_{j.name}") for i in nodes_all for j in nodes_all if
neighbour(i, j)}
selected = {i.name: model.NewBoolVar(f"select_{i.name}") for i in nodes_all}
arcs = [(i, j, v) for (i, j), v in U.items()] + [(i, i, v.Not()) for i, v in selected.items()]
model.AddCircuit(arcs)
for cell in cells_all:
nodes_in_cell = [n.name for n in cell.nodes]
around_expr = [v for (i, j), v in U.items() if i in nodes_in_cell and j in nodes_in_cell]
if cell.value is not None:
model.Add(sum(around_expr) == cell.value)
# Maximize x
expressions = [v * dist(i, j, node_by_id) for (i, j), v in U.items()]
# model.Minimize(sum(expressions))
status = solver.Solve(model)
print(solver.status_name(status), solver.objective_value)
for (i, j), v in U.items():
if i > j:
x0, y0 = node_by_id[i].position
x1, y1 = node_by_id[j].position
plt.plot([x0, x1], [y0, y1], c='k', lw=0.5, alpha=0.5)
plt.tight_layout()
plt.axis('off')
plt.savefig('base_slitherlink.png')
for (i, j), v in U.items():
if i > j:
x0, y0 = node_by_id[i].position
x1, y1 = node_by_id[j].position
if solver.value(v) + solver.value(U[j, i]) == 1:
plt.plot([x0, x1], [y0, y1], c='r', lw=3)
else:
plt.plot([x0, x1], [y0, y1], c='k', lw=0.5, alpha=0.5)
plt.tight_layout()
plt.axis('off')
plt.savefig('final_slitherlink.png')
plt.show()