Python 實現auto linlink 連連看

先上知乎上大神所寫:html

你看過/寫過哪些有意思的代碼?

而後別急着看blog,玩一把遊戲再說!python

 

看知乎評論,有人說他用了一個下午時間就寫出來了. wo kao!!! 我斷斷續續寫了一週的下午才搞定,而後又用了4個小時將近一個下午纔將代碼搬到博客園. 要說這個自動鏈接連連看說簡單也簡單,說不簡單也不簡單.反正是沒你想的那麼簡單也沒你想的那麼複雜(喜歡說廢話的人).作這個小程序遇到了好幾個問題,一個一個搞定還蠻有成就感.web

 我也先放一個視頻來看看程序跑起來的效果,(話說博客園不能上傳視頻,只能上傳swf) mp4 轉swf 失真的太厲害啦! 就忍着這樣看吧.......算法

 

 

 

 連連看的算法

連連看顧名思義, 將相同的圖案鏈接起來,規則是什麼呢? 很簡單, 在三條線之內完成鏈接便可,換句話說,在小於等於兩次拐角的狀況下能把圖案相同的鏈接起來就是可聯通點.因此思路很清晰了,只要知足條件就能聯通,3條線之內可分三種狀況.chrome

狀況一:直線鏈接, 也就是兩個點同在X軸 或同在Y軸, 這種狀況下,咱們先判斷是在X軸仍是在Y軸, 而後計算出同一個軸上的最大值與最小值,最大值MAX減去最小值Min就是兩個點之間的距離,而後在判斷兩點之間的距離是否爲空,爲空即爲可聯通的點,不爲空表示之間有其餘不一樣的圖案點.json

狀況二:一次拐角鏈接, 一次拐角就至關因而用了兩條線進行聯通,也就是兩個點確定不在同一個X軸或確定不在同一個Y軸,那兩個點的沿(長)線上確定會有交集, 並且確定是兩個交集, 一個是點1的X軸與點2的Y軸交集 暫叫該點爲點C, 另一個是點1的Y軸與點2的X軸 暫叫該點爲點D,那問題就很簡單了,一次拐角的鏈接其實就是兩個直線的鏈接, 只是這個拐角的鏈接點變成了兩點直接的兩個交集, 只要計算兩點與交集是否能同時聯通, 同時聯通就認爲一次拐角可聯通.好比:點1與C可聯通,但點C與點2不可聯通,視爲不可聯通, 若是點1與點D可聯通,且點D與點2可聯通,即視爲點1與點2一次拐角可聯通.小程序

狀況三:兩次拐角鏈接,剛纔說過一次拐角鏈接的狀況其實就是兩個直線鏈接的and狀況, 那兩次拐角鏈接是否是三個直線的and狀況呢, 對的,事實就是這樣的,只不過一次拐角鏈接的狀況咱們能知道兩點的交集點,也就是上面剛纔所說的點C與點D,兩次拐角的狀況咱們就不知道這個點在哪了,因此咱們就要先選擇一個點, 就它了---->點1,   遍歷點1在一條直線上的點,這條直線多是在點1的X軸上,有可能在點1的Y軸上 ,無論如何,遍歷出的這個點確定在點1的X軸或Y軸上,不然與點1就不是一條直線了,假設咱們遍歷點1直線上的點如今取到了該點爲點E, 問題就變成點1與點E是否直線可聯通, 點E與點2是否一次拐角可聯通,點E與點2是否一次拐角可聯通能夠直接用狀況二的算法. 當知足一個直線,一個拐角 and (且) 狀況下即爲聯通,不然繼續遍歷尋找點E,直到知足條件. 瀏覽器

 

坎坷的實現過程

再來講說實現的過程app

selenium+PIL 獲取網頁flash圖像

網頁flash遊戲來自: 工具

http://www.4399.com/flash/24238_2.htm

這種方案是由於哥用的Ubuntu,應用商店幾乎沒有好玩點的連連看,H5, flash確定是首選,那經過PC玩遊戲更是首選.

參考: 

利用selenium實現驗證碼獲取並驗證   https://zhuanlan.zhihu.com/p/25171554  

selenium+python實現1688網站驗證碼圖片的截取   http://www.javashuo.com/article/p-gqjeabvi-bq.html

selenium獲取位置,經過PIL保存截圖,分析相同圖像,在經過click事件觸發自動完成連連看, OK,上代碼:

# 前提 程序啓動時 對瀏覽器窗口大小 設置
'''
https://zhuanlan.zhihu.com/p/25171554
https://blog.csdn.net/zwq912318834/article/details/78605486
'''
from selenium import webdriver
import time
from PIL import Image

chromeOpitons = webdriver.ChromeOptions()
prefs = {
    "profile.managed_default_content_settings.images": 1,
    "profile.content_settings.plugin_whitelist.adobe-flash-player": 1,
    "profile.content_settings.exceptions.plugins.*,*.per_resource.adobe-flash-player": 1,
}
chromeOpitons.add_experimental_option('prefs', prefs)
time.sleep(5)

