-
Notifications
You must be signed in to change notification settings - Fork 1
/
solve.py
executable file
·139 lines (119 loc) · 4.09 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
#!/usr/bin/env python3
# vim: set foldmethod=marker
import kanren as kn
from itertools import combinations
from vision import crop_to_puzzle, find_text_regions, extract_puzzle
from pprint import pprint
import cv2
import subprocess
import time
TRANSLATION = str.maketrans("01", "OI")
fp = open("/usr/share/dict/words", "r")
RAW_WORDS = set(map(lambda x: str.lower(str.strip(x)), fp.readlines()))
fp.close()
def ranked_solutions(puzzle, candidates):
"""
Rank solutions by how many of the characters in a column of
the puzzle are in the set of characters at that same position
in the solution words.
"""
puzzle = list(map(lambda x: list(map(str.lower, x)), puzzle))
rs = dict()
for c in combinations(candidates, max(len(puzzle) - 1, len(candidates))):
score = 0
for i, v in enumerate(puzzle):
score = len(set(v) - set([x[i] for x in c]))
rs[c] = score
return [k for k, _ in sorted(rs.items(), key=lambda item: item[1])]
def logic_solve(puzzle):
"""First attempt to solve the puzzle using membero. Works but SLOW..."""
words = set()
for word in RAW_WORDS:
words.add(tuple(word))
vs = kn.vars(len(puzzle))
gs = []
for i, v in enumerate(vs):
print(tuple(map(str.lower, puzzle[i])))
gs.append(kn.membero(v, tuple(map(str.lower, puzzle[i]))))
gs.append(kn.membero(vs, words))
return kn.run(0, vs, *gs)
def solve(puzzle):
"""
Starting from a large word list, find all the candidate words
that match the requirements of the puzzle in terms of having
the right character at the right position. A position in a word
corresponds to a column in the puzzle.
"""
words = set()
puzzle = list(map(lambda x: list(map(str.lower, x)), puzzle))
for word in RAW_WORDS:
if len(word) == len(puzzle) and (
all([word[i] in puzzle[i] for i in range(len(puzzle))])
):
words.add(tuple(word))
return ranked_solutions(puzzle, words)
def step(transitions, initial, next_):
"""
Identify a single step along on a column, -n means to get
from initial to next_ you need to move up n steps, n means
move down n steps.
"""
i = transitions.index(initial)
n = transitions.index(next_)
steps = i - n
return steps, n
def play(pz):
"""
Find the moves up/down that are required to enter
the words of the solution into the game.
"""
puzzle = [[x[0] for x in i] for i in pz]
solutions = solve(puzzle) # find the possible solutions
best = sorted(solutions.pop()) # extract the best one
print("Solution: ", ["".join(i) for i in best])
mid = [i[-2] for i in pz]
res = []
for word in best:
prog = []
for (i, c) in enumerate(word):
trans = puzzle[i]
steps, n = step(trans, mid[i][0], c.upper())
mid[i] = pz[i][n]
prog.append(steps)
res.append(prog)
return res
def send_commands(prog, mid):
"""
Send the sequence of up/down movements needed to
enter a word to a connected device running the game with
adb
"""
for p in prog:
for k, c in enumerate(p):
x1, y1 = mid[k]
x2, y2 = x1, y1 (100 * (-1 if c < 0 else c))
positions = list(map(str, [x1, y1, x2, y2]))
for _ in range(abs(c)):
subprocess.run(["adb", "shell", "input", "swipe", *positions])
time.sleep(0.5)
time.sleep(0.5)
time.sleep(1)
return
def main():
subprocess.run(["adb", "shell", "screencap", "/sdcard/ts.png"])
subprocess.run(["adb", "pull", "/sdcard/ts.png", "/tmp/input.png"])
im = cv2.imread("/tmp/input.png")
resized = crop_to_puzzle(im)
cols, gray = find_text_regions(resized)
puzzle_grid = extract_puzzle(cols, gray)
# basic error correction
puzzle_grid = [
[(x[0].translate(TRANSLATION), y) for x, y in i] for i in puzzle_grid
]
pprint(puzzle_grid)
mid = [i[-2][1] for i in puzzle_grid]
prog = play(puzzle_grid)
send_commands(prog, mid)
return
if __name__ == "__main__":
main()