歡樂五子棋輔助
- 2020 年 3 月 29 日
- 筆記
苦練技術是不可能的,這輩子不可能好好練習。學技術又學不進去,就只有靠輔助才能維持的生活這樣子。
簡述
最近玩微信小程式 – 歡樂五子棋,結果老是被虐,好氣啊。偶然想到了前段時間網上很火爆的跳一跳輔助。簡單想了一想輔助實現的思路,發現目前所需的工具已經足夠。
需要的工具主要分為以下三類:
-
Yixin 奕心引擎,這個引擎是國人所作,可以說是非商用版裡面最強的五子棋AI
這個是官網 https://www.aiexp.info/pages/yixin-cn.html
一開始我想使用奕心的介面+引擎那款,因為可訂製性足夠強,結果發現Python 不好與帶介面的程式進行交互,所以就選擇了引擎。然而尷尬的一點是,我把官方文檔翻了個遍都沒有找到引擎的使用方法。不過後來在世界五子棋錦標賽 https://gomocup.org/ 找到了參加比賽的AI 必備的兩種介面。
http://petr.lastovicka.sweb.cz/protocl2en.htm 使用輸入輸出流,本文選擇的就是這種方式
-
Python 簡單圖片處理
-
Python adb 操作手機
思路
整個思路如下圖,我們按照上面的順序從簡到繁,開始介紹每個模組。整個思路順下來程式其實是比較好寫的,就是前期需要手動的截取匹配圖片,查看相應的像素點位置。
實現
前期準備
下面的區域參數以我的手機1920*1080為例,不同螢幕請自行適配
class mVars: address='C:/Users/EA/Desktop/yixin/' # 使用到的文件所存放地址 boradOne = 67 #一個相鄰落子點的像素間隔 borad = (65,480) #用來將圖片像素坐標和棋盤坐標互轉 confirmBW = (820,1590,820+45,1590+60)#用來確定己方是黑棋還是白棋的區域 confirmWin = (660,1780,660+46,1780+46)#用來確定是否勝利的區域
這些是前期需要準備好的圖片,至於怎麼獲取這些圖片,等後面有時間會在補充在文末,事實上,做好這些準備工作,整個進度就完成了30%
Yixin 引擎
從官網上可以下載這個Yixin 引擎 http://gomocup.org/static/download-ai/YIXIN18.zip
結合 http://petr.lastovicka.sweb.cz/protocl2en.htm 可以知道如何與引擎交互
我使用 subprocess 這個模組讓python 與 引擎交互
import subprocess as sub class YiXin: mYixin = sub.Popen(mVars.address+"Yixin.exe", stdin=sub.PIPE, stdout=sub.PIPE, stderr=sub.PIPE) def __init__(self): self.input('START 15') self.input('INFO timeout_match 300000') self.input('INFO timeout_turn 10000') self.output() print("YiXin ready!!") def input(self,str): #向Yixin 輸入對手落子指令 print('Human: '+str) self.mYixin.stdin.write((str+'n').encode()) self.mYixin.stdin.flush() def output(self): #獲取Yixin 的輸出 #一直獲取Yixin 輸出,直到落子的指令或其它 while True: str = bytes.decode(self.mYixin.stdout.readline()) print('YiXin: '+ str,end='') if ((',' in str) or ('OK' in str)): break; self.mYixin.stdout.flush() if(',' in str): return str def restart(self): self.input('RESTART 15') self.output()
圖片處理
這個模組需要做的事就是處理跟圖片相關的,包括比較圖片,轉換坐標等
起初有兩種思路,1.每隔一段時間獲取截一張圖,對比兩張圖不同的地方,從而獲取對手落點位置。2.沒隔一段時間截一張圖,識別圖上的所有有棋的位置並保存,然後通過比較,得到對手落子位置。出於效率考慮,我選擇了第一種方法。
class ImageProcess: #如果匹配成功,則返回中心像素點 def matchImg(self,imgsrc,imgobj,confidence=0.8): coord = None res = ac.find_template(imgsrc,imgobj,confidence) if res != None: coord = (int(res['result'][0]),int(res['result'][1])) return coord #將像素坐標轉化為棋盤坐標 def transformBoard(self,coord): x = coord[0] y = coord[1] xcoord = ycoord = 0 while x>=mVars.borad[0]: x-=mVars.boradOne xcoord+=1 while y>=mVars.borad[1]: y-=mVars.boradOne ycoord+=1 return xcoord-1,ycoord-1 #將棋盤坐標轉化為像素坐標 def transfromScreen(self,coord): return (coord[0]*mVars.boradOne+mVars.borad[0],coord[1]*mVars.boradOne+mVars.borad[1]) #對比兩張圖片的差異 def difference(self,img1,img2): return img1-img2
ADB 模組
這個模組與手機交互,這塊我用的是無線ADB連接,當然也可以有線連接,關於ADB連接教程見https://github.com/mzlogin/awesome-adb
import os import time class Adb: #無線連接手機 def __init__(self): os.system('adb connect 1.1.1.1:5555')#ip 示例 os.system('adb devices') #捕獲截圖 def capture(self): os.system('adb exec-out screencap -p > '+mVars.address+'sc.jpg') return ac.imread(mVars.address+'sc.jpg') #點擊特定位置 def click(self,piexl): os.system('adb shell input tap %d %d'%(piexl[0],piexl[1])) time.sleep(0.1) os.system('adb shell input tap %d %d'%(piexl[0],piexl[1]))
遊戲系統模組
class System: Yixin = YiXin() ImageP = ImageProcess() Adb = Adb() imgobj = None #用來檢測對手落子的圖片 certain = 0 #1表示己方為白,2表示己方為黑 #確認是否勝利 def confirmWin(self,imgsrc): x0,y0,x1,y1 = mVars.confirmWin imgsrc = imgsrc[y0:y1,x0:x1] imgobj = ac.imread(mVars.address+'confirmwin.jpg') return self.ImageP.matchImg(imgsrc,imgobj,0.9) #確認己方是黑棋還是白棋 def confirmBW(self,imgsrc): x0,y0,x1,y1 = mVars.confirmBW imgsrc = imgsrc[y0:y1,x0:x1] imgobjw = ac.imread(mVars.address+'confirmw.jpg') imgobjb = ac.imread(mVars.address+'confirmb.jpg') if (self.ImageP.matchImg(imgsrc,imgobjw,0.8) != None): self.certain = 1 self.imgobj=ac.imread(mVars.address+'objb.jpg') elif (self.ImageP.matchImg(imgsrc,imgobjb,0.8)!= None): self.certain = 2 self.imgobj=ac.imread(mVars.address+'objw.jpg') #做好比賽前準備, def ready(self): while True: imgsrc = self.Adb.capture() self.confirmBW(imgsrc) if(self.certain != 0): break; print('UnCertain') time.sleep(1) if self.certain == 2: self.runCommand('BEGIN') return imgsrc elif self.certain == 1: return ac.imread(mVars.address+'None.jpg') #向Yixin 輸入對方落點,獲得Yixin 落點並點擊螢幕 def runCommand(self,COMMAND): self.Yixin.input(COMMAND) str = self.Yixin.output() a = str.find(',') b = str.find('r') piexl = self.ImageP.transfromScreen((int(str[0:a]),int(str[a+1:b]))) # print(piexl) self.Adb.click(piexl) #開始遊戲 def play(self,imgsrc): flag=False imagep = self.ImageP oldimg = newimg = imgsrc while self.confirmWin(newimg) == None: imgdif = imagep.difference(oldimg,newimg) ac.cv2.imwrite(mVars.address+'diff.jpg',imgdif) coord = imagep.matchImg(imgdif,self.imgobj) # print(coord) if(coord != None): x, y = imagep.transformBoard(coord) COMMAND = "TURN %d,%d"%(x,y) self.runCommand(COMMAND) oldimg,newimg = newimg,self.Adb.capture() time.sleep(0.8) #新一輪遊戲 def newGame(self): time.sleep(4) os.system('cls') os.system('adb shell input tap %d %d'%(100,1820)) time.sleep(0.5) os.system('adb shell input tap %d %d'%(540,940)) self.Yixin.restart() self.certain = 0 self.imgobj = None
主函數
msys = System() # n = input("請輸入你想玩的局數:") # for i in range(1,int(n)+1): while True: imgBegin = msys.ready() msys.play(imgBegin) print("You Win !! Next Game Will Begin After 4sec") msys.newGame()
後記
所有程式碼到這裡就結束了,後面將用到的資源整理一下再上傳。
已經成功的上到最高級,勝率不是100%,因為歡樂五子棋沒有禁手,所以執白時偶爾會輸,但執黑必勝