1. 程式人生 > >厲害了,用Python實現自動掃雷!(附帶原始碼)

厲害了,用Python實現自動掃雷!(附帶原始碼)

 

自動掃雷一般分為兩種,一種是讀取記憶體資料,而另一種是通過分析圖片獲得資料,並通過模擬滑鼠操作,這裡我用的是第二種方式。

學習Python中有不明白推薦加入交流群
                號:960410445
                群裡有志同道合的小夥伴,互幫互助,
                群裡有不錯的視訊學習教程和PDF!

 

一、準備工作

1、掃雷遊戲

我是win10,沒有預設的掃雷,所以去掃雷網下載

 

 

 

2、python 3

我的版本是 python 3.6.1

3、python的第三方庫

win32api,win32gui,win32con,Pillow,numpy,opencv

可通過 pip install —upgrade SomePackage 來進行安裝

注意:有的版本是下載pywin32,但是有的要把pywin32升級到最高並自動下載了pypiwin32,具體情況每個python版本可能都略有不同

我給出我的第三方庫和版本僅供參考

 

 

 

二、關鍵程式碼組成

1、找到遊戲視窗與座標

 
#掃雷遊戲視窗 class_name = "TMain" title_name = "Minesweeper Arbiter " hwnd = win32gui.FindWindow(class_name, title_name) #視窗座標 left = 0 top = 0 right = 0 bottom = 0 if hwnd: print("找到視窗") left, top, right, bottom = win32gui.GetWindowRect(hwnd) #win32gui.SetForegroundWindow(hwnd) print("視窗座標:") print(str(left)+' '+str(right)+' '+str(top)+' '+str(bottom)) else: print("未找到視窗") 

2、鎖定並抓取雷區影象

 
#鎖定雷區座標 #去除周圍功能按鈕以及多餘的介面 #具體的畫素值是通過QQ的截圖來判斷的 left += 15 top += 101 right -= 15 bottom -= 42 #抓取雷區影象 rect = (left, top, right, bottom) img = ImageGrab.grab().crop(rect) 

3、各影象的RGBA值

 
#數字1-8 周圍雷數 #0 未被開啟 #ed 被開啟 空白 #hongqi 紅旗 #boom 普通雷 #boom_red 踩中的雷 rgba_ed = [(225, (192, 192, 192)), (31, (128, 128, 128))] rgba_hongqi = [(54, (255, 255, 255)), (17, (255, 0, 0)), (109, (192, 192, 192)), (54, (128, 128, 128)), (22, (0, 0, 0))] rgba_0 = [(54, (255, 255, 255)), (148, (192, 192, 192)), (54, (128, 128, 128))] rgba_1 = [(185, (192, 192, 192)), (31, (128, 128, 128)), (40, (0, 0, 255))] rgba_2 = [(160, (192, 192, 192)), (31, (128, 128, 128)), (65, (0, 128, 0))] rgba_3 = [(62, (255, 0, 0)), (163, (192, 192, 192)), (31, (128, 128, 128))] rgba_4 = [(169, (192, 192, 192)), (31, (128, 128, 128)), (56, (0, 0, 128))] rgba_5 = [(70, (128, 0, 0)), (155, (192, 192, 192)), (31, (128, 128, 128))] rgba_6 = [(153, (192, 192, 192)), (31, (128, 128, 128)), (72, (0, 128, 128))] rgba_8 = [(149, (192, 192, 192)), (107, (128, 128, 128))] rgba_boom = [(4, (255, 255, 255)), (144, (192, 192, 192)), (31, (128, 128, 128)), (77, (0, 0, 0))] rgba_boom_red = [(4, (255, 255, 255)), (144, (255, 0, 0)), (31, (128, 128, 128)), (77, (0, 0, 0))] 

