-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from coolsonu39/sudoku-adb
Adds android sudoku solver
- Loading branch information
Showing
4 changed files
with
173 additions
and
0 deletions.
There are no files selected for viewing
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
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) |
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
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) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
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) |