# 打開
driver = webdriver.Chrome()
url = "http://www.4399.com/flash/24238_2.htm"
driver.set_window_size(1200, 800)
driver.get(url)

time.sleep(15)
# 獲取截圖
driver.get_screenshot_as_file('screenshot.png')

# 獲取指定元素位置
element = driver.find_element_by_id('swfdiv')
left = int(element.location['x'])
top = int(element.location['y'])

print(left, top)
print(element.size['width'], element.size['height'])
right = int(element.location['x'] + element.size['width'])
bottom = int(element.location['y'] + element.size['height'])

# 經過Image處理圖像
im = Image.open('screenshot.png')
im = im.crop((left, top, right, bottom))
im.save('flash_game.png')

問題來了,chrome瀏覽器支持flash,可是當webdriver 控制瀏覽器的時候,flash死活不能啓用! Firefox也是同樣!

           

後來當我寫這個blog的時候又search 查緣由,嘗試了下, 竟然能夠了!不過已經用本地player實現了, 有時間了在寫這個webdriver 控制瀏覽器截圖+自動click

說一下當時爲何flash在瀏覽器中能夠啓用, 在webdriver控制中就不能使用, 一行代碼解決,自行體會

sudo apt-get install pepperflashplugin-nonfree

 

當時放棄了selenium 控制瀏覽器的方法後,就把flash.swf下載下來,經過 gnash flash播放器打開swf文件,PIL截圖,切圖,對比圖片,pyautogui模擬鼠標點擊.......的思路完成自動連連看.思路有了,就一步一步去實現.

PIL截圖 切圖

查找類似的圖片就是把每一個小方塊都切割出來進行對比,PIL和openCV均可以作到,這裏只提PIL.

 PIL截圖很容易Image.save 方法就是保存操做,切圖使用Image.crop方法,crop有四個元組的參數表示爲座標 (left, upper, right, lower), right-left 與lower-upper 計算的大小就是圖片的像素大小.

例以下面這張圖片,切成4X4的小圖片  (源圖片的大小爲 840X1280)

 

from PIL import Image

img = Image.open('resource.jpg')  # 固定圖片的大小爲 840X1280
region = (210, 0, 420, 320)  # rop((x0,y0,x1,y1)) 圖片裁切

for i in range(0, 4):  # width長
    for j in range(0, 4):  # height高

        r = (i * 210, j * 320, 210 + 210 * i, 320 + 320 * j)
        # 裁切圖片
        cropImg = img.crop(r)
        # 保存裁切後的圖片
        cropImg.save('crop' + str(i) + str(j) + '.jpg')
        print(r)

切割後就生成了12個小圖片,如下是12個小圖片拼接的效果

其實,在這個程序中,也能夠不用保存小方塊, 直接將切好的圖片對象放在list中,用的時候直接從內存讀取. 在程序中生成是便於理解.ok,知道了PIL如何截圖和切圖,咱們如今模擬打開flash播放器,截取屏幕中的遊戲區域與連連看的小方塊區域,並生成小方塊.

在這以前,咱們要知道flash的大小,遊戲區域與屏幕距離大小, 連連看的小方塊區域與flash播放器的高度與左側空間區域大小,每一個小方塊像素大小.

上代碼:

import pyscreenshot as ImageGrab
import os
import time
from PIL import Image

os.popen('gnash a.swf -j750 -k450 -X100 -Y100')
print('打開gnash swf 完成')

time.sleep(5)
im = ImageGrab.grab([100, 100, 850, 580])  # X1,Y1,X2,Y2  (X2-X1=750,Y2-Y1=450)  # 經過截圖工具查看爲750*480
im.save("1.png")

# 切片
# 經過Image處理圖像
im = Image.open('1.png')
left = 80
top = 145
im = im.crop((left, top, 560 + left, 320 + top))
im.save('2.png')

# 貼片成小方塊
img = Image.open('2.png')
for y in range(0, 8):  # height高

    for x in range(0, 14):  # width長
        r = (x * 40, y * 40, 40 + 40 * x, 40 + 40 * y)
        cropImg = img.crop(r)
        x_play = x + 1
        y_play = abs(8 - y)

        cropImg.save('./openpic_o/x' + str(x_play) + "y" + str(y_play) + '.png')
        cropImg = cropImg.crop((5, 5, 35, 35))  # 生成的40*40 左右上下裁剪5px

        cropImg.save('./openpic/x' + str(x_play) + "y" + str(y_play) + '.png')
        time.sleep(0.01)
        print(r)

 

打印的內容:

