利用Python制作扫雷游戏的外挂脚本

本方法不读取内存地雷的分布来作弊,而是利用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的照片需要你们自己抓取和编辑

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值