-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsolve.py
181 lines (139 loc) · 5.42 KB
/
solve.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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
from ast import literal_eval
from tabulate import tabulate
from file_path import *
import json
import z3
from schema import CROSSWORD_GRID
"""
NOTE: No length-verification is required in Solve(),
as all clues retrieved from CLUES_PATH are of required length
"""
# Pattern is (initial_X, initial_Y), direction(D or A), length
# "": {"start":(), "direction":"", "length": },
GRID = CROSSWORD_GRID
class Solve():
def __init__(self):
pass
def fetch_clues(self):
"""Fetches the clues present in "clues.json"
"""
clues = dict()
with open(CLUES_PATH) as fp:
clues = literal_eval(json.load(fp))
return clues
def get_matrix(self):
clues = self.fetch_clues()
start_positions = dict()
# finding the square matrix size
max_val = 0
for clue in GRID:
start_positions[GRID[clue]["start"]] = clue
if GRID[clue]["start"][0] > max_val:
max_val = GRID[clue]["start"][0]
elif GRID[clue]["start"][1] > max_val:
max_val = GRID[clue]["start"][1]
# As the GRID starts from 0, we have to add 1 to get actual max_val
max_val += 1
matrix = [[None for index in range(max_val)] for index in range(max_val)]
# The following code encodes the matrix declared with z3.Int(VALUE)
# If at a position value is: (z3.Int(VALUE)_1, z3.Int(VALUE)_2), it signifies intersection is taking place there
# NOTE: _1 -> signifies the COLUMN index AND _2 -> signifies the ROW index
for x_index in range(max_val):
for y_index in range(max_val):
if (x_index, y_index) in list(start_positions.keys()):
pos_info = GRID[start_positions[(x_index, y_index)]]
for i in range(pos_info["length"]):
if pos_info["direction"] == "A":
if isinstance(matrix[x_index][y_index + i], z3.z3.ArithRef):
matrix[x_index][y_index + i] = (z3.Int("alpha_" + str(x_index) + "_" + str(y_index + i) + "_1"), z3.Int("alpha_" + str(x_index) + "_" + str(y_index + i) + "_2"))
else:
matrix[x_index][y_index + i] = z3.Int("alpha_" + str(x_index) + "_" + str(y_index + i))
elif pos_info["direction"] == "D":
if isinstance(matrix[x_index + i][y_index], z3.z3.ArithRef):
matrix[x_index + i][y_index] = (z3.Int("alpha_" + str(x_index + i) + "_" + str(y_index) + "_1"), z3.Int("alpha_" + str(x_index + i) + "_" + str(y_index) + "_2"))
else:
matrix[x_index + i][y_index] = z3.Int("alpha_" + str(x_index + i) + "_" + str(y_index))
return matrix, start_positions, max_val
def convert_clues_code(self):
"""converts all the clues (in string format) to a list of their ascii characters value
example: "mumbai" -> [109, 117, 109, 98, 97, 105]
"""
clues = self.fetch_clues()
clues_ord = dict()
for clue in clues:
for guess in clues[clue]:
try:
clues_ord[clue].append([ord(ch) for ch in guess.lower()])
except:
clues_ord[clue] = [[ord(ch) for ch in guess.lower()]]
return clues_ord
def make_guess_constraint(self):
clues = self.convert_clues_code()
matrix, start_positions, max_val = self.get_matrix()
clue_constraint = list()
for x_index in range(max_val):
for y_index in range(max_val):
if (x_index, y_index) in list(start_positions.keys()):
pos_info = GRID[start_positions[(x_index, y_index)]]
clue = start_positions[(x_index, y_index)]
guesses_ord = clues[clue]
all_guesses_constraint = list()
for guess_ord in guesses_ord:
guess_constraint = list()
x_i = x_index
y_i = y_index
for ch in guess_ord:
if isinstance(matrix[x_i][y_i], tuple):
# NOTE: _1 -> signifies the COLUMN index AND _2 -> signifies the ROW index
if pos_info["direction"] == "D":
matrix_val = matrix[x_i][y_i][0]
elif pos_info["direction"] == "A":
matrix_val = matrix[x_i][y_i][1]
else:
matrix_val = matrix[x_i][y_i]
guess_constraint.append(z3.And(matrix_val == ch))
if pos_info["direction"] == "D":
x_i += 1
elif pos_info["direction"] == "A":
y_i += 1
all_guesses_constraint.append(z3.And(guess_constraint))
clue_constraint.append(z3.Or(all_guesses_constraint))
clues_constraint = z3.And(clue_constraint)
return clues_constraint
def common_position_constraint(self):
clues = self.convert_clues_code()
matrix, start_positions, max_val = self.get_matrix()
equality_constraint = list()
for x_index in range(max_val):
for y_index in range(max_val):
if isinstance(matrix[x_index][y_index], tuple):
first = matrix[x_index][y_index][0]
second = matrix[x_index][y_index][1]
equality_constraint.append(z3.And(first == second))
common_position_constraint = z3.And(equality_constraint)
return common_position_constraint
def apply_constraints(self):
solver = z3.Solver()
solver.add(self.make_guess_constraint())
solver.add(self.common_position_constraint())
solver.check()
return solver.model()
def solution(self):
solved = self.apply_constraints()
solved_keys = [index.name() for index in solved]
matrix, start_positions, max_val = self.get_matrix()
for x_index in range(max_val):
for y_index in range(max_val):
if isinstance(matrix[x_index][y_index], tuple):
matrix_val = matrix[x_index][y_index][0]
elif matrix[x_index][y_index] != None:
matrix_val = matrix[x_index][y_index]
else:
continue
no = solved[matrix_val]
ch = chr(no.as_long())
matrix[x_index][y_index] = ch
return matrix
if __name__ == '__main__':
solve = Solve()
print(tabulate(solve.solution()))