打開gnash swf 完成
(0, 0, 40, 40)
(40, 0, 80, 40)
(80, 0, 120, 40)
(120, 0, 160, 40)
(160, 0, 200, 40)
(200, 0, 240, 40)
(240, 0, 280, 40)
(280, 0, 320, 40)
(320, 0, 360, 40)
(360, 0, 400, 40)
(400, 0, 440, 40)
(440, 0, 480, 40)
(480, 0, 520, 40)
(520, 0, 560, 40)
(0, 40, 40, 80)
(40, 40, 80, 80)
(80, 40, 120, 80)
(120, 40, 160, 80)
(160, 40, 200, 80)
(200, 40, 240, 80)
(240, 40, 280, 80)
(280, 40, 320, 80)
(320, 40, 360, 80)
(360, 40, 400, 80)
(400, 40, 440, 80)
(440, 40, 480, 80)
(480, 40, 520, 80)
(520, 40, 560, 80)
(0, 80, 40, 120)
(40, 80, 80, 120)
(80, 80, 120, 120)
(120, 80, 160, 120)
(160, 80, 200, 120)
(200, 80, 240, 120)
(240, 80, 280, 120)
(280, 80, 320, 120)
(320, 80, 360, 120)
(360, 80, 400, 120)
(400, 80, 440, 120)
(440, 80, 480, 120)
(480, 80, 520, 120)
(520, 80, 560, 120)
(0, 120, 40, 160)
(40, 120, 80, 160)
(80, 120, 120, 160)
(120, 120, 160, 160)
(160, 120, 200, 160)
(200, 120, 240, 160)
(240, 120, 280, 160)
(280, 120, 320, 160)
(320, 120, 360, 160)
(360, 120, 400, 160)
(400, 120, 440, 160)
(440, 120, 480, 160)
(480, 120, 520, 160)
(520, 120, 560, 160)
(0, 160, 40, 200)
(40, 160, 80, 200)
(80, 160, 120, 200)
(120, 160, 160, 200)
(160, 160, 200, 200)
(200, 160, 240, 200)
(240, 160, 280, 200)
(280, 160, 320, 200)
(320, 160, 360, 200)
(360, 160, 400, 200)
(400, 160, 440, 200)
(440, 160, 480, 200)
(480, 160, 520, 200)
(520, 160, 560, 200)
(0, 200, 40, 240)
(40, 200, 80, 240)
(80, 200, 120, 240)
(120, 200, 160, 240)
(160, 200, 200, 240)
(200, 200, 240, 240)
(240, 200, 280, 240)
(280, 200, 320, 240)
(320, 200, 360, 240)
(360, 200, 400, 240)
(400, 200, 440, 240)
(440, 200, 480, 240)
(480, 200, 520, 240)
(520, 200, 560, 240)
(0, 240, 40, 280)
(40, 240, 80, 280)
(80, 240, 120, 280)
(120, 240, 160, 280)
(160, 240, 200, 280)
(200, 240, 240, 280)
(240, 240, 280, 280)
(280, 240, 320, 280)
(320, 240, 360, 280)
(360, 240, 400, 280)
(400, 240, 440, 280)
(440, 240, 480, 280)
(480, 240, 520, 280)
(520, 240, 560, 280)
(0, 280, 40, 320)
(40, 280, 80, 320)
(80, 280, 120, 320)
(120, 280, 160, 320)
(160, 280, 200, 320)
(200, 280, 240, 320)
(240, 280, 280, 320)
(280, 280, 320, 320)
(320, 280, 360, 320)
(360, 280, 400, 320)
(400, 280, 440, 320)
(440, 280, 480, 320)
(480, 280, 520, 320)
(520, 280, 560, 320)

再看同級目錄下有兩張圖片,1.png與2.png

    

在openpic_o目錄,就能夠看到裁剪後的小方塊,方塊大小就是原小方塊大小 40*40像素的.

在openpic目錄, 看到的是在小方塊的基礎之上,又裁剪了一次,把小方塊的上 下 左 右 各邊框都裁剪了5個像素,爲何要這樣操做呢? 由於40*40px的圖片即便flash引用的相同圖片,但因分辨率問題,PIL裁剪後也有可能出現毛邊,爲了排除這個干擾才又裁剪了一次.以下圖

     

 

關於座標

有沒有發現上面代碼中有這麼兩行代碼

x_play = x + 1
y_play = abs(8 - y)

爲何是這樣呢?咱們先來看下圖, 下圖是咱們平時用的平面二位直角座標,是從中心點(0,0)開始, 橫向左爲負數,橫向右爲正數,縱軸下爲負數,縱軸上爲正數,可是電腦上的座標都是左上角(0,0)爲座標系,爲了便於理解,咱們就把Y軸進倒置,由於高度有8個小方塊,因此就是8-y的絕對值.至於x軸,由於從左到右也是依次增大,so,X軸不變.

