Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds android sudoku solver #43

Merged
merged 1 commit into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Python/Android sudoku solver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# sudoku-adb
Python script to automate solving a sudoku game in android over ADB

### See it in action [here](https://www.linkedin.com/posts/sonalagr_python-sudoku-automated-activity-6710469597004488704--Oji).

## How does it work?
- Extract the numbers into a 2D Matrix from the screenshot
- Solve the incomplete grid using backtracking algoithm
- Update the values by sending touch commands to device

### Game download [link](https://play.google.com/store/apps/details?id=com.hagstrom.henrik.sudoku)
77 changes: 77 additions & 0 deletions Python/Android sudoku solver/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# import required modules
from ppadb.client import Client
from PIL import Image
import pytesseract
import copy
from sudoku import solve, print_board

# connect to local adb server
adb = Client()
devices = adb.devices()
if len(devices) == 0:
print("No devices attached")
quit()
device = devices[0]

# save a screenshot & load it using PIL
result = device.screencap()
with open("screen.png", "wb") as fp:
fp.write(result)
im = Image.open("screen.png")

# starting position of the grid on screen
x, y = 3, 285

# width & height of single cell
dx, dy = 112, 114

# grid contains the incomplete sudoku (9x9 matrix)
# touch is also a 9x9 matrix where each element is a tuple containing pixel coordinate
grid, touch = [], []

for j in range(1,10):
grid_row, t_row = [], []
xcopy = x

for i in range(1,10):
# crop & zoom into individual cells
imcrop = im.crop((x,y,x+dx,y+dy)).crop((16,13,98,104))

t_row.append( ((2*x+dx)//2, (2*y+dy)//2) )

# every third vertical line is thicker
x += 11 if i%3==0 else 7

# extract the number from a cell
temp = pytesseract.image_to_string(imcrop, config='--psm 10')
temp = int(temp[0]) if temp[0].isnumeric() else 0

grid_row.append(temp)
x += dx
x = xcopy

# every third horizontal divider is thicker
y += dy + (12 if j%3==0 else 8)

grid.append(grid_row)
touch.append(t_row)

# solve & print the board
orig_grid = copy.deepcopy(grid)
solve(grid)
print_board(grid)

def click(i, j):
device.shell(f'input touchscreen tap {touch[i][j][0]} {touch[i][j][1]}')

def select(n):
arr = [123, 212, 314, 410, 515, 623, 724, 839, 937]
device.shell(f'input touchscreen tap {arr[n-1]} 1453')

# update values on screen
for i in range(len(grid)):
for j in range(len(grid[0])):
# only click on cells that were empty initially
if (orig_grid[i][j] == 0):
select(grid[i][j])
click(i, j)
Binary file added Python/Android sudoku solver/screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 85 additions & 0 deletions Python/Android sudoku solver/sudoku.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# copied from https://techwithtim.net/tutorials/python-programming/sudoku-solver-backtracking/

# board = [
# [7,8,0,4,0,0,1,2,0],
# [6,0,0,0,7,5,0,0,9],
# [0,0,0,6,0,1,0,7,8],
# [0,0,7,0,4,0,2,6,0],
# [0,0,1,0,5,0,9,3,0],
# [9,0,4,0,6,0,0,0,5],
# [0,7,0,3,0,0,0,1,2],
# [1,2,0,0,0,7,4,0,0],
# [0,4,9,2,0,6,0,0,7]
#

# board = [[6, 0, 0, 0, 9, 0, 0, 1, 0], [3, 9, 0, 5, 0, 2, 0, 0, 0], [0, 0, 2, 0, 0, 0, 0, 0, 4], [0, 0, 0, 9, 0, 5, 0, 2, 0], [0, 4, 0, 0, 2, 0, 7, 0, 3], [0, 0, 6, 0, 8, 0, 9, 4, 0], [0, 3, 0, 4, 0, 1, 0, 6, 7], [0, 5, 0, 0, 0, 0, 8, 0, 9], [0, 0, 7, 0, 0, 9, 4, 0, 1]]

def solve(bo):
find = find_empty(bo)
if not find:
return True
else:
row, col = find

for i in range(1,10):
if valid(bo, i, (row, col)):
bo[row][col] = i

if solve(bo):
return True

bo[row][col] = 0

return False


def valid(bo, num, pos):
# Check row
for i in range(len(bo[0])):
if bo[pos[0]][i] == num and pos[1] != i:
return False

# Check column
for i in range(len(bo)):
if bo[i][pos[1]] == num and pos[0] != i:
return False

# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3

for i in range(box_y*3, box_y*3 + 3):
for j in range(box_x * 3, box_x*3 + 3):
if bo[i][j] == num and (i,j) != pos:
return False

return True


def print_board(bo):
for i in range(len(bo)):
if i % 3 == 0 and i != 0:
print("- - - - - - - - - - - - - ")

for j in range(len(bo[0])):
if j % 3 == 0 and j != 0:
print(" | ", end="")

if j == 8:
print(bo[i][j])
else:
print(str(bo[i][j]) + " ", end="")


def find_empty(bo):
for i in range(len(bo)):
for j in range(len(bo[0])):
if bo[i][j] == 0:
return (i, j) # row, col

return None

# print_board(board)
# solve(board)
# print("___________________")
# print_board(board)