4、掃描雷區影象儲存至一個二維陣列map

 
#掃描雷區影象 def showmap(): img = ImageGrab.grab().crop(rect) for y in range(blocks_y): for x in range(blocks_x): this_image = img.crop((x * block_width, y * block_height, (x + 1) * block_width, (y + 1) * block_height)) if this_image.getcolors() == rgba_0: map[y][x] = 0 elif this_image.getcolors() == rgba_1: map[y][x] = 1 elif this_image.getcolors() == rgba_2: map[y][x] = 2 elif this_image.getcolors() == rgba_3: map[y][x] = 3 elif this_image.getcolors() == rgba_4: map[y][x] = 4 elif this_image.getcolors() == rgba_5: map[y][x] = 5 elif this_image.getcolors() == rgba_6: map[y][x] = 6 elif this_image.getcolors() == rgba_8: map[y][x] = 8 elif this_image.getcolors() == rgba_ed: map[y][x] = -1 elif this_image.getcolors() == rgba_hongqi: map[y][x] = -4 elif this_image.getcolors() == rgba_boom or this_image.getcolors() == rgba_boom_red: global gameover gameover = 1 break #sys.exit(0) else: print("無法識別影象") print("座標") print((y,x)) print("顏色") print(this_image.getcolors()) sys.exit(0) #print(map) 

5、掃雷演算法

這裡我採用的最基礎的演算法

1、首先點出一個點

2、掃描所有數字,如果周圍空白+插旗==數字,則空白均有雷,右鍵點選空白插旗

3、掃描所有數字,如果周圍插旗==數字,則空白均沒有雷,左鍵點選空白

4、迴圈2、3,如果沒有符合條件的,則隨機點選一個白塊

 
#插旗 def banner(): showmap() for y in range(blocks_y): for x in range(blocks_x): if 1 <= map[y][x] and map[y][x] <= 5: boom_number = map[y][x] block_white = 0 block_qi = 0 for yy in range(y-1,y+2): for xx in range(x-1,x+2): if 0 <= yy and 0 <= xx and yy < blocks_y and xx < blocks_x: if not (yy == y and xx == x):if map[yy][xx] == 0: block_white += 1 elif map[yy][xx] == -4: block_qi += 1if boom_number == block_white + block_qi:for yy in range(y - 1, y + 2): for xx in range(x - 1, x + 2): if 0 <= yy and 0 <= xx and yy < blocks_y and xx < blocks_x: if not (yy == y and xx == x): if map[yy][xx] == 0: win32api.SetCursorPos([left+xx*block_width, top+yy*block_height]) win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0) showmap() #點選白塊 def dig(): showmap() iscluck = 0 for y in range(blocks_y): for x in range(blocks_x): if 1 <= map[y][x] and map[y][x] <= 5: boom_number = map[y][x] block_white = 0 block_qi = 0 for yy in range(y - 1, y + 2): for xx in range(x - 1, x + 2): if 0 <= yy and 0 <= xx and yy < blocks_y and xx < blocks_x: if not (yy == y and xx == x): if map[yy][xx] == 0: block_white += 1 elif map[yy][xx] == -4: block_qi += 1if boom_number == block_qi and block_white > 0:for yy in range(y - 1, y + 2): for xx in range(x - 1, x + 2): if 0 <= yy and 0 <= xx and yy < blocks_y and xx < blocks_x: if not(yy == y and xx == x): if map[yy][xx] == 0: win32api.SetCursorPos([left + xx * block_width, top + yy * block_height]) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0) iscluck = 1 if iscluck == 0: luck() #隨機點選 def luck(): fl = 1 while(fl): random_x = random.randint(0, blocks_x - 1) random_y = random.randint(0, blocks_y - 1) if(map[random_y][random_x] == 0): win32api.SetCursorPos([left + random_x * block_width, top + random_y * block_height]) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0) fl = 0 def gogo(): win32api.SetCursorPos([left, top]) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0) showmap() global gameover while(1): if(gameover == 0): banner() banner() dig() else: gameover = 0 win32api.keybd_event(113, 0, 0, 0) win32api.SetCursorPos([left, top]) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0) showmap() 

這個演算法在初級和中級通過率都不錯,但是在高階成功率慘不忍睹,主要是沒有考慮邏輯組合以及白塊是雷的概率問題,可以對這兩個點進行改進,提高成功率

 
原始碼:https://github.com/chestnut-egg/GoMine