但爲何又是  x_play = x + 1  先看生成的小方塊,小方塊的名稱是從 x1y1, x1y2,x1y3....x2y1...... 也就是從1開始,若是(1,1)就是起始的座標點的話,四個邊框的點座標就只能直線鏈接或從裏面消除點後再經過裏面點進行迂迴鏈接,這樣便一開始就出問題了,由於四個邊框的點是能夠經過兩個拐角鏈接的,換句話說,四個邊框的外圍還有一圈空的點,這些空的點座標是能夠聯通的,因此才能構成邊框上的點聯通.也就有了(0,1),(1,0),(0,2),(0,3)...等座標了,這裏的x+1,也就是直接初試從(1,1)開始,給空的座標點作預留.看下圖.

咱們定義link_ponts 爲可聯通的點座標

link_points = []  # 可聯通點座標
for x in range(0, 14 + 2):  # 網格個數+2  能夠認爲網格小方塊的外圍有一圈空的方塊
    for y in range(0, 8 + 2):  # 網格個數+2
        load_name = "x" + str(x) + "y" + str(y)
        if not os.path.exists("./openpic/" + load_name + '.png'):
            link_points.append(load_name)

print(link_points)

打印link_points

['x0y0', 'x0y1', 'x0y2', 'x0y3', 'x0y4', 'x0y5', 'x0y6', 'x0y7', 'x0y8', 'x0y9', 'x1y0', 'x1y9', 'x2y0', 'x2y9', 'x3y0', 'x3y9', 'x4y0', 'x4y9', 'x5y0', 'x5y9', 'x6y0', 'x6y9', 'x7y0', 'x7y9', 'x8y0', 'x8y9', 'x9y0', 'x9y9', 'x10y0', 'x10y9', 'x11y0', 'x11y9', 'x12y0', 'x12y9', 'x13y0', 'x13y9', 'x14y0', 'x14y9', 'x15y0', 'x15y1', 'x15y2', 'x15y3', 'x15y4', 'x15y5', 'x15y6', 'x15y7', 'x15y8', 'x15y9']

 

 

類似圖片分組

這一步驟, 就要提到昨天寫的blog

Python OpenCV 圖像相識度對比

圖片的類似度無論知乎大神用的是

cv2.subtract

仍是

numpy.subtract

我這裏處理的小方塊都有問題,很簡單的(x1y2.png),(x2y4.png), (x2y6.png)識別就有問題.說x1y2.png與x2y6.png不相等,我就笑了.

(x1y2.png)    (x2y4.png)     (x2y6.png)   

後來我就用了openCV圖像的類似度對比,哈希算法也行,灰度RGB通道直方圖算法匹配也行, 可是感受通道直方圖計算出來的值更準確, 只能說沒有更好的,只有更適合的.

可是有個問題,通道直方圖計算匹配的時候賊慢賊慢的,個人電腦計算的時候執行了8秒多.

def has_group_list(list_group, m2):
    ret = False
    if list_group:
        for klist in list_group:
            if m2 in klist:
                ret = True
                break
    return ret


# 經過獲得每一個通道的直方圖來計算類似度
def classify_hist_with_split(image1, image2, size=(256, 256)):
    # 將圖像resize後,分離爲三個通道,再計算每一個通道的類似值
    image1 = cv2.resize(image1, size)
    image2 = cv2.resize(image2, size)
    sub_image1 = cv2.split(image1)
    sub_image2 = cv2.split(image2)
    sub_data = 0
    for im1, im2 in zip(sub_image1, sub_image2):
        sub_data += calculate(im1, im2)
    sub_data = sub_data / 3
    return sub_data


# 計算單通道的直方圖的類似值
def calculate(image1, image2):
    hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
    hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
    # 計算直方圖的重合度
    degree = 0
    for i in range(len(hist1)):
        if hist1[i] != hist2[i]:
            degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
        else:
            degree = degree + 1
    degree = degree / len(hist1)
    return degree


list_group = []


def get_list_group():
    tmpgroup = []
    tmpIJ = ''
    for i in range(1, 15):
        for j in range(1, 9):
            # ij 座標1
            m1 = 'x' + str(i) + 'y' + str(j)
            for n in range(1, 15):
                for m in range(1, 9):
                    # nm 座標2
                    m2 = 'x' + str(n) + 'y' + str(m)
                    img1 = cv2.imread('openpic/' + m1 + ".png")
                    img2 = cv2.imread('openpic/' + m2 + ".png")
                    hn = classify_hist_with_split(img1, img2)

                    if hn > 0.80:
                        if tmpIJ != m1:
                            if tmpgroup:
                                list_group.append(tmpgroup)
                            tmpgroup = []
                            print(tmpIJ)
                            print(list_group)
                            tmpIJ = m1
                        else:
                            if not has_group_list(list_group, m2):
                                if m1 not in tmpgroup:
                                    tmpgroup.append(m1)
                                tmpgroup.append(m2)


get_list_group()
print(list_group)

打印list_group

[['x1y1', 'x1y3', 'x5y6', 'x8y4', 'x13y8', 'x14y8'], ['x1y2', 'x1y8', 'x2y4', 'x3y8', 'x6y8', 'x7y2'], ['x1y4', 'x11y1'], ['x1y5', 'x4y7', 'x6y6', 'x11y3', 'x11y5', 'x12y8'], ['x1y6', 'x3y2', 'x4y8', 'x8y1', 'x9y7', 'x10y4', 'x11y4', 'x14y3'], ['x1y7', 'x2y1', 'x3y3', 'x4y3', 'x6y4', 'x8y5'], ['x2y2', 'x2y3', 'x7y8', 'x12y2', 'x13y1', 'x14y5'], ['x2y5', 'x6y3', 'x6y5', 'x8y8'], ['x2y6', 'x6y2'], ['x2y7', 'x5y5'], ['x2y8', 'x3y6', 'x12y5', 'x14y4'], ['x3y1', 'x5y1', 'x9y1', 'x10y3', 'x13y2', 'x13y5'], ['x3y4', 'x7y3', 'x7y4', 'x10y2', 'x11y8', 'x12y6', 'x13y4', 'x13y7', 'x14y2', 'x14y6'], ['x3y5', 'x4y4', 'x7y6', 'x9y8'], ['x3y7', 'x14y1'], ['x4y1', 'x9y2', 'x10y8', 'x11y2'], ['x4y2', 'x4y6', 'x12y3', 'x12y4'], ['x4y5', 'x5y7', 'x6y1', 'x9y5'], ['x5y2', 'x13y6'], ['x5y3', 'x7y1'], ['x5y4', 'x8y2'], ['x5y8', 'x9y3', 'x10y5', 'x11y7', 'x12y7', 'x13y3'], ['x6y7', 'x9y6'], ['x7y5', 'x8y3', 'x9y4', 'x10y7'], ['x7y7', 'x11y6'], ['x8y6', 'x12y1'], ['x8y7', 'x10y1'], ['x10y6', 'x14y7']]

 問我爲何視頻中沒有等待8秒時間, 笑笑笑..... 

看代碼:

""" list_group 寫入文件 """
fileObject = open('data_list_group.txt', 'w')
ret = json.dump(link_points,fileObject)
fileObject.close()




""" 讀取  list_group """
f= open('data_list_group.txt','r')
list_group=[]
for i in f.readlines():
    list_group.append(i.strip('\n'))
print(list_group)

 

模擬鼠標點擊

如今算法有了, 小方塊座標有了, 可聯通的link_points列表有了,類似圖片分組list_group有了,就差模擬鼠標點擊了,pyautogui登場

PyAutoGUI 簡介

畫個方形我也會:

import pyautogui

for i in range(3):
      pyautogui.moveTo(300, 300, duration=0.25)
      pyautogui.moveTo(400, 300, duration=0.25)
      pyautogui.moveTo(400, 400, duration=0.25)
      pyautogui.moveTo(300, 400, duration=0.25)

OK! 例子在手, 說走就走!

 

 

完整代碼

config.py

# -*- coding:utf-8 -*-
"""常量配置文件"""

PLAYER = 'gnash'  # player

SWF_PATH = 'play.swf'

GAME_Width = 750  # flash 實際 750*450

GAME_HEIGHT = 450

PLAYER_JK = '-j' + str(GAME_Width) + ' -k' + str(GAME_HEIGHT)  # jk 窗口寬高,參考 gnash -h

PLAYER_X = 100  # XY 窗口 距離(0.0)座標的位置長度與高度

PLAYER_Y = 100  # XY 窗口 距離(0.0)座標的位置長度與高度

GRID_SIZE = 40  # 網格大小

GRID_X_NUM = 14  # 網格X軸個數

GRID_Y_NUM = 8  # 網格Y軸個數

X11_HEIGHT = 30  # Ubuntu X11 窗體 title欄高度

PLAY_BTN = (315, 380)  # player flash 開始遊戲按鈕座標

CENTER_LEFT = 80  # 中心網格左側距離

CENTER_TOP = 145  # 中心網格頂部高度距離

GRID_DIR = "img/"  # 網格小方塊目錄

GRID_MIC_DIR = 'img_micro/'  # 裁剪後小方塊保存目錄

GRID_ZOOM = 5  # 裁剪小方塊 上 下 左 右 5px

play.swf

回到本頁頂部,本身手動玩哈.

 

screenshot.py

# -*- coding:utf-8 -*-
import pyscreenshot as ImageGrab
from PIL import Image
import time, os
from config import *

class screenshot_class:
    """
    截取 player 窗體圖片在init中
    """

    def __init__(self):
        im = ImageGrab.grab([PLAYER_X, PLAYER_Y, PLAYER_X + GAME_Width,
                             PLAYER_Y + GAME_HEIGHT + X11_HEIGHT])  # X1,Y1,X2,Y2  (X2-X1=750,Y2-Y1=450+30)
        im.save("1.png")
        print("截取player圖片完成")

        time.sleep(1)
        im = Image.open('1.png')
        im = im.crop((CENTER_LEFT, CENTER_TOP, GRID_X_NUM * GRID_SIZE + CENTER_LEFT,
                      GRID_Y_NUM * GRID_SIZE + CENTER_TOP))  # 80,145,40*14+80,40*8+155
        im.save('2.png')
        print("裁剪截圖處理完成")

    def save_grid(self):
        """
        保存小方塊
        """
        print("cut cut cut .......")
        if not os.path.exists(GRID_DIR):
            os.mkdir(GRID_DIR)  # 小方塊存放目錄
        if not os.path.exists(GRID_MIC_DIR):
            os.mkdir(GRID_MIC_DIR)  # 裁剪後的小方塊存放目錄
        img = Image.open('2.png')
        for y in range(0, GRID_Y_NUM):  # height高
            for x in range(0, GRID_X_NUM):  # width長
                r = (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE + GRID_SIZE * x, GRID_SIZE + GRID_SIZE * y)
                crop_img = img.crop(r)
                x_play = x + 1
                y_play = abs(GRID_Y_NUM - y)  # 原座標是從左上開始計算Y軸,將其轉換爲從(0,0) 開始
                crop_img.save(GRID_DIR + "x" + str(x_play) + "y" + str(y_play) + '.png')
                crop_img = crop_img.crop((GRID_ZOOM, GRID_ZOOM, GRID_SIZE - GRID_ZOOM,
                                          GRID_SIZE - GRID_ZOOM))  # 生成的40*40 左右上下裁剪5px (5, 5, 35, 35)
                crop_img.save(GRID_MIC_DIR + "x" + str(x_play) + "y" + str(y_play) + '.png')
        time.sleep(1)
        print("cut 小方塊完成")


if __name__ == '__main__':
    print("from img")
    screenshot_class()

 

 

autolinlink.py

# -*- coding:utf-8 -*-
import pyautogui
from itertools import combinations
import cv2
import os, time
from config import *
import screenshot


