Last active
May 18, 2022 07:17
-
-
Save LaughDonor/7b250fa04bc55ee45be5b2052efee0c9 to your computer and use it in GitHub Desktop.
Solves Torus Puzzle on Exponential Idle on Android (no root)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# coding: utf-8 | |
# In[1]: | |
"""Solves Torus Puzzle on Exponential Idle on Android (no root)""" | |
__author__ = "Sunny Patel <github.com/laughdonor>" | |
from __future__ import annotations | |
from dataclasses import dataclass, astuple | |
from itertools import groupby | |
from PIL import Image | |
from pprint import pprint | |
from pytesseract import pytesseract, image_to_string, image_to_data, Output | |
from sys import exit | |
from time import sleep | |
from typing import List | |
import numpy as np | |
import cv2, os, torus # Torus solver from https://codegolf.stackexchange.com/a/172852/58557 | |
# Requires Python 3.7+ | |
@dataclass | |
class Rect: | |
x0: int | |
y0: int | |
x1: int | |
y1: int | |
def cell(self, size) -> Rect: | |
dx, dy = int((self.x1 - self.x0) / size), int((self.y1 - self.y0) / size) | |
return Rect(x0=int(self.x0 + dx // 2), y0=int(self.y0 + dy // 2), x1=dx, y1=dy) | |
def __getitem__(cls, x): | |
return getattr(cls, x) | |
def __setitem__(cls, x, value): | |
return setattr(cls, x, value) | |
# In[2]: | |
# Phone specific coordinates of grid area. Change debug variable and get coordinates from temp_file (use paint) | |
grid = Rect(x0=57, y0=896, x1=1041, y1=1880) | |
debug = False | |
tess_config = '--dpi 420 --psm 6' # Use `adb shell wm density` to get the dpi | |
pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe" | |
# In[3]: | |
def batch(iterable, n=1): | |
l = len(iterable) | |
for ndx in range(0, l, n): | |
yield iterable[ndx:min(ndx + n, l)] | |
def capture(temp_file: str ="scr.png") -> Image: | |
os.system(r"adb exec-out screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > " + temp_file) | |
grey = cv2.bitwise_not(cv2.cvtColor(cv2.imread(temp_file), cv2.COLOR_BGR2GRAY)) | |
if not debug: | |
os.remove(temp_file) | |
return Image.fromarray(cv2.threshold(grey, 152, 255, cv2.THRESH_BINARY)[1]) | |
def solve(image: Image) -> (List[(int, str, int)], int): | |
t = image_to_string(image.crop(astuple(grid)), config=f"{tess_config} --tessdata-dir ./tess -l digits").strip() | |
rows = t.count("\n") + 1 | |
M = np.fromstring(t, sep=" ", dtype=int).reshape(-1, rows) | |
if len(np.unique(M)) != rows ** 2: | |
print(f"\n\n\nERROR: Didn't recognize every number: {len(np.unique(M))}\n\n\n") | |
pprint(M) | |
exit() | |
return [(int(r), d, len(list(g))) for (d, r), g in groupby(torus.f(M.tolist()))], rows | |
def swipes(steps: List[(int, str, int)], rows: int) -> List[str]: | |
print(f"Solving with {len(steps)} swipes") | |
output, cell = [], grid.cell(rows) | |
for index, direction, count in steps: | |
coord = Rect(x0=cell.x0, y0=cell.y0, x1=cell.x0, y1=cell.y0) | |
if direction in "LR": | |
coord[f'x{"LR".index(direction)}'] += count * cell.x1 | |
coord.y0 += index * cell.y1 | |
coord.y1 = coord.y0 | |
elif direction in "UD": | |
coord[f'y{"UD".index(direction)}'] += count * cell.y1 | |
coord.x0 += index * cell.x1 | |
coord.x1 = coord.x0 | |
output.append(f"input touchscreen swipe {coord.x0} {coord.y0} {coord.x1} {coord.y1} 150") | |
return output | |
def tap(data: dict, idx: int) -> List[str]: | |
return [f"input touchscreen tap {data['left'][idx]} {data['top'][idx]}"] | |
def send_commands(commands: List[str]): | |
for batchset in batch(commands, 16): # 16 is used because of 1024 character limit of adb commands | |
command = " && sleep 0.1 && ".join(batchset) | |
if debug: | |
print(f"adb shell \"{command}\"") | |
os.system(f"adb shell \"{command}\"") | |
sleep(0.1) | |
sleep(1) | |
# In[4]: | |
while True: | |
image = capture() | |
text = image_to_string(image, config=tess_config).strip() | |
data = image_to_data(image, config=tess_config, output_type=Output.DICT) | |
if "Give Up" in text: | |
send_commands(swipes(*solve(image))) | |
elif "Claim" in data['text']: | |
pprint("Complete!") | |
idx = data['text'].index("Claim") | |
send_commands(tap(data, idx)) | |
elif "Play Torus Puzzle" in text: | |
idx = data['text'].index("Torus") | |
send_commands(tap(data, idx)) | |
elif "Select Difficulty" in text: | |
idx = data['text'].index("Hard") | |
send_commands(tap(data, idx)) | |
else: | |
pprint("Required Text not Found!") | |
pprint(text) | |
pprint(data['text']) | |
break | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
# https://codegolf.stackexchange.com/questions/172824/rubik-sorting-a-matrix-a-k-a-the-torus-puzzle | |
def f(a): | |
d = len(a) | |
r = [] | |
def V(j, b=-1): | |
b %= d | |
if d - b < b: | |
for k in range(d - b): | |
if r and r[-1] == 'U%d' % j: | |
r.pop() | |
else: | |
r.append('D%d' % j) | |
b = a[-1][j] | |
for i in range(len(a) - 1): | |
a[-1 - i][j] = a[-2 - i][j] | |
a[0][j] = b | |
else: | |
for k in range(b): | |
if r and r[-1] == 'D%d' % j: | |
r.pop() | |
else: | |
r.append('U%d' % j) | |
b = a[0][j] | |
for i in range(len(a) - 1): | |
a[i][j] = a[i + 1][j] | |
a[-1][j] = b | |
def H(i, b=-1): | |
b %= d | |
if d - b < b: | |
for k in range(d - b): | |
if r and r[-1] == 'L%d' % i: | |
r.pop() | |
else: | |
r.append('R%d' % i) | |
a[i] = (a[i])[-1:] + (a[i])[:-1] | |
else: | |
for k in range(b): | |
if r and r[-1] == 'R%d' % i: | |
r.pop() | |
else: | |
r.append('L%d' % i) | |
a[i] = (a[i])[1:] + (a[i])[:1] | |
b = sorted(sum(a, [])) | |
for i in range(d - 1): | |
for j in range(d): | |
c = b.pop(0) | |
e = sum(a, []).index(c) | |
if e // d == i: | |
if j == 0: | |
H(i, e - j) | |
elif j < e % d: | |
if i: | |
V(e % d, 1) | |
H(i, j - e) | |
V(e % d) | |
H(i, e - j) | |
else: | |
V(e) | |
H(1, e - j) | |
V(j, 1) | |
else: | |
if j == e % d: | |
H(e // d) | |
e += 1 | |
if e % d == 0: | |
e -= d | |
if i: | |
V(j, i - e // d) | |
H(e // d, e - j) | |
V(j, e // d - i) | |
c = [b.index(e) for e in a[-1]] | |
c = [sum(c[(i + j) % d] < c[(i + k) % d] for j in range(d) for k in | |
range(j)) % 2 and d * d or sum(abs(c[(i + j) % d] - j) | |
for j in range(d)) for i in range(d)] | |
e = min(~c[::-1].index(min(c)), c.index(min(c)), key=abs) | |
H(d - 1, e) | |
for j in range(d - 2): | |
e = a[-1].index(b[j]) | |
if e > j: | |
c = b.index(a[-1][j]) | |
if c == e: | |
if e - j == 1: | |
c = j + 2 | |
else: | |
c = j + 1 | |
V(e) | |
H(d - 1, j - e) | |
V(e, 1) | |
H(d - 1, c - j) | |
V(e) | |
H(d - 1, e - c) | |
V(e, 1) | |
return r |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment