本方法不读取内存地雷的分布来作弊,而是利用pyautogui,模仿人眼看到的实景来扫雷(按我目前的认知最后是有很大几率要猜几个的,如有高手知道必胜方法,欢迎交流)
话不多说,直接上程序
需要用到的匹配照片,把照片保存到同一目录下,后缀png:
一、需要用到的库
import cv2
import pyautogui
import time
import sys
import numpy as np
from tkinter import messagebox
import random
from tkinter import *
二、构建一个简单的界面
root=Tk()
lb1=Label(root,text='自动扫雷',width=15,fg="orange",bg='WHITE',font=("hei",20),relief='solid')
lb1.pack(side=TOP)
btn1=Button(root,width=15,text='Exit',command=exit_program)
btn1.pack(side=TOP)
btn2=Button(root,width=15,text='start',command=program_start)
btn2.pack(side=TOP)
frm_1=Frame(root)
frm_1.pack(side=TOP)
label_1=Label(frm_1,width=7,text='行')
label_1.grid(row=0,column=1)
en_1=Entry(frm_1,width=7)
en_1.grid(row=0,column=2)
en_1.insert(END, '16')
label_2=Label(frm_1,width=7,text='列')
label_2.grid(row=1,column=1)
en_2=Entry(frm_1,width=7)
en_2.grid(row=1,column=2)
en_2.insert(END,'30')
label_3=Label(frm_1,width=7,text='雷数')
label_3.grid(row=2,column=1)
en_3=Entry(frm_1,width=7)
en_3.grid(row=2,column=2)
en_3.insert(END,'99')
label_4=Label(root,text='信息',width=20)
label_4.pack(side=TOP)
root.mainloop()
三、开始按钮,初始化
def program_start():
global mine_map,total_mine,leftclick_yesno,l,t,w,h,d_row,d_column,d_mine
if messagebox.askokcancel('info','自动扫雷将在4秒后启动,请将扫雷游戏置于最前端')==False:
return
time.sleep(4)
#######################
# 注:初始=0,空=9,雷=8 #
#######################
d_row=int(en_1.get())
d_column=int(en_2.get())
d_mine=int(en_3.get())
print(d_row,d_column,d_mine)
# 定位雷区 地雷尺寸18*18--------------------------------------------------------
image=cv2.imread('basic_position.png')
location=pyautogui.locateOnScreen(image)
# print(location)
if location!=None:
l,t,w,h=location
pyautogui.screenshot('mine_area.png', region=(l+w, t+h, d_column*18, d_row*18))
pyautogui.click(l+w+d_column*18/2-10,t+h+d_row*18/2-5)
else:
messagebox.showerror('error','扫雷游戏没有在最前端,请重试')
return
# # 创建二维数组-----------------------------------------------------------------
mine_map=np.zeros((d_row,d_column)) # 二维雷地图
total_mine=0 #全局雷地址
double_click=0 # 开荒的地址数
while total_mine<(d_mine-1):
map_analysis() #抓图
search_result=search_mine() #搜索地图,返回本轮雷地址clear_address
print(search_result)
print(mine_map)
right_click(search_result)
map_analysis() #更新地图,因为有非雷区点开
double_click=right_left_click() #同击
print('clear address:',len(search_result),'同击:',double_click)
print('found mine:',total_mine)
if len(search_result)==0 and double_click==0:
search_result=search_share_mines() #启动共享雷判断程序
print(search_result)
if len(search_result)==0 and leftclick_yesno==0:
# if input('已经没有明确的雷,需要盲猜一个吗?')=='y':
print('没有明确的雷了,开始盲猜')
label_4.configure(text='没有明确的雷了,开始盲猜')
root.update()
# time.sleep(4)
blind_guess()
# else:
# sys.exit()
elif len(search_result)!=0:
right_click(search_result)
map_analysis() #更新地图,因为有非雷区点开
double_click=right_left_click() #同击
print('sucess!!!')
四、每次点雷或者点非雷,利用locationOnScreen, 更新2维数组的mine_map
def map_analysis(): #无返回,更新mine_map
label_4.configure(text='地图分析')
root.update()
global mine_map,l,t,w,h,d_row,d_column
locations=None
image=cv2.imread('1.png')
location_all=pyautogui.locateAllOnScreen(image,confidence=0.8,grayscale=False,region=(l+w, t+h, d_column*18, d_row*18))
locations=list(location_all)
for item_1 in locations:
a,b,c,d=item_1
if mine_map[int((b-t-h)/18),int((a-l-w)/18)]==0 or mine_map[int((b-t-h)/18),int((a-l-w)/18)]==10:
mine_map[int((b-t-h)/18),int((a-l-w)/18)]=1
image=cv2.imread('2.png')
location_all=pyautogui.locateAllOnScreen(image,confidence=0.8,grayscale=False,region=(l+w, t+h, d_column*18, d_row*18))
locations=list(location_all)
for item_2 in locations:
a,b,c,d=item_2
if mine_map[int((b-t-h)/18),int((a-l-w)/18)]==0 or mine_map[int((b-t-h)/18),int((a-l-w)/18)]==10:
mine_map[int((b-t-h)/18),int((a-l-w)/18)]=2
image=cv2.imread('3.png')
location_all=pyautogui.locateAllOnScreen(image,confidence=0.8,grayscale=False,region=(l+w, t+h, d_column*18, d_row*18))
locations=list(location_all)
for item_3 in locations:
a,b,c,d=item_3
if mine_map[int((b-t-h)/18),int((a-l-w)/18)]==0 or mine_map[int((b-t-h)/18),int((a-l-w)/18)]==10:
mine_map[int((b-t-h)/18),int((a-l-w)/18)]=3
image=cv2.imread('4.png')
location_all=pyautogui.locateAllOnScreen(image,confidence=0.8,grayscale=False,region=(l+w, t+h, d_column*18, d_row*18))
locations=list(location_all)
for item_4 in locations:
a,b,c,d=item_4
if mine_map[int((b-t-h)/18),int((a-l-w)/18)]==0 or mine_map[int((b-t-h)/18),int((a-l-w)/18)]==10:
mine_map[int((b-t-h)/18),int((a-l-w)/18)]=4
image=cv2.imread('5.png')
location_all=pyautogui.locateAllOnScreen(image,confidence=0.8,grayscale=False,region=(l+w, t+h, d_column*18, d_row*18))
locations=list(location_all)
for item_5 in locations:
a,b,c,d=item_5
if mine_map[int((b-t-h)/18),int((a-l-w)/18)]==0 or mine_map[int((b-t-h)/18),int((a-l-w)/18)]==10:
mine_map[int((b-t-h)/18),int((a-l-w)/18)]=5
image=cv2.imread('6.png')
location_all=pyautogui.locateAllOnScreen(image,confidence=0.8,grayscale=False,region=(l+w, t+h, d_column*18, d_row*18))
locations=list(location_all)
for item_6 in locations:
a,b,c,d=item_6
if mine_map[int((b-t-h)/18),int((a-l-w)/18)]==0 or mine_map[int((b-t-h)/18),int((a-l-w)/18)]==10:
mine_map[int((b-t-h)/18),int((a-l-w)/18)]=6
image=cv2.imread('empty.png')
location_all=pyautogui.locateAllOnScreen(image,confidence=0.85,grayscale=False,region=(l+w, t+h, d_column*18, d_row*18))
locations=list(location_all)
for item_7 in locations:
a,b,c,d=item_7
if mine_map[int((b-t-h)/18),int((a-l-w)/18)]==0 or mine_map[int((b-t-h)/18),int((a-l-w)/18)]==10:
mine_map[int((b-t-h)/18),int((a-l-w)/18)]=9
五、根据规则寻找雷的位置
def search_mine(): #寻找可以点雷的地址,返回clear_address
global mine_map,l,t,w,h,d_row,d_column
label_4.configure(text='搜索雷')
root.update()
clear_address=[] #本轮雷地址
for m in range(d_row):
for n in range(d_column):
# try:
mine_address=[] # 未开地址
found_address=[] #确认的雷的地址
for a in [1,0,-1]:
for b in [1,0,-1]:
if a==0 and b==0:
continue
if m+a<0 or n+b<0 or m+a>(d_row-1) or n+b>(d_column-1):
continue
if mine_map[m+a,n+b]==0:
mine_address.append([m+a,n+b])
if mine_map[m+a,n+b]==8:
found_address.append([m+a,n+b])
if len(mine_address)==mine_map[m,n]-len(found_address): #如果相等,加入点雷列表
print(mine_address)
for item_1 in mine_address:
if item_1 not in clear_address:
clear_address.append(item_1)
# 1-2-1结构查找
if mine_map[m,n]-len(found_address)==2 and len(mine_address)==3: #剩余2,未开3
#邻近找剩余1个雷的
print('剩余=2,未开=3',m,n)
for a in [1,0,-1]:
for b in [1,0,-1]:
if a==0 and b==0:
continue
if m+a<0 or m+a>(d_row-1) or n+b<0 or n+b>(d_column-1):
continue
if mine_map[m+a,n+b]>7 or mine_map[m+a,n+b]==0: #邻近不是1-7数值跳过
continue
# 如果是数值的,确认是否只剩1颗雷
print('是数值:',m+a,n+b)
close_mine=[] #未开
open_mine=[] #已点
for c in [1,0,-1]:
for d in [1,0,-1]:
if c==0 and d==0:
continue
if m+a+c<0 or n+b+d<0 or m+a+c>(d_row-1) or n+b+d>(d_column-1):
continue
if mine_map[m+a+c,n+b+d]==0:
close_mine.append([m+a+c,n+b+d])
if mine_map[m+a+c,n+b+d]==8:
open_mine.append([m+a+c,n+b+d])
if mine_map[m+a,n+b]-len(open_mine)!=1:
print('剩余不止1颗雷:',m+a,n+b)
print('-----------------------------')
continue
else: #如果剩余1颗雷,确认是否共享2个未开
print('剩余1颗雷:',m+a,n+b,'未开:',len(close_mine))
share_mine=[] #未开共享地址
for i in mine_address:
for ii in close_mine:
if i==ii:
share_mine.append(i)
# 如果共享是2个
if len(share_mine)==2:
print('共享2颗雷:',m+a+c,n+b+d)
for item_3 in mine_address:
if item_3 not in share_mine: #数值=2的除去共享的,剩余一颗点雷
if item_3 not in clear_address:
print('share clear mine:',item_3 )
clear_address.append(item_3)
for item_4 in close_mine: #剩余1颗雷的,除去共享的2颗,其余都不是
if item_4 not in share_mine:
pyautogui.leftClick(l+w+18*item_4[1]+9,t+h+18*item_4[0]+9)
mine_map[item_4[0],item_4[1]]=10 #标记非雷
print('非雷:',item_4[0],item_4[1])
return clear_address
六、标记雷
def right_click(clear_address): #右击点雷,更新mne_map,返回total_mine,看点了多少雷
label_4.configure(text='点雷')
root.update()
global total_mine,l,t,w,h
for item_2 in clear_address:
if mine_map[item_2[0],item_2[1]]!=8: #如果此地址还未点出
pyautogui.rightClick(l+w+18*item_2[1]+9,t+h+18*item_2[0]+9) # 点雷
mine_map[item_2[0],item_2[1]]=8 #地图标记雷
total_mine+=1 #雷计数器
七、点开非雷(原先用双击,但是需要每点一次执行一次map更新,否则会有很多空点击,所以后来改为左键单击)
def right_left_click(): #同击开荒,返回double_click,看还有没有可以开的余地
global double_click,mine_map,l,t,w,h,d_row,d_column
label_4.configure(text='点非雷')
root.update()
double_click=0
for m in range(d_row):
for n in range(d_column):
if 0<mine_map[m,n]<8: #只处理已点开区域
mine_address=[] #已点的雷
hide_mine=[] #还未开的格子
for a in [1,0,-1]:
for b in [1,0,-1]:
# try:
if a==0 and b==0:
continue
if m+a<0 or n+b<0 or m+a>(d_row-1) or n+b>(d_column-1):
continue
if mine_map[m+a,n+b]==8:
mine_address.append([m+a,n+b])
if mine_map[m+a,n+b]==0:
hide_mine.append([m+a,n+b])
# except:
# continue
if len(mine_address)==mine_map[m,n] and len(hide_mine)>0: #如果周围雷够了,但还有未开,左击未开
print('middle click:',m,n,'hide:',hide_mine,'open:',mine_address)
# pyautogui.middleClick(l+w+18*m+9,t+h+18*n+9)
for item in hide_mine:
pyautogui.moveTo(l+w+18*item[1]+9,t+h+18*item[0]+9)
# pyautogui.mouseDown() #按下鼠标按键(左键)
pyautogui.leftClick() #单击鼠标右键
mine_map[item[0],item[1]]=10
# pyautogui.mouseUp() #释放鼠标按键(左键)
double_click+=1
# if len(mine_address)==mine_map[m,n] and len(hide_mine)>0: #如果周围雷够了,但还有未开,双击
# print('middle click:',m,n,'hide:',hide_mine,'open:',mine_address)
# # pyautogui.middleClick(l+w+18*m+9,t+h+18*n+9)
# pyautogui.moveTo(l+w+18*n+9,t+h+18*m+9)
# pyautogui.mouseDown() #按下鼠标按键(左键)
# pyautogui.rightClick() #单击鼠标右键
# pyautogui.mouseUp() #释放鼠标按键(左键)
# double_click+=1
else:
continue
return double_click
八、最麻烦的计算,程序里有解释,比较生涩
def search_share_mines():
# 未开的雷被周围一格未开的雷全部共享,剩余的雷和周围一格剩余的雷一样,则不共享的都不是雷,
# 如果周围一格剩余的雷-查询格剩余的雷=除去共享的雷,则除去共享的雷都是雷
# if mine_map[m,n]-len(found_address)==1 and len(mine_address)==2: #剩余1,未开=2
# #邻近找共享这2个未开的
# print('剩余=1,未开=2',m,n)
global mine_map,leftclick_yesno,l,t,w,h,d_row,d_column
label_4.configure(text='共享类型雷搜索')
root.update()
leftclick_yesno=0
clear_address=[] #本轮雷地址
for m in range(d_row):
for n in range(d_column):
if mine_map[m,n]>7 or mine_map[m,n]==0: #只针对已点开数值格子进行判断
continue
mine_address=[] # 未开地址
found_address=[] #确认的雷的地址
num_address=[] # 数值的地址
for a in [1,0,-1]:
for b in [1,0,-1]:
if a==0 and b==0:
continue
if m+a<0 or n+b<0 or m+a>(d_row-1) or n+b>(d_column-1):
continue
if mine_map[m+a,n+b]==0:
mine_address.append([m+a,n+b])
if mine_map[m+a,n+b]==8:
found_address.append([m+a,n+b])
if 0<mine_map[m+a,n+b]<8:
num_address.append([m+a,n+b])
if len(mine_address)<2 or len(num_address)<0: # 如果未开雷<2个或者旁边没有数值,跳过
continue
for item_13 in num_address:
print('2个以上未开,是数值:',item_13)
close_mine=[] #未开
open_mine=[] #已点
for c in [1,0,-1]:
for d in [1,0,-1]:
if c==0 and d==0:
continue
if item_13[0]+c<0 or item_13[1]+d<0 or item_13[0]+c>(d_row-1) or item_13[1]+d>(d_column-1):
continue
if mine_map[item_13[0]+c,item_13[1]+d]==0:
close_mine.append([item_13[0]+c,item_13[1]+d])
if mine_map[item_13[0]+c,item_13[1]+d]==8:
open_mine.append([item_13[0]+c,item_13[1]+d])
# 未开的雷被周围一格未开的雷全部共享,剩余的雷和周围一格剩余的雷一样,则不共享的都不是雷
if len(close_mine)<=len(mine_address):
print(item_13,'未开<=[',m,n,']未开数,跳过',close_mine,open_mine)
continue
# print(item_13,'未开<[',m,n,']未开数,跳过',close_mine,open_mine)
all_share=1
for item_11 in mine_address: #验证是否全部共享
if item_11 not in close_mine:
all_share=0
break
if all_share==0:
print('未全包含',item_13[0]+c,item_13[1]+d)
continue
if mine_map[m,n]-len(found_address)==mine_map[item_13[0],item_13[1]]-len(open_mine):
print('全部共享,且剩余雷相同:',m+a,n+b,'剩下都不是雷')
for item_12 in close_mine:
if item_12 not in mine_address:
pyautogui.leftClick(l+w+18*item_12[1]+9,t+h+18*item_12[0]+9)
mine_map[item_12[0],item_12[1]]=10 #标记非雷
leftclick_yesno=1
print('非雷:',item_12)
#除去共享剩余的未开数=剩余雷数差值,则剩下的都是雷
elif len(close_mine)-len(mine_address)==(mine_map[item_13[0],item_13[1]]-len(open_mine))-\
(mine_map[m,n]-len(found_address)):
print('共享所有,且剩余未开=剩余雷差:',m+a,n+b,'剩下的都为雷')
for item_21 in close_mine: #剩余1颗雷的,除去共享的2颗,其余都不是
if item_21 not in mine_address:
clear_address.append(item_21)
else:
continue
return clear_address
九、盲猜
def blind_guess():
global l,t,w,h,total_mine,d_row,d_column,d_mine
blind_guess=[]
#列出未开的地址
for m in range(d_row):
for n in range(d_column):
if mine_map[m,n]==0:
blind_guess.append([m,n])
a=random.choice(blind_guess) #随机挑选盲猜地址
print('盲猜地址:',a)
pyautogui.leftClick(l+w+18*a[1]+9,t+h+18*a[0]+9)
time.sleep(2)
image=cv2.imread('fail.png')
location_all=pyautogui.locateAllOnScreen(image,confidence=0.8,grayscale=False,region=(l+w, t+h, d_column*18, d_row*18))
locations=list(location_all)
if len(locations)>0:
# messagebox.showinfo('info','猜错了,游戏失败')
print('猜错了,游戏失败')
label_4.configure(text='猜错了,游戏失败')
root.update()
total_mine=d_mine+1
其它两个相关块
def exit_program():
if messagebox.askokcancel('info','确实要退出程序吗?')==True:
root.destroy()
def fail_found(): # 返回fail和continue
global l,t,w,h,d_row,d_column
image=cv2.imread('fail.png')
location=pyautogui.locateOnScreen(image,region=(l+w, t+h, d_column*18, d_row*18))
if location!=None:
print('fail')
return 'fail'
else:
return 'continue'
以上程序copy下来就能运行,看着程序为你找雷吧
记得把屏幕分辨率设置为 类似1366*768,否则locationonscreen的照片需要你们自己抓取和编辑