class autolinlink:
    """autolinlink"""

    @staticmethod
    def open_swf():
        """打開player"""
        os.popen(PLAYER + " " + SWF_PATH + " " + PLAYER_JK + " " + "-X" + str(PLAYER_X) + " -Y" + str(PLAYER_Y))
        print("打開player完成")

    @staticmethod
    def play_btn():
        """play btn 開始按鈕"""
        pyautogui.moveTo(PLAYER_X + PLAY_BTN[0], PLAYER_Y + PLAY_BTN[1], duration=3.6)  # 移動鼠標至play位置
        pyautogui.click()
        print("已進入遊戲")

    def screen_shot(self):
        time.sleep(0.2)  # 休眠0.2s 等待background後的網格小方塊加載
        screenshot.screenshot_class()
        screenshot.screenshot_class.save_grid(self)

    def get_points(self):
        """
        獲取 可聯通點 座標
        :return: list_points
        """
        print("獲取可聯通點座標")
        link_points = []  # 可聯通點座標
        for x in range(0, GRID_X_NUM + 2):  # 網格個數+2  能夠認爲網格小方塊的外圍有一圈空的方塊
            for y in range(0, GRID_Y_NUM + 2):  # 網格個數+2
                load_name = "x" + str(x) + "y" + str(y)
                if not os.path.exists(GRID_MIC_DIR + load_name + '.png'):
                    link_points.append(load_name)

        return link_points

    def get_group(self):
        """
        獲取 類似圖片group
        :return: list_group
        """
        print("計算圖片類似度,大概持續8秒左右.....")
        list_group = []

        # f = open('data_list_group.txt')
        # list_group = json.load(f)
        # return  list_group

        tmp_group = []  # 每一個相同小方塊的group分組
        tmpIJ = ''  # 每一個相同座標分組中,第一個座標1,以後循環與該點匹配判斷是否在同一個分組
        for i in range(1, GRID_X_NUM + 1):  # GRID_X_NUM+1  [1-14]區間
            for j in range(1, GRID_Y_NUM + 1):
                # ij 座標1
                m1 = 'x' + str(i) + 'y' + str(j)
                for n in range(1, GRID_X_NUM + 1):
                    for m in range(1, GRID_Y_NUM + 1):
                        # nm 座標2
                        m2 = 'x' + str(n) + 'y' + str(m)
                        img1 = cv2.imread("./" + GRID_MIC_DIR + m1 + ".png")
                        img2 = cv2.imread(GRID_MIC_DIR + m2 + ".png")
                        hn = self.classify_hist_with_split(img1, img2)
                        if hn > 0.80:  # 大於0.8認爲小方塊相同
                            if tmpIJ != m1:
                                if tmp_group:
                                    list_group.append(tmp_group)
                                tmp_group = []
                                tmpIJ = m1
                            else:
                                if not self.has_group_list(list_group, m2):
                                    if m1 not in tmp_group:
                                        tmp_group.append(m1)
                                    tmp_group.append(m2)
        return list_group

    def classify_hist_with_split(self, image1, image2, size=(256, 256)):
        """經過獲得每一個通道的直方圖來計算類似度"""
        # 將圖像resize後,分離爲三個通道,再計算每一個通道的類似值
        image1 = cv2.resize(image1, size)
        image2 = cv2.resize(image2, size)
        sub_image1 = cv2.split(image1)
        sub_image2 = cv2.split(image2)
        sub_data = 0
        for im1, im2 in zip(sub_image1, sub_image2):
            sub_data += self.calculate(im1, im2)
        sub_data = sub_data / 3
        return sub_data

    def calculate(self, image1, image2):
        """計算單通道的直方圖的類似值"""
        hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
        hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
        # 計算直方圖的重合度
        degree = 0
        for i in range(len(hist1)):
            if hist1[i] != hist2[i]:
                degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
            else:
                degree = degree + 1
        degree = degree / len(hist1)
        return degree

    def has_group_list(self, list_group, p):
        """判斷當前點座標在不在group中"""
        ret = False
        if list_group:
            for klist in list_group:
                if p in klist:
                    ret = True
                    break
        return ret

    def run(self, list_group, link_points, tmp_points_list=[]):
        """執行操做"""
        total = (GRID_X_NUM + 2) * (GRID_Y_NUM + 2)
        sum_link_points = len(link_points)
        haslink = False
        if link_points != total:
            for k in list_group:  # k=(x2y1,x2y4,x12y12,x90y90)
                for i in combinations(k, 2):  # permutations 排列重複組合,combinations 組合不重複
                    if i[0] not in link_points and i[1] not in link_points:  # 排除 當前k () 已在link_points中
                        llk = self.linlink(i, link_points)
                        if llk:
                            tmp_points_list.append(i[0])
                            tmp_points_list.append(i[1])

                            if i == ('x5y4', 'x8y2'):
                                time.sleep(1)  # flash "加時"魔法干擾,臨時sleep 1s

                            time.sleep(0.5)
                            self.click(i[0], i[1])
                            haslink = True
                            break

            if sum_link_points != total and haslink == False:
                print('剩餘點沒法消除')
            elif sum_link_points == total:
                print('完成')
            else:
                print('再來一次')
                list_group, link_points, tmp_points_list = self.update_group(list_group, link_points, tmp_points_list)
                return self.run(list_group, link_points, tmp_points_list)

    def update_group(self, list_group, link_points, tmp_points_list):
        link_points += tmp_points_list
        # 刪除list_group 已鏈接數據
        for i in list_group:
            for t in tmp_points_list:
                if t in i:
                    i.remove(t)
        tmp_points_list = []

        return list_group, link_points, tmp_points_list

    def linlink(self, xy, link_points):
        ret = False
        # 判斷點是否能聯通
        x1 = xy[0].split('y')[0]
        y1 = 'y' + xy[0].split('y')[1]
        x2 = xy[1].split('y')[0]
        y2 = 'y' + xy[1].split('y')[1]
        result = self.lineCase(xy, link_points, x1, y1, x2, y2)

        if result:
            print(xy, '直線鏈接可聯通')
            ret = True
        else:
            result = self.onceCorner(xy, link_points, x1, y1, x2, y2)  # 一次拐角
            if result:
                print(xy, '一次拐角可聯通')
                ret = True
            else:
                result = self.doubleCorner(xy, link_points, x1, y1, x2, y2)  # 兩個拐角
                if result:
                    print(xy, '兩次拐角可聯通')
                    ret = True
                else:
                    # print(xy, '兩次拐角貌似不可聯通')
                    pass
        return ret

    # 兩次拐角
    def doubleCorner(self, xy, link_points, x1, y1, x2, y2):
        ret = False
        first_match = 1
        for px in range(GRID_X_NUM + 2):  # (GRID_X_NUM + 2) * (GRID_Y_NUM + 2)
            for py in range(GRID_Y_NUM + 2):
                tmpx = 'x' + str(px)
                tmpy = 'y' + str(py)
                # 以第一個點座標尋找拐點,
                if (tmpx == x1 and tmpy != y1) or (tmpy == y1 and tmpx != x1):  # 第一個點座標四個方向但除了本身
                    # 任意拐點,(px,py)與第一個點座標爲直線狀況,與第二個點爲一次拐角狀況
                    lineC = self.lineCase(xy, link_points, x1, y1, tmpx, tmpy, corner=True)
                    onceC = self.onceCorner(xy, link_points, tmpx, tmpy, x2, y2)
                    if lineC and onceC and first_match == 1:
                        first_match += 1
                        ret = True
                        break

        return ret

    # 一次拐角
    def onceCorner(self, xy, link_points, x1, y1, x2, y2):
        ret = False
        C = (x2, y1)  # 用 第一個的Y,第二個X  # C,D 爲中間(p,Tmp) 的過渡點
        D = (x1, y2)  # 用 第一個X,第二個Y
        # C點分別與第一個點 與第二點 聯通
        ClineCaseY = self.lineCase(xy, link_points, x1, y1, C[0], C[1], True)  # 即 (x1,y1,x2,y1)
        ClineCaseX = self.lineCase(xy, link_points, x2, y2, C[0], C[1], True)  # 即 (x2,y2,x2,y1)
        if ClineCaseX and ClineCaseY:  # C點與第一個點的X軸聯通,與第二個點的Y軸聯通
            ret = True
        DlineCaseX = self.lineCase(xy, link_points, x1, y1, D[0], D[1], True)  # 即 (x1,y1,x1,y2)
        DlineCaseY = self.lineCase(xy, link_points, x2, y2, D[0], D[1], True)  # 即 (x2,y2,x1,y2)
        if DlineCaseX and DlineCaseY:  # D點與第一個點的X軸聯通,與第二個點的Y軸聯通
            ret = True

        return ret

    def lineCase(self, xy, link_points, x1, y1, x2, y2, corner=False):
        ret = False
        if x1 == x2:  # x縱軸 相同
            xmin = min(int(y1[1:]), int(y2[1:]))  # 字符串不能進行max min比較    int(max(x1,x2)[1:]) # x1=9 x2=13
            xmax = max(int(y1[1:]), int(y2[1:]))
            point_num = xmax - xmin - 1
            if point_num == 0 and corner == False:
                ret = True
            elif point_num == 0 and corner:  # 相連 and 來自拐角
                if x2 + y2 in link_points:  # 判斷後(xy,tmp) tmp中間點 點是否爲空
                    ret = True
            else:

                point_num_able = 0
                for i in range(xmin + 1, xmax):
                    if x1 + "y" + str(i) in link_points:
                        point_num_able += 1

                if point_num == point_num_able and point_num > 0 and ((x2 + y2 in link_points and corner) or corner == False):  # 可聯通點的個數等於同軸points點的個數
                    ret = True

        if y1 == y2:

            ymin = min(int(x1[1:]), int(x2[1:]))  # 字符串不能進行max min比較    int(max(x1,x2)[1:]) # x1=9 x2=13
            ymax = max(int(x1[1:]), int(x2[1:]))
            point_num = ymax - ymin - 1

            if point_num == 0 and corner == False:
                ret = True
            elif point_num == 0 and corner:
                if x2 + y2 in link_points:
                    ret = True
            else:

                point_num_able = 0
                for i in range(ymin + 1, ymax):
                    if "x" + str(i) + y1 in link_points:
                        point_num_able += 1

                if (point_num == point_num_able) and point_num > 0 and (( x2 + y2 in link_points and corner) or corner == False):  # 可聯通點的個數等於同軸points點的個數 # x2+y2 in link_points and corner做爲拐角的點也必須爲空
                    ret = True
        return ret

    def click(self, p1, p2):
        """模擬鼠標點擊"""  # ('x4y8', 'x8y1')
        # 設置x(0,0),y(0,0)座標
        init_pint = (PLAYER_X + CENTER_LEFT - GRID_SIZE + 20, CENTER_TOP + X11_HEIGHT + PLAYER_Y + ((GRID_Y_NUM) * GRID_SIZE) - 20)  # x=80-40    , y= 145+30+9*40  x+20 與y-20可讓鼠標移動到小方塊的中心

        # x軸 相加, Y軸 相減
        pX1 = init_pint[0] + int(p1.split('y')[0][1:]) * GRID_SIZE
        pY1 = init_pint[1] - int(p1.split('y')[1]) * GRID_SIZE

        pX2 = init_pint[0] + int(p2.split('y')[0][1:]) * GRID_SIZE
        pY2 = init_pint[1] - int(p2.split('y')[1]) * GRID_SIZE

        pyautogui.moveTo(pX1, pY1, duration=0.05)
        pyautogui.click()

        pyautogui.moveTo(pX2, pY2, duration=0.05)
        pyautogui.click()


if __name__ == '__main__':
    app = autolinlink()
    app.open_swf()
    app.play_btn()
    app.screen_shot()
    link_points = app.get_points()
    list_group = app.get_group()
    if link_points and list_group:
        app.run(list_group, link_points)

 

源文件:

 http://u.163.com/Bhfyp9nm   提取碼: LkYikiOz

相關文章
相關標籤/搜索