400行代码通过python解数独并操作手机自动填入
import xml.dom.minidom, os, time, randomimport cv2
from PIL import Image
import pytesseract
import matplotlib.pyplot as plt
import PIL, numpy, time
import pickle
def run_adb_way(adb_command):
process = os.popen(adb_command)
output = process.read()
return output
def go_to_input(x, y):
run_adb_way('adb shell input tap {} {}'.format(x, y))
def get_shudu_png():
t1=time.time()
run_adb_way("adb shell screencap /sdcard/screen.png")# adb shell screencap /sdcard/截图名称
run_adb_way("adb pull/sdcard/screen.png {}\screen.png".format(os.getcwd()))
img = cv2.imread(r'{}\screen.png'.format(os.getcwd()))
x=[]
y=[]
#for i in range(len(img)):、
#for j in range(len(img)):
#if 208 in img and 134 in img and 42 in img:#橙色线条的rgb是208,134,42
#if i not in y:
#y.append(i)
#if j not in x:
#x.append(j)
#zuobiao = (x,y,x+len(x),y+len(y))
zuobiao=(13, 464, 1069, 1520)
img = Image.open(r'{}\screen.png'.format(os.getcwd()))
cropped = img.crop(zuobiao)
cropped.save(r'{}\shudu.png'.format(os.getcwd()))
os.remove(r'{}\screen.png'.format(os.getcwd()))
print('将截屏中的数独单独裁剪出来,耗时{}秒'.format(time.time()-t1))
def get_shuzi():
q=0
img = Image.open(r'{}\shudu.png'.format(os.getcwd()))
shudu=[]
print('正在识别数字中....请稍后')
for i in range(0, 1056, 117)[:-1]:
for j in range(0, 1056, 117)[:-1]:
q+=1
zuobiao = (35, 20, 86, 100)#将数独分割之后再切割
cropped = img.crop((j, i, j + 117, i + 117)).crop(zuobiao)
path=r'{}\shuduqiege\temp{}.png'.format(os.getcwd(),q)
cropped.save(path)
try:
num = get_num(path)
except:
num=0
shudu.append(num)
result=[]
for y in range(0, 9):
for x in range(0, 9):
if x == 0:
result.append([])
result.append(shudu)
return result
def pretreatment(ima):
ima = ima.convert('L')
im = numpy.array(ima)
for i in range(im.shape):# 转化为二值矩阵
for j in range(im.shape):
if im == 105 or im == 160 or im == 126:
im = 1
else:
im = 0
return im
# 提取图片特征
def feature(A):
midx = int(A.shape / 2) + 1
midy = int(A.shape / 2) + 1
A1 = A.mean()
A2 = A, 0:midx].mean()
A3 = A].mean()
A4 = A, midx:A.shape].mean()
A5 = A.mean()
AF =
return AF
# 切割图片并返回每个子图片特征
def incise(im):
# 竖直切割并返回切割的坐标
a = [];
b = []
if any(im[:, 0] == 1):
a.append(0)
for i in range(im.shape - 1):# 第i列
if all(im[:, i] == 0) and any(im[:, i + 1] == 1):# 判断左边界并选择切割坐标
a.append(i + 1)
elif any(im[:, i] == 1) and all(im[:, i + 1] == 0):# 判断右边界
b.append(i + 1)
if any(im[:, im.shape - 1] == 1):
b.append(im.shape)
# 水平切割并返回分割图片特征
names = locals();
AF = []
for i in range(len(a)):
names['na%s' % i] = im[:, range(a, b)]
if any(names['na%s' % i] == 1):# 如果第一行含1,则直接从第一行开始截取
c = 0
elif any(names['na%s' % i].shape - 1, :] == 1):# 如果最后一行含1,则直接从最后一行开始截取
d = names['na%s' % i].shape - 1
for j in range(names['na%s' % i].shape - 1):# 每行开始循环
if all(names['na%s' % i] == 0) and any(names['na%s' % i] == 1):# 寻找上边界
c = j + 1
elif any(names['na%s' % i] == 1) and all(names['na%s' % i] == 0):# 寻找下边界
d = j + 1
names['na%s' % i] = names['na%s' % i]
AF.append(feature(names['na%s' % i]))# 提取特征
return AF
def training():
train_set = {}
for i in range(1, 10):
value = []
for j in range(1, 5):
try:
ima = PIL.Image.open('C:\\Users\\m\\Desktop\\shibie\\' + str(i) + '\\' + str(i) + '-' + str(j) + '.png')
im = pretreatment(ima)
AF = incise(im)
value.append(AF)
except:
pass
train_set = value
# 把训练结果存为永久文件,以备下次使用
# output=open(r'C:\Users\m\Desktop\train_set.pkl','wb')
# pickle.dump(train_set,output)
# output.close()
return train_set
def distance(v1, v2):
vector1 = numpy.array(v1)
vector2 = numpy.array(v2)
Vector = (vector1 - vector2) ** 2
distance = Vector.sum() ** 0.5
return distance
def knn(train_set, V, k):
key_sort = * k
value_sort = * k
for key in range(1, 10):
for value in train_set:
d = distance(V, value)
for i in range(k):
if d < value_sort:
for j in range(k - 2, i - 1, -1):
key_sort = key_sort
value_sort = value_sort
key_sort = key
value_sort = d
break
max_key_count = -1
key_set = set(key_sort)
for key in key_set:
if max_key_count < key_sort.count(key):
max_key_count = key_sort.count(key)
max_key = key
return max_key
def get_num(ima_path):
ima = PIL.Image.open(ima_path)
im = pretreatment(ima)# 图片预处理
AF = incise(im)# 切割图片并提取特征
train_set = {
1: [,
],
2: [,
],
3: [,
],
4: [,
,
,
],
5: [,
],
6: [,
],
7: [,
,
,
],
8: [,
,
,
],
9: []}
#train_set = training()
num = knn(train_set, AF, 1)
return num
class SuduKu():
def __init__(self, sudu_ku_data):
if not isinstance(sudu_ku_data, list):
raise TypeError(f'数独必须是一个列表, 但是{sudo_ku_data}却是 {type(sudo_ku_data)}')
if len(sudu_ku_data) != 9 or len(sudu_ku_data) != 9:
raise TypeError(f'数独必须是 9*9 列表, 但是 {sudo_ku_data} 是一个{len(sudo_ku_data)}*{len(sudo_ku_data)}的列表')
self.sudo_ku = sudu_ku_data
# 存放每一行已有的数据
self.every_row_data = {}
# 存放每一列已有的数据
self.every_column_data = {}
# 存放每一个3*3已有的数字
self.every_three_to_three_data = {}
# 存放每一个空缺的位置
self.vacant_position = []
# 每一个空缺位置尝试了的数字
self.every_vacant_position_tried_values = {}
# 初始化数据
self._init()
def _add_row_data(self, row, value):
if row not in self.every_row_data:
self.every_row_data = set()
if value in self.every_row_data:
raise TypeError(f'{self.sudo_ku}是个无效数独')
self.every_row_data.add(value)
def _add_column_data(self, column, value):
if column not in self.every_column_data:
self.every_column_data = set()
if value in self.every_column_data:
raise TypeError(f'{self.sudo_ku}是个无效数独')
self.every_column_data.add(value)
def _get_three_to_three_key(self, row, column):
if row in :
if column in :
key = 1
elif column in :
key = 2
else:
key = 3
elif row in :
if column in :
key = 4
elif column in :
key = 5
else:
key = 6
elif row in :
if column in :
key = 7
elif column in :
key = 8
else:
key = 9
return key
def _add_three_to_three_data(self, row, column, value):
key = self._get_three_to_three_key(row, column)
if key not in self.every_three_to_three_data:
self.every_three_to_three_data = set()
self.every_three_to_three_data.add(value)
def _init(self):
for row, row_datas in enumerate(self.sudo_ku):
for column, value in enumerate(row_datas):
if value == 0:
self.vacant_position.append((row, column))
else:
self._add_row_data(row, value)
self._add_column_data(column, value)
self._add_three_to_three_data(row, column, value)
def _judge_value_is_legal(self, row, column, value):# 判断放置的数据是否合法
if value in self.every_row_data:
return False
if value in self.every_column_data:
return False
key = self._get_three_to_three_key(row, column)
if value in self.every_three_to_three_data:
return False
return True
def _calculate(self, vacant_position):# 计算,开始对数独进行放置值
row, column = vacant_position
values = set(range(1, 10))
key = str(row) + str(column)# 对当前为位置创建一个唯一key,用来存放当前位置已经尝试了的数据
if key in self.every_vacant_position_tried_values:
values = values - self.every_vacant_position_tried_values
else:
self.every_vacant_position_tried_values = set()
for value in values:
self.every_vacant_position_tried_values.add(value)
if self._judge_value_is_legal(row, column, value):# 如果当前value合法,可以放置
#print(f"{vacant_position}空值的值是{value}")
self.every_column_data.add(value)
self.every_row_data.add(value)
key = self._get_three_to_three_key(row, column)
self.every_three_to_three_data.add(value)
self.sudo_ku = value# 修改这个位置的值为value
return True, value# 返回True 和填充的 value
return False, None
def _backtrack(self, current_vacant_position, previous_vacant_position, previous_value):
"""
回溯
:param current_vacant_position: 当前尝试失败的位置
:param previous_vacant_position: 上一次成功的位置
:param previous_value:上一次成功的值
:return:
"""
#print(f"上一次成功的值是{previous_value},上一次成功的位置是{previous_vacant_position}")
row, column = previous_vacant_position
# 对上一次成功的值从需要用到的判断的数据中移除
self.every_column_data.remove(previous_value)
self.every_row_data.remove(previous_value)
key = self._get_three_to_three_key(row, column)
self.every_three_to_three_data.remove(previous_value)
self.sudo_ku = ''# 并且上一次改变的的值变回去
current_row, current_column = current_vacant_position
key = str(current_row) + str(current_column)
self.every_vacant_position_tried_values.pop(key)
def get_result(self):
print('正在计算数独中....请稍后')
q = 0
t1 = time.time()
length = len(self.vacant_position)# 空缺位置的长度
index = 0# 空缺位置的下标
tried_values = []# 存放已经尝试了的数据
while index < length:
q += 1
vacant_position = self.vacant_position
is_success, value = self._calculate(vacant_position)
if is_success:
tried_values.append(value)
index += 1
else:
self._backtrack(vacant_position, self.vacant_position, tried_values.pop())
index -= 1
if index < 0:
raise ValueError(f"{self.sudo_ku}是无效的数独")
self.show_sudu_ku()
print(time.time() - t1,q)
return self.sudo_ku
def show_sudu_ku(self):
for row in self.sudo_ku:
print(row)
def judge_value_is_legal(row, column, value, sudo_ku):# 判断值是否合法
# column
for i in range(0, 9):
if row == i:
continue
if value == sudo_ku:
return False
# row
for i in range(0, 9):
if column == i:
continue
if value == sudo_ku:
return False
# three_to_three
for i in range(row // 3 * 3, row // 3 * 3 + 3):
for j in range(column // 3 * 3, column // 3 * 3 + 3):
if i == row and j == column:
continue
if value == sudo_ku:
return False
return True
def judge_sudo_ku_is_legal(sudo_ku):# 判断数独是否合法
for row, row_valus in enumerate(sudo_ku):
for column, value in enumerate(row_valus):
if not judge_value_is_legal(row, column, value, sudo_ku):
return False
return True
def shudu_to_screen(SuduKu):
num_xy_dict = dict(zip(, [(x, 1982) for x in range(91, 1000, 112)]))#获得备选数字的坐标
num_screen_xy_dict = [(j + 99, i + 550) for i in range(0, 1056, 117)[:-1] for j in range(0, 1056, 117)[:-1]]#获得数独每个单元格的坐标
for i in range(9):
for j in range(9):
x1, y1 = num_xy_dict]
x2, y2 = num_screen_xy_dict
# time.sleep(0.1)
go_to_input(x2, y2)#先填数独需要填的单元格坐标
# time.sleep(0.1)
go_to_input(x1, y1)#后点该单元格的正确数字
print(x2, y2, x1, y1, "第{}行第{}列的数字是{}".format(i + 1, j + 1, SuduKu))
def main():
get_shudu_png()#获得数独图片
sudo_ku_data = get_shuzi()#数独图片识别未数字数独
shudu_answer = SuduKu(sudo_ku_data).get_result()#解数独
shudu_to_screen(shudu_answer)#将数独的结果传到手机上面
if __name__=='__main__':
main()
感谢分享! 66666666666666666 {:10_277:} 厉害了大佬 1 2 3 大佬太强了 这是怎么回事,少了图片
FileNotFoundError: No such file or directory: 'G:\\0\\数独\\screen.png' 5 感谢分享! {:10_256:} {:10_256:} 大家都会Python啊,我好废物啊,咋办? 哎,我突然想学Python了,Python能不能做游戏啊?
{:5_102:} {:7_132:} {:5_109:} 感谢大佬
页:
[1]
2