歡樂五子棋輔助

苦練技術是不可能的,這輩子不可能好好練習。學技術又學不進去,就只有靠輔助才能維持的生活這樣子。html

簡述

最近玩微信小程序 - 歡樂五子棋,結果總是被虐,好氣啊。偶然想到了前段時間網上很火爆的跳一跳輔助。簡單想了一想輔助實現的思路,發現目前所需的工具已經足夠。python

須要的工具主要分爲如下三類:c++

  1. Yixin 奕心引擎,這個引擎是國人所做,能夠說是非商用版裏面最強的五子棋AIgit

    這個是官網 https://www.aiexp.info/pages/yixin-cn.htmlgithub

    一開始我想使用奕心的界面+引擎那款,由於可定製性足夠強,結果發現Python 很差與帶界面的程序進行交互,因此就選擇了引擎。然而尷尬的一點是,我把官方文檔翻了個遍都沒有找到引擎的使用方法。不事後來在世界五子棋錦標賽 https://gomocup.org/ 找到了參加比賽的AI 必備的兩種接口。web

    http://petr.lastovicka.sweb.cz/protocl2en.htm 使用輸入輸出流,本文選擇的就是這種方式shell

    http://petr.lastovicka.sweb.cz/protocl1en.htm 使用文件小程序

  2. Python 簡單圖片處理微信小程序

  3. Python adb 操做手機微信

思路

整個思路以下圖,咱們按照上面的順序從簡到繁,開始介紹每一個模塊。

image-20200329134722139

整個思路順下來程序實際上是比較好寫的,就是前期須要手動的截取匹配圖片,設置查找區域像素點位置比較麻煩。

實現

前期準備

下面的區域參數以個人手機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%

image-20200329105705796
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%,由於歡樂五子棋沒有禁手,因此執白時偶爾會輸,但執黑必勝 關於如何獲取前期準備的那些圖片,等到後期整理一下方法再上傳

相關文章
相關標籤/搜索