基于Anaconda 写炉石传说脚本

请不要转载,如有疑问请私,如侵权,请私可删.
联系方式 Q 537406976

目录

需要的库

random库 是实现引入随机位置.
os库 实现关闭炉石传说进程.
multiprocessing库 实现多进程.
cv2库 实现图片模板匹配.
pyautogui 实现模拟鼠标键盘.

import win32gui,time,pyautogui,cv2
import os
from PIL import Image
import numpy as np
from openpyxl import load_workbook
from multiprocessing import Process,Queue
from random import randint,choice

截图与查找

为实现相关功能,需实现截屏与图像查找.

截屏

get_screen(img2,region) img2为截屏后保存的地方,region为截屏区域.

# 类 Screen 属性
def get_screen(self,img2='scree.jpg',region=(0,0,1920,1080)):
        '''
        截屏
        ----------
        img2 : 图片存储路径. 默认'scree.jpg'.
        region : (x1,y1,width,hight) 截取矩形左顶点(x1,y1) 与矩形width,hight
        '''
        if region=='default':
            region = (0,0,1920,1080)
        elif len(region)<4:
            raise ValueError('region值错误 截取矩形左顶点(x1,y1) 与矩形width,hight')
        pyautogui.screenshot(img2,region=region)

图像匹配

模板指在图片中要查找的图像.

is_sim :
K1 不为True时,表示输出匹配值.
N 不为True时,表示不截屏,这时需要img2参数表示图片.
T 表示利用cv2.imread()读取时灰度读取或彩色读取.
thresh 不为None,表示二值化匹配,经过数千次试验后,仅用于识别手牌费用.
mul 匹配多模板,比如用于识别费用,在不使用机器学习的情况下,必须匹配多模板,然后返回匹配值大的,同时该最大值还是要大于一定值
mul_I 即为多个模板.

# 类Screen
def is_sim(self,img1='',img2='default',s=0.75,K1=True,N=True,region='default',mul=False,mul_I=[],thresh=None,T=-1):
        '''

        判断img2里是否有img1
        进行多图像匹配时,需要参数mul,img2,mul_I,region
        ----------
        img1 :  The default is ''.
        img2 :  默认为'img1_1',格式与img1相同.
        s : 匹配最小值. The default is 0.75.
        K1 : 用于匹配值较小时使用,仅在测试时设置为False.
        N : 不截屏,而是使用已有图像,默认截屏.
        region : 传给get_screen.
        mul : 截图,匹配多图像,返回每个对象匹配值;
                若thresh不为None,则先二值化再匹配.
        mul_I : 图像中含有对象,list.
        T : 0表示灰度读取,-1表示彩色读取.传给cv2.imread().
        thresh : 不为None,则表示二值化后匹配,目前用于识别费用.不为None则T应该为0.
        
        '''
        if mul:
            if N:
                self.get_screen(img2,region=region)
            im2 = cv2.imread(img2,T)
            if thresh!=None:
                assert T==0
                im2 = cv2.adaptiveThreshold(im2,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
            M = []
            for i in mul_I:
                im1 = cv2.imread(i,T)
                if thresh!=None:
                    im1 = cv2.adaptiveThreshold(im1,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
                res = cv2.matchTemplate(im2,im1,cv2.TM_CCOEFF_NORMED)
                M.append(res.max())
            return M
        im1 = cv2.imread(img1,T)
        if img2=='default':
            img2 = img1[:img1.index('.')]+'_1'+img1[img1.index('.'):]
        if N:
            self.get_screen(img2,region=region)
        im2 = cv2.imread(img2,T)
        res = cv2.matchTemplate(im2,im1,cv2.TM_CCOEFF_NORMED)
        if not K1:
            return res.max()
        if res.max()<s:
            return False
        return True

获取模板的位置

用于实现之后的点击.
一般使用只需img1参数.这时会在没有识别该模板时,等待直到识别到该模板.
M 比如由于识别嘲讽时不一定场上有嘲讽,返回100,100.

# 类Screen
def get_position(self,img1='',img2='default',s=0.75,M=True,K1=True,N=True,region='default',mul=False,mul_I=[],T=-1,thresh=None):
        '''
        多图像查找位置时,不支持等待.
        ----------
        img1 : 路径名,需要在屏幕上查找的图片
        img2 : 截图时临时存放位置. 默认为'img1_1',格式与img1相同.
        s : 不大于1的正数,传递给is_sim函数.
        M : BOOL, optional
            为False时,未查找到返回(100,100)点. The default is True.
        K1 : False 取消阈值 默认True.
        N  :    False表示不截图,直接进行图像对比, 默认True.
        region : 传给get_screen.
        T : 0表示灰度读取,-1表示彩色读取.传给cv2.imread().
        thresh : 不为None,则表示二值化后匹配,目前用于识别费用.
        mul : 截图,匹配多图像,返回每个对象匹配值.
        mul_I : 图像中含有对象,list.
        Returns
        -------
        (X,Y) 图像中心点坐标
        '''
        if mul:
            M = self.is_sim(mul=mul,mul_I=mul_I,img2=img2,region=region,T=T,thresh=thresh)
            M1 = [M[0],M[1],M[3],M[4]]
            a = max(M1)
            if a>s:
                img1 = mul_I[M.index(a)]
                d = Image.open(img1).size
                im = cv2.imread(img1,T)
                
                im2 = cv2.imread(img2,T)
                
                res = cv2.matchTemplate(im2,im,cv2.TM_CCOEFF_NORMED)
                loc = np.where(res==res.max())
        
                X = int(loc[1]+d[0]/2)
                Y = int(loc[0]+d[1]/2)
                T = 'left' # 左上角
                if a==M[-1] or a==M[-2]:
                    T = 'right' # 右上角
                if region != 'default':
                    X += region[0]
                    Y += region[1]
                    return (X,Y,T)
            if M[2]>0.85:
                img1 = mul_I[2]
                d = Image.open(img1).size
                im = cv2.imread(img1,T)
                im2 = cv2.imread(img2,T)
                res = cv2.matchTemplate(im2,im,cv2.TM_CCOEFF_NORMED)
                loc = np.where(res==res.max())
        
                X = int(loc[1]+d[0]/2)
                Y = int(loc[0]+d[1]/2)
                T = 'left' # 左上角
                if region != 'default':
                    X += region[0]
                    Y += region[1]
                    return (X,Y,T)
            return 100,100,None
        if img2=='default':
            img2 = img1[:img1.index('.')]+'_1'+img1[img1.index('.'):]
        while K1 and self.is_sim(img1=img1,img2=img2,s=s,K1=K1,N=N,region=region,T=T,thresh=thresh) is not True:
            if M is True :
                continue
            else:
                return (100,100)
        if N:
            self.get_screen(img2,region)
        d = Image.open(img1).size

        im = cv2.imread(img1,T)
        if thresh!=None:
            im = cv2.adaptiveThreshold(im,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
        im2 = cv2.imread(img2,T)
        if thresh!=None:
            im2 = cv2.adaptiveThreshold(im2,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
        res = cv2.matchTemplate(im2,im,cv2.TM_CCOEFF_NORMED)
        loc = np.where(res==res.max())
        
        X = int(loc[1]+d[0]/2)
        Y = int(loc[0]+d[1]/2)
        if region != 'default':
            X += region[0]
            Y += region[1]
        return (X,Y)

实现鼠标与键盘模拟

优化pyautogui内的功能,同时出牌时是drag,一个位置托到另一个位置.

# 类Screen
def click(self,x,y,t=20,button='primary'):
        '''
        点击给定点附近,所有非强制要求点应使用该方法
        -------
        x,y : 位置(x,y)
        t : 默认20
        button : 默认'primary',可设为'left'/'right'/'middle'
        '''
        pyautogui.click(x+randint(-t, t),y+randint(-t,t),button=button)
    
    def move(self,x,y,t=20):
        '''
        移到给定点附近
        '''
        pyautogui.moveTo(x+randint(-t, t),y+randint(-t, t))
    
    def drag(self,x0,y0,x1,y1,t=5):
        '''
        从当前位置(x0,y0)拖拽到指定位置(x1,y1)
        '''
        def mousedown(x,y,t):
            pyautogui.mouseDown(x+randint(-t, t),y+randint(-t, t))
        def mouseup(x,y,t):
            pyautogui.mouseUp(x+randint(-t, t),y+randint(-t, t))

        mousedown(x0, y0,t)
        self.move(x1,y1,t)
        time.sleep(0.5)
        mouseup(x1, y1,t)

该游戏的特化

上面是几乎所有游戏都需要的,下面是基于该游戏的优化代码以及过程.

己方随从的位置

# 全局变量,及D前面并没有空格
D = {'one':(926,560,71,86),'two':(830,560,40,88),
     'three':(760,560,40,81),'four':(670,560,45,73),
     'five':(610,560,40,75),'six':(535,560,40,78),
     'seven':(465,560,40,73)}

重连

由于潜在的代码异常可能使得游戏以及掉线,这时应先关闭游戏再重连.假设执行该函数chl时,游戏进程已结束.
先回到桌面 : 按’windows’键加’d’
再识别暴雪战网的位置.默认点击后会自动登录账号.
再识别进入游戏的位置,这时注意请确保进入后的是炉石窗口
之后再识别炉石传说的对战模式.

Sc = Screen()

# 函数
def chl():
    time.sleep(10) # 处理其它进程
    pyautogui.hotkey('winleft','d')
    time.sleep(1)
    x,y = Sc.get_position('baoxue.jpg')
    time.sleep(1)
    pyautogui.doubleClick(x,y)
    time.sleep(5)     
    x,y = Sc.get_position('jinru.jpg')
    time.sleep(1)
    pyautogui.doubleClick(x,y)     
    time.sleep(10)
    while True:
        if Sc.is_sim('duizhan.jpg'):
            break
        if Sc.is_sim('chlchg.jpg'): # 避免重连,实际用处几乎不大
            return 'chl'
    x,y = Sc.get_position('duizhan.jpg')  
    pyautogui.doubleClick(x,y) 
    time.sleep(3)

从这之后是另一个类GameAssist
它继承上一个类Screen.

初始化

X_D是相应手牌数各手牌对应位置.
fy是模板费用列表
T是特殊牌(及不是直接托到敌方英雄处就能释放的牌,以及使用后需点击其它位置(比如有发现一张牌等)建议不要携带特殊牌.

class GameAssist(Screen):   
    def __init__(self,classname,wdname):
        """初始化"""
        super().__init__()
        # 获取窗口句柄
        self.hwnd = win32gui.FindWindow(classname,wdname)
        if not self.hwnd:
            self.Chonglian()
        #窗口显示最前化
        self.SetAsForegroundWindow()
        # 使得分辨率为1920x1080
        self.get_screen()
        
        # 以下为一些参数信息,之后优化
        self.X_D = {8:[667,719,769,825,902,997,1059,1150],
                    7:[650,750,809,874,958,1038,1119],
                    6:[675,794,856,924,1022,1124],
                    5:[673,825,928,1041,1110],
                    4:[732,849,996,1108],
                    3:[805,897,1097],
                    2:[867,978],1:[889],0:[]}
        self.fy = []
        
        for m in range(11):
            img = f'shoupai\\{m}.jpg'
            self.fy.append(img)
        self.T = {} # 特殊牌

重连机制

通过查找窗口句柄来确定是否需要重连

# 重连机制,通过查找窗口句柄确认
# 类GameAssist
    def Chonglian(self,q=Queue()):
        '''
        重连机制,找不到窗口则回到桌面,打开应用
        -------
        q, 如果未找到窗口,不为空
        '''
        self.hwnd = win32gui.FindWindow(classname,wdname)
        if not self.hwnd:
            q.put('error')
            A = chl() # 重连
            if A=='chl': # 表示返回对局(数千次运行发现可能性极低)
                self.renshu()
            self.hwnd = win32gui.FindWindow(classname,wdname)
        else:
            A = self.get_position('ljzhd.jpg',M=False)
            # 有时会有连接中断,这时需退出
            if A[0]==100:
                pass
            else:
                self.tuichu()
            

窗口置前

# 类GameAssist
def SetAsForegroundWindow(self):
        win32gui.SetForegroundWindow(self.hwnd)

表示已进入对局

def start(self,Q):
        '''
        Q : 用来将数据传出到其它进程
        '''
        Q.put({'num_h':0,'start_T':time.time()}) # 回合数,与开始时间
        pyautogui.moveTo(800,200)
        while self.is_sim(img1='in_game\\duishouxuanp.jpg'):
            time.sleep(0.5) # 对手选牌
        pyautogui.moveTo(700,400)
        time.sleep(5)
        self.in_game(Q) # 开始对局

退出

def tuichu(self):
        '''
        使用cmd命令结束进程
        '''
        os.system("taskkill /f /t /im Hearthstone.exe")

技能

def jineng(self,mode=None):
        '''
        使用技能,每回合必进行一次,避免浪费费用.
        mode : None,仅仅的点击技能即可.此类适用于(猎人,战士,萨满,圣骑士)
                'fashi' : 法师技能.对敌方英雄使用.
                'mushi' : 牧师技能,对自己英雄使用.
                'shushi' : 术士技能,永不使用.
                'other' : 潜行者,德鲁伊,恶魔猎手技能,使用无嘲讽则攻击敌方英雄

        '''
        if mode==None:
            self.click(1140, 810)
        elif mode=='fashi':
            self.drag(1140,810,961,201)
        elif mode=='mushi':
            self.drag(1140,810,961,821)
        elif mode=='shushi':
            pass
        elif mode=='other':
            self.click(1140,810)
            x,y = self.chaofeng()
            if x==950:
                self.drag(961,821,x,y)

可能出现的异常

def yichang(self):
        x,y = self.get_position(M=False,img1='renwu.jpg')
        # 这部分是每日凌晨时会出现任务,需点击后才能继续对局
        while x!=100:
            time.sleep(1)
            self.click(x,y,0)
            time.sleep(1)
            x,y = self.get_position(M=False,img1='renwu.jpg')
        x,y = self.get_position(M=False,img1='yichang.jpg') # 该异常是一个确定按钮
        if x==100:
            x,y = self.get_position(N=False,img1='chxlj.jpg',img2='yichang_1.jpg',M=False) # 该异常是重新连接按钮
            if x==100:
                return None
        self.click(x,y,0)

特殊

注意请不要使用特殊牌,除非你已对该脚本了解,并进行相关的优化.

def teshu(self):
        for j in self.T:
            if self.is_sim(N=False,img1=j,img2='shoupai\\new_big.jpg',K1=False)>0.8:
                return self.T[j]
        return {}

认输

主动的认输更能使得在一定时间内达到更多的胜利场数.

# 认输
    def renshu(self):
        pyautogui.press('esc')
        
        time.sleep(1)
        x,y = self.get_position('renshu.jpg',M=False)
        if x==100:
            self.tuichu()
        else:
            self.click(x,y,0)

返回出牌的费用

如需请自己优化,这里将用于自己回合时确定出牌

def min_SP(self,SP=[],mode='low',fee=None):
        '''
        返回手牌最小费用
        ----------
        SP : 手牌费用.
        mode : 默认'low'表示从低费开始
                若为高费,需要fee参数,用来返回不大于fee
                的最大费用
        返回
        -------
        去除0费后最小费用
        '''
        if mode=='low':
            if min(SP)==0:
                for i in range(1,10):
                    if SP.count(i)!=0:
                        return i
            return min(SP)
        else:
            if max(SP)>fee:
                for i in range(fee,0,-1):
                    if SP.count(i)!=0:
                        return i
            return max(SP)

获得手牌费用

该部分已经过证明可行,无需优化,最好别更改此处代码,除非在效率上能有更好的提高.

def feiyong(self,num=4,position=None,M=[],G_F=None): 
        '''
        查找手牌费用,起始时根据num查找所有手牌费用,之后
        指定位置,查找获得手牌费用.
        实现为仅在第一回合获得手牌费用,这得于对出牌函数优化引入重读取手牌费用步骤
        实现对新牌费用查找
        获得牌费用,考虑到有时费用仅靠灰度识别误差较大,更改为二值化后再识别.
        ------
        num : 手牌数,由幸运币判断起始手牌数self.N
            之后根据出牌与获得牌更新,为1表示查找某处费用.
        position : 默认不需要,指定表示仅查找该处牌费用.
        G_F : 不为None,表示更新手牌费用列表.
        ------
        返回 手牌从左到右的费用
        '''
        if position==None or G_F!=None:
            if position==None:
                X = self.X_D[num]
            else:
                X = position
            num = len(X)
            if num==0:
                X = self.X_D[1]
            L,L_ = [],[]
            M = []
            for j in range(num): # 该部分是之后用于确定优先出牌使用
                img = f'shoupai\\in_{j}.jpg'
                L.append(img)
                self.move(X[j],1000,0)
                time.sleep(0.2)
                self.get_screen(img,region=(501,552,880,125))
                img_ = f'shoupai\\in_{j}_big.jpg'
                L_.append(img_)
                self.get_screen(img_,region=(X[j]-270,552,620,580))
                
            for i in L:
                F = self.is_sim(mul=True,mul_I=self.fy,N=False,img2=i,thresh=True,T=0)
                if max(F)>0.65:
                    M.append(F.index(max(F)))
            self.move(200,500,100)
            return M
        else:
            self.move(position[0],position[1],0)
            time.sleep(0.2)
            self.get_screen('shoupai\\new.jpg',region=(501,552,880,105))
            self.get_screen('shoupai\\new_big.jpg',region=(position[0]-270,552,620,580))
            
            Ex = self.is_sim(mul=True,mul_I=self.fy,N=False,img2='shoupai\\new.jpg',thresh=True,T=0)
            
           
            j = max(Ex)
            i = Ex.index(max(Ex))
            if j>0.65:
                M.append(i)
        if num==1:
            return i
        self.move(200,500,100)
        return M

敌方嘲讽随从

mul_I 中为嘲讽随从模板,几乎满足要求.

def chaofeng(self):
        '''
        已实现对嘲讽的快速查找
        ------
        修改mul_I
        '''
        mul_I=['in_game\\chaof1.jpg','in_game\\chaof2.jpg',
               'in_game\\chaof3.jpg','in_game\\chaof4.jpg',
               'in_game\\chaof5.jpg']
        region=(471,293,1007,199)
        x0,y0,T = self.get_position(mul=True,mul_I=mul_I,img2='chaofeng.jpg',region=region,s=0.75)

        if x0==100:
            return 950,190
        if T=='left':
            x0 += 50
            y0 += 20
        elif T=='right':
            x0 -= 50
            y0 += 20
        return x0,y0

攻击辅助

改部分辅助于识别己方随从数目.同时确定是否为先手.

def init(self):
        '''
        用来获得关卡界面,与gongji函数一起实现推测
        己方随从位置.
        同时判断是否含有幸运币.
        进而获取此时所有手牌费用
        '''
        self.move(1700,77,10)
        time.sleep(2.5)
        self.get_screen('in_game\\beijing.jpg',region=D['one'])
        self.get_screen('in_game\\bj_1.jpg',region=D['two'])
        self.get_screen('in_game\\bj_2.jpg',region=D['three'])
        self.get_screen('in_game\\bj_3.jpg',region=D['four'])
        self.get_screen('in_game\\bj_4.jpg',region=D['five'])
        self.get_screen('in_game\\bj_5.jpg',region=D['six'])
        self.get_screen('in_game\\bj_6.jpg',region=D['seven'])
        if self.is_sim(img1='xyb.jpg',region=(982,924,132,137)):
            self.N = 6
        else:
            self.N = 4

出牌(未完善,但可用)

注意如有特殊牌,请优化此处!!!

def chupai(self,x,position=None,position_=None,D={}):
        '''
        说明 : 目前属于设想阶段,将记录特殊的牌.
              为实现此应优化feiyong函数,实现对特殊牌的识别.
              
              -----
              对于识别,由于要识别的特殊牌目前来说终究是较小的,
              考虑到费用的不确定性,目前暂未确定检索方式.
              -----
              
        设想 : 
              position :
                  None : 表示出牌位置为敌方英雄处,所有随从\武器\以及一些可以打脸的法术.
                  'debuff' : 表示对敌方随从使用的法术.
                  'buff' : 表示对己方随从使用的法术.
                  'huixue' : 表示回血法术.
            
              position_ :
                  None : 表示该牌打出后无需其它操作.
                  'huixue' : 表示回血.
                  'buff' : 表示对己方随从使用.
                  'debuff' : 表示对敌方随从使用.
                  'debuff_2' : 表示对敌方英雄使用.
                  'choice' : 表示选择.
                  
        ------------------
        参数x : 出牌位置x,是由相关函数获得.
        position : 已实现对默认值与'huixue'的操作.
        position_ : 已实现对默认值,'huixue','debuff_2'的操作.
        

        '''
        position,position_ = None,None
        if len(D)==0:
            position,position_ = None,None
        else:
            for i in D:
                if i=='position':
                    position = D[i]
                if i=='position_':
                    position_ = D[i]
        if position==None:
            self.drag(x, 1000, 963, 190) # 出牌
            if position_==None:
                time.sleep(0.7)
            elif position_=='huixue':
                time.sleep(0.3)
                self.click(941,829,5)

            elif position_=='debuff_2':
                time.sleep(0.3)
                self.click(963,190,5)

        elif position=='huixue':
            self.drag(x,1000,941,829)
        
        elif position=='buff': #暂时没用到,故还未优化
            self.move(1000,500)
            time.sleep(0.5)
            L = self.gongji()
            if len(L)==0:
                print('这里需要实现优化,在自己场上没有随从时不使用该法术')
        
        time.sleep(0.5)

攻击

返回己方随从位置列表

def gongji(self):
        '''
        通过确定己方场上有几个随从来判断随从位置
        '''
        s = 0.7
        if self.is_sim('in_game\\beijing.jpg','beijing.jpg',s,region=D['one']):
            return []
        if self.is_sim('in_game\\bj_1.jpg','bj_1.jpg',s,region=D['two']):
            return [956]    
        if self.is_sim('in_game\\bj_2.jpg','bj_2.jpg',s,region=D['three']):
            return [900,1024]
        if self.is_sim('in_game\\bj_3.jpg','bj_3.jpg',s,region=D['four']):
            return [817,956,1104]
        if self.is_sim('in_game\\bj_4.jpg','bj_4.jpg',s,region=D['five']):
            return [735,900,1024,1170]
        if self.is_sim('in_game\\bj_5.jpg','bj_5.jpg',s,region=D['six']):
            return [680,817,956,1104,1237]
        if self.is_sim('in_game\\bj_6.jpg','bj_6.jpg',s,region=D['seven']):
            return [614,755,900,1024,1170,1306]
        return [539,680,817,956,1104,1237,1388]

执行回合

炉石传说进入游戏不过是你一回合我一回合.
in_game()表示回合开始执行

判断出什么牌

优先出恰好的费用,及四费出一张四费卡,无若大于4费从高费出起,否则从低费出起.

def func(fee,mode='low'):
            '''
            判断手牌中哪些牌可以出
            -------
            fee : 接受目前可出费用.
            mode : 接受出牌模式.
            -------
            return None,None,{}
            第一个参数表示要出牌在手牌中的排序,None表示无牌可出
            第二个参数表示是否可继续,None表示无需继续,True表示可继续
            第三个参数不为空时表示需传给self.chupai.
            '''
            F_ = self.F[:]
            Err = False
            Err1 = False
            while not Err1:
                if Err:
                    Err1 = True
                po,T,D = None,None,{}
                if len(F_)==0:
                    return None,None,{}
                if len(F_)==1:
                    if F_[0]<=fee:
                        po = 0
                        M = F_[0]
                
                elif F_.count(fee)!=0:
                    po = F_.index(fee)
                    M = fee
    
                elif mode=='low':
                    # 从低费出起
                    M = self.min_SP(F_,mode,fee=fee)
                    if M<=fee:
                        po = F_.index(M)
                        fee = fee-M
                        F_.remove(M)
                        M2 = self.min_SP(F_,mode,fee=fee)
                        if M2<=fee:
                            T = True
                        
                else:
                    # 从高费开始
                    M = self.min_SP(F_,mode='h',fee=fee)
                    po = F_.index(M)
                    fee = fee-M
                    F_.remove(M)
                    M2 = self.min_SP(F_,mode='low',fee=fee)
                    if M2<=fee:
                        T = True
                if po==None:
                    return None,None,{}
                x = self.X_D[len(self.F)][po]
                N = self.feiyong(num=1,position=(x,1000))
                D = self.teshu()
                if N==M or N==0:
                    return po,T,D
                else:
                    self.F = self.feiyong(num=len(self.F),position=None)
                    F_ = self.F[:]
                    fee = fee+M
                    Err = True
                    continue
            return None,None,{}

出牌

获得新手牌的费用,同时实现要出的牌的费用与执行时不同时重新读取费用.

def chupai(fee=2):
            '''
            fee : 实际上是回合数.
            返回 : 手牌费用.
            ----------
            进入自己回合执行,获得新手牌费用,增加到F中
            如果费用大于4就从高费出起,否则从低费出起
            出完后更新F,与可使用费用,
            当无牌可出时结束
            '''
            position = self.X_D[len(self.F)+1][-1]
            self.F = self.feiyong(position=(position,1000),M=self.F)
            
            T = True
            t = time.time()
            while T:
                if time.time()-t>25:
                    print('异常,执行出牌时间过长')
                    break
                if self.F.count(0)!=0: # 幸运币机制,直接跳费使用
                    if self.F.count(fee+1)!=0:
                        x = self.X_D[len(self.F)][self.F.index(0)]
                        if self.feiyong(num=1,position=(x,1000))==0:
                            self.chupai(x)
                            self.F.remove(0)
                            po,T,D = func(fee+1)
                            x = self.X_D[len(self.F)][po]
                            self.chupai(x,D=D)
                            f = self.F.pop(po)
                            fee = fee+1-f
                        else:
                            self.F = self.feiyong(num=len(self.F),position=None)
                            continue
                if fee>4:
                    mode='h'
                else:
                    mode='low'
                po,T,D = func(fee,mode)
                if po==None:
                    break
                x = self.X_D[len(self.F)][po]
                try:
                    self.chupai(x,D=D)
                except IndexError:
                    print(po,x,self.F,'错误')
                    raise IndexError
                f = self.F.pop(po)
                fee = fee-f
                if len(self.F)==0:
                    self.F = self.feiyong(G_F='need',position=[889])

结束回合

结束回合,通过对手回合模板表示对手回合,当未匹配时并匹配到结束回合时表示又到了己方回合.(注意有两种结束回合)

def jieshu(g):
            '''
            g : 回合数.
            F : 手牌费用.
            结束本回合,
            对方一个回合用时大于3*60 则直接退出
            --------
            返回 : 对手用时,手牌费用
            '''
            
            pos1 = {1:(1468,154),2:(489,130),3:(472,883)} # 对手回合鼠标位置
            
            pyautogui.click(1561,496)
            x,y = pos1[randint(1, 3)] 
            self.move(x, y, t=50)
            time.sleep(0.3)
            t0 = time.time()
            time.sleep(2)
                   
            while self.is_sim('in_game\\duishou.jpg',K1=False)>=0.55:
                if int(time.time()-t0)>3*60:
                    self.tuichu()
                continue

攻击

获得己方随从数,查找有无敌方嘲讽随从,打脸或打敌方嘲讽随从.每打完一个随从后重新更新己方随从数与敌方嘲讽随从.
当己方随从数目变幻时继续从两边随从开始.未实现对该出随从的休息机制.
引入计时机制,避免异常导致一直执行最后引起整个程序异常.

def gongji():
            '''
            不再随机攻击,而是自两端向中间攻击
            '''
            y1 = 590
            L = self.gongji()
            L1 = L[:]
            t0 = time.time()
            j = 0
            while len(L)>0:
                if time.time()-t0>20: # 引入计时机制
                    break
                x0,y0 = self.chaofeng()
                x = L[0] if j%2==0 else L[-1]
                L.remove(x)
                self.drag(x, y1, x0, y0)
                time.sleep(0.8)
                self.click(1750,300,50,'right') # 右击取消选定,避免一个错误使得其它随从无法选定其它随从
                time.sleep(1) 
                j += 1
                if len(L1)!=len(self.gongji()):
                    L = self.gongji()
                    L1 = L[:]

己方回合

先出牌,在释放技能,在攻击,最后结束.
不引入烧绳机制.

def mine(g):
            '''
            自己回合,
            执行完便回合结束,不再引入拖时间机制
            '''
            mul_I = ['in_game\\jieshu.jpg',
                     'in_game\\jieshu_2.jpg']
            L = self.is_sim(mul=True,mul_I=mul_I,img2='huihe.jpg')
            while max(L)<0.7:
                time.sleep(0.5)
                L = self.is_sim(mul=True,mul_I=mul_I,img2='huihe.jpg')
           
            self.move(1780,200)
            if g==1:
                self.init()
                time.sleep(0.5)
                self.F = self.feiyong(G_F='need',num=self.N)
                return None
             
            time.sleep(0.5)
            chupai(g)
            time.sleep(1.5)
            self.jineng('shushi')  # 技能释放改为出完牌后,随从攻击前
            time.sleep(1)
            gongji()

整个in_game(代码)

def in_game(self,Q):
        def func(fee,mode='low'):
            '''
            判断手牌中哪些牌可以出
            -------
            F : 接受手牌费用.
            fee : 接受目前可出费用.
            mode : 接受出牌模式.
            -------
            return None,None,{}
            第一个参数表示要出牌在手牌中的排序,None表示无牌可出
            第二个参数表示是否可继续,None表示无需继续,True表示可继续
            第三个参数不为空时表示需传给self.chupai.
            '''
            F_ = self.F[:]
            Err = False
            Err1 = False
            while not Err1:
                if Err:
                    Err1 = True
                po,T,D = None,None,{}
                if len(F_)==0:
                    return None,None,{}
                if len(F_)==1:
                    if F_[0]<=fee:
                        po = 0
                        M = F_[0]
                
                elif F_.count(fee)!=0:
                    po = F_.index(fee)
                    M = fee
    
                elif mode=='low':
                    # 从低费出起
                    M = self.min_SP(F_,mode,fee=fee)
                    if M<=fee:
                        po = F_.index(M)
                        fee = fee-M
                        F_.remove(M)
                        M2 = self.min_SP(F_,mode,fee=fee)
                        if M2<=fee:
                            T = True
                        
                else:
                    # 从高费开始
                    M = self.min_SP(F_,mode='h',fee=fee)
                    po = F_.index(M)
                    fee = fee-M
                    F_.remove(M)
                    M2 = self.min_SP(F_,mode='low',fee=fee)
                    if M2<=fee:
                        T = True
                if po==None:
                    return None,None,{}
                x = self.X_D[len(self.F)][po]
                N = self.feiyong(num=1,position=(x,1000))
                D = self.teshu()
                if N==M or N==0:
                    return po,T,D
                else:
                    self.F = self.feiyong(num=len(self.F),position=None)
                    F_ = self.F[:]
                    fee = fee+M
                    Err = True
                    continue
            return None,None,{}
                
        def chupai(fee=2):
            '''
            F : 手牌费用.
            fee : 实际上是回合数.
            返回 : 手牌费用.
            ----------
            进入自己回合执行,获得新手牌费用,增加到F中
            如果费用大于4就从高费出起,否则从低费出起
            出完后更新F,与可使用费用,
            当无牌可出时结束
            '''

            # 暂时不引入使用幸运币机制

            position = self.X_D[len(self.F)+1][-1]
            self.F = self.feiyong(position=(position,1000),M=self.F)
            
            T = True
            t = time.time()
            while T:
                if time.time()-t>25:
                    print('异常,执行出牌时间过长')
                    break
                if self.F.count(0)!=0: # 幸运币机制,直接跳费使用
                    if self.F.count(fee+1)!=0:
                        x = self.X_D[len(self.F)][self.F.index(0)]
                        if self.feiyong(num=1,position=(x,1000))==0:
                            self.chupai(x)
                            self.F.remove(0)
                            po,T,D = func(fee+1)
                            x = self.X_D[len(self.F)][po]
                            self.chupai(x,D=D)
                            f = self.F.pop(po)
                            fee = fee+1-f
                        else:
                            self.F = self.feiyong(num=len(self.F),position=None)
                            continue
                if fee>4:
                    mode='h'
                else:
                    mode='low'
                po,T,D = func(fee,mode)
                if po==None:
                    break
                x = self.X_D[len(self.F)][po]
                try:
                    self.chupai(x,D=D)
                except IndexError:
                    print(po,x,self.F,'错误')
                    raise IndexError
                f = self.F.pop(po)
                fee = fee-f
                if len(self.F)==0:
                    self.F = self.feiyong(G_F='need',position=[889])
                
        def jieshu(g):
            '''
            g : 回合数.
            F : 手牌费用.
            结束本回合,
            再自己结束第一个回合后开始返回手牌费用.
            
            对方一个回合用时大于3*60 则直接退出
            --------
            返回 : 对手用时,手牌费用
            '''
            
            pos1 = {1:(1468,154),2:(489,130),3:(472,883)} # 对手回合鼠标位置
            
            pyautogui.click(1561,496)
            x,y = pos1[randint(1, 3)] 
            self.move(x, y, t=50)
            time.sleep(0.3)
            t0 = time.time()
            time.sleep(2)
                   
            while self.is_sim('in_game\\duishou.jpg',K1=False)>=0.55:
                if int(time.time()-t0)>3*60:
                    self.tuichu()
                continue
            # return self.F
        
        def gongji():
            '''
            不再随机攻击,而是自两端向中间攻击

            
            '''
            y1 = 590
            L = self.gongji()
            L1 = L[:]
            t0 = time.time()
            j = 0
            while len(L)>0:
                if time.time()-t0>20: # 引入计时机制
                    break
                x0,y0 = self.chaofeng()
                x = L[0] if j%2==0 else L[-1]
                L.remove(x)
                self.drag(x, y1, x0, y0)
                time.sleep(0.8)
                self.click(1750,300,50,'right') # 右击取消选定,避免一个错误使得其它随从无法选定其它随从
                time.sleep(1) 
                j += 1
                if len(L1)!=len(self.gongji()):
                    L = self.gongji()
                    L1 = L[:]
                
        def mine(g):
            '''
            自己回合,
            执行完便回合结束,不再引入拖时间机制

            '''
            mul_I = ['in_game\\jieshu.jpg',
                     'in_game\\jieshu_2.jpg']
            L = self.is_sim(mul=True,mul_I=mul_I,img2='huihe.jpg')
            while max(L)<0.7:
                time.sleep(0.5)
                L = self.is_sim(mul=True,mul_I=mul_I,img2='huihe.jpg')
           
            self.move(1780,200)

        
            if g==1:
                self.init()
                time.sleep(0.5)
                self.F = self.feiyong(G_F='need',num=self.N)
                return None
             
            time.sleep(0.5)
            chupai(g)
            time.sleep(1.5)
            self.jineng('shushi')  # 技能释放改为出完牌后,随从攻击前
            time.sleep(1)
            gongji()
            # return self.F
        
        time.sleep(4)
        g = 1
        self.F = [] # 起始置空,实际上在第二回合结束由jieshu获取
        
        while True:
            A = Q.get()
            A['num_h'] = g
            Q.put(A)
            mine(g)#
            time.sleep(0.5)
            jieshu(g)#
            g = g+1

接下来是先可以优化以简化,但本人失去动力的代码,它们重要但繁琐.

对于多进程之间的通信

def Break(q=Queue(),q1=Queue(),q2=Queue()):
    '''
        根据返回值传递信息:
        返回 'error' ,表示未找到游戏窗口,这时应只有重连操作进行,
        其余相关进程应结束;
        返回 'in' , 表示这时进入开局,这时由进程"over"开始对局;
        返回 'start' , 表示这时开始对局,进行进程"start".
    '''
    if not q.empty():
        return 'error'
    if q2.empty():
        return None
    order = q2.get()
    q2.put(order)
    if q1.empty() is False:
        return True
    elif order=='start':
        return 'start'
    elif order=='in':
        return 'in'

实现多进程

def process_job(name,q=Queue(),q1=Queue(),q2=Queue(),Q=Queue(),List=[]):
    '''
    根据name决定进程,实现多进程
    ----------
    name : 目前有'over' : 用来确定一局结束,进行下一局;
                'start' : 用来开始对局,进行对局,并运行对局中的脚本;
                'error' : 监测窗口句柄,窗口句柄未找到则进行运行游戏相关操作
                .
    q : Queue(), 当未找到窗口句柄时,不为空,并在运行Break 返回'error'时,重新为空.
    q1 : Queue(), 不为空表示一局游戏结束,因终止'over'.
    q2 : Queue(), 接受信息有'in','start'.先由over输入'in'后,'start'接受'in'并输入'start'
        'in'表示已进入关内,'main'表示开始可进入关,'finishi'已完成关卡.
    List : 用来存储各相关进程所需分别参数.
    '''
    
    while True:
        if name=='over': #一把游戏结束
            if Break(q,q1,q2)=='error':
                break
            if time.time()-List[0]>=20*60: # 20分钟后直接退出并结束该进程
                print('该局时间大于20分钟',f'现在时间 {time.localtime()[3:5]}')
                # demo.renshu()
                demo.tuichu()
                break
            L = demo.is_sim(img2='jieshu.jpg',mul=True,mul_I=['shengli.jpg','shibai.jpg','in_game\\djjx.jpg'])
            if max(L)>0.6:
                q1.put(None)
                T = ''
                for i in time.localtime()[1:5]:
                    T = T+str(i)+'|'
                T = T[:-1]
                if L[0]>0.6:
                    end = '胜利'
                elif L[1]>0.6:
                    end = '失败'
                else:
                    end = '未知'
                if Q.empty() is True:
                    break
                A = Q.get()
                try:
                    num_h = A['num_h']
                    game_time = int(time.time()-A['start_T'])
                    game_time = f'{game_time//60}m {game_time%60}s'
                except KeyError:
                    print(A,'错误')
                    break
                except Exception as E:
                    print(E,'错误')
                    break
                
                if A['num_h']>3:
                    demo.write(game_endtime=T,end=end,num_h=num_h,game_time=game_time)
                break
            
            if demo.is_sim('start.jpg'):
                if Break(q,q1,q2)!='in':
                    
                    q2.put('start')
                    time.sleep(3)

        elif name=='start': # 开始游戏
            # 此进程一次结束,并在游戏结束
            if Break(q,q1,q2)=='start':
                q2.get()
                q2.put('in')
                x,y = demo.get_position('start.jpg')
                demo.click(x,y)
                time.sleep(1)
                while demo.is_sim('start.jpg'):
                    x,y = demo.get_position('start.jpg')
                    demo.click(x,y)
                    time.sleep(1)
                    
                T = '' # T = 'maoxian'表示与旅店老板战斗,进行优化
                
                if T=='maoxian': # 旅馆进行优化
                    x = 1400
                    y = choice([116,199,256,319,396,442,517,576,650,718])
                    demo.click(x,y,0)
                    x,y = demo.get_position('start.jpg')
                    demo.click(x,y)
                    time.sleep(1)
                    while demo.is_sim('start.jpg'):
                        x,y = demo.get_position('start.jpg')
                        demo.click(x,y)
                        time.sleep(1)
                t = time.time()
                
                
                
            if Break(q,q1,q2)=='in':
                while not demo.is_sim(img1='in_game\\qr.jpg',s=0.5):
                    if time.time()-t>5*60:
                        demo.tuichu()
                        break
                x,y = demo.get_position(img1='in_game\\qr.jpg',s=0.5)
                
                
                if  time.localtime()[2]<=20 and List[0]%2==0 and T!='maoxian' : # 每月20号之后不再自动投降
                    demo.renshu()
                    break
                
                time.sleep(1)
                t = 3.5
                if not demo.is_sim(img1='fee\\fee_2.jpg',region=(452,328,1026,95)):
                    X = [600,900,1300]
                    y0 = 538
                    for x0 in X:
                        demo.click(x0,y0)
                    t = 7.5
                demo.click(x,y)
                time.sleep(t)
                demo.start(Q)
    
        elif name=='error': # 异常    
            demo.Chonglian(q)
            demo.yichang()
            if not q.empty(): # 执行完重连后将q复位
                q.get()
   
        else:
            print('未设置该',name,'函数')

剩下的部分

这是窗口句柄的获取

classname = "UnityWndClass"
wdname = "炉石传说"
demo = GameAssist(classname,wdname)

这是重要的
由于是多进程运行,写入到__main__内是重要的,同时需在Shell中运行.

if __name__=='__main__' and 1==1:
    J = 0
    while True:
        q,q1,q2,Q = Queue(),Queue(),Queue(),Queue()
        
        # pro进程,一直进行,实时查找游戏窗口
        pro = Process(target=process_job,args=('error',q,q1,q2,Q))
        pro.daemon = True
        pro.start()
        
        while True: #该循环不能退出,否则会增加pro进程
            while not q.empty():
                time.sleep(5)
                continue
            t0 = time.time()
            pr1 = Process(target=process_job,args=('over',q,q1,q2,Q,[t0],))
            pr1.start()
            
            pr2 = Process(target=process_job,args=('start',q,q1,q2,Q,[J]))
            J = J+1
            pr2.start()
            
            while win32gui.FindWindow(classname,wdname):
                if not pr1.is_alive():
                    if not pr2.is_alive():
                        pr2.terminate()
                    if not q1.empty():
                        t = time.time()
                        time.sleep(1)
                        while not demo.is_sim(img1='start.jpg'):
                            demo.click(700,900,50)
                            time.sleep(1.5)
                            if time.time()-t>3*60:
                                demo.tuichu()
                                time.sleep(1)
                                break
                    break
            if pr1.is_alive():
                pr1.terminate()
            if pr2.is_alive():
                pr2.terminate()
            time.sleep(5)
            q1,q2,Q = Queue(),Queue(),Queue()

整个代码

# -*- coding: utf-8 -*-
"""
Created on Thu Mar 18 18:52:06 2021

@author: 537406976(这时本人QQ)
"""




import win32gui,time,pyautogui,cv2
import os
from PIL import Image
import numpy as np
from openpyxl import load_workbook
from multiprocessing import Process,Queue
from random import randint,choice

os.chdir(r'F:\Python\game')

class Screen():
    def get_screen(self,img2='scree.jpg',region=(0,0,1920,1080)):
        '''
        截屏
        ----------
        img2 : 图片存储路径. 默认'scree.jpg'.
        region : (x1,y1,width,hight) 截取矩形左顶点(x1,y1) 与矩形width,hight
        '''
        if region=='default':
            region = (0,0,1920,1080)
        elif len(region)<4:
            raise ValueError('region值错误 截取矩形左顶点(x1,y1) 与矩形width,hight')
        pyautogui.screenshot(img2,region=region)
        
    def is_sim(self,img1='',img2='default',s=0.75,K1=True,N=True,region='default',mul=False,mul_I=[],thresh=None,T=-1):
        '''

        判断img2里是否有img1
        进行多图像匹配时,需要参数mul,img2,mul_I,region
        ----------
        img1 :  The default is ''.
        img2 :  默认为'img1_1',格式与img1相同.
        s : 匹配最小值. The default is 0.75.
        K1 : 用于匹配值较小时使用,仅在测试时设置为False.
        N : 不截屏,而是使用已有图像,默认截屏.
        region : 传给get_screen.
        mul : 截图,匹配多图像,返回每个对象匹配值;
                若thresh不为None,则先二值化再匹配.
        mul_I : 图像中含有对象,list.
        T : 0表示灰度读取,-1表示彩色读取.传给cv2.imread().
        thresh : 不为None,则表示二值化后匹配,目前用于识别费用.不为None则T应该为0.
        
        '''
        if mul:
            if N:
                self.get_screen(img2,region=region)
            im2 = cv2.imread(img2,T)
            if thresh!=None:
                assert T==0
                im2 = cv2.adaptiveThreshold(im2,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
            M = []
            for i in mul_I:
                im1 = cv2.imread(i,T)
                if thresh!=None:
                    im1 = cv2.adaptiveThreshold(im1,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
                res = cv2.matchTemplate(im2,im1,cv2.TM_CCOEFF_NORMED)
                M.append(res.max())
            return M
        im1 = cv2.imread(img1,T)
        if img2=='default':
            img2 = img1[:img1.index('.')]+'_1'+img1[img1.index('.'):]
        if N:
            self.get_screen(img2,region=region)
        im2 = cv2.imread(img2,T)
        res = cv2.matchTemplate(im2,im1,cv2.TM_CCOEFF_NORMED)
        if not K1:
            return res.max()
        if res.max()<s:
            return False
        return True
    
    def get_position(self,img1='',img2='default',s=0.75,M=True,K1=True,N=True,region='default',mul=False,mul_I=[],T=-1,thresh=None):
        '''
        建议设置img2参数,避免多进程抢资源.
        多图像查找位置时,不支持等待.
        ----------
        img1 : 路径名,需要在屏幕上查找的图片
            DESCRIPTION.
        img2 : 截图时临时存放位置. 默认为'img1_1',格式与img1相同.
        s : 不大于1的正数,传递给is_sim函数.
        M : BOOL, optional
            为False时,未查找到返回(100,100)点. The default is True.
        K1 : False 取消阈值 默认True.
        N  :    False表示不截图,直接进行图像对比, 默认True.
        region : 传给get_screen.
        T : 0表示灰度读取,-1表示彩色读取.传给cv2.imread().
        thresh : 不为None,则表示二值化后匹配,目前用于识别费用.
        mul : 截图,匹配多图像,返回每个对象匹配值.
        mul_I : 图像中含有对象,list.
        Returns
        -------
        (X,Y) 图像中心点坐标
        '''
        if mul:
            M = self.is_sim(mul=mul,mul_I=mul_I,img2=img2,region=region,T=T,thresh=thresh)
            M1 = [M[0],M[1],M[3],M[4]]
            a = max(M1)
            if a>s:
                img1 = mul_I[M.index(a)]
                d = Image.open(img1).size
                im = cv2.imread(img1,T)
                
                im2 = cv2.imread(img2,T)
                
                res = cv2.matchTemplate(im2,im,cv2.TM_CCOEFF_NORMED)
                loc = np.where(res==res.max())
        
                X = int(loc[1]+d[0]/2)
                Y = int(loc[0]+d[1]/2)
                T = 'left' # 左上角
                if a==M[-1] or a==M[-2]:
                    T = 'right' # 右上角
                if region != 'default':
                    X += region[0]
                    Y += region[1]
                    return (X,Y,T)
            if M[2]>0.85:
                img1 = mul_I[2]
                d = Image.open(img1).size
                im = cv2.imread(img1,T)
                im2 = cv2.imread(img2,T)
                res = cv2.matchTemplate(im2,im,cv2.TM_CCOEFF_NORMED)
                loc = np.where(res==res.max())
        
                X = int(loc[1]+d[0]/2)
                Y = int(loc[0]+d[1]/2)
                T = 'left' # 左上角
                if region != 'default':
                    X += region[0]
                    Y += region[1]
                    return (X,Y,T)
            return 100,100,None
        if img2=='default':
            img2 = img1[:img1.index('.')]+'_1'+img1[img1.index('.'):]
        while K1 and self.is_sim(img1=img1,img2=img2,s=s,K1=K1,N=N,region=region,T=T,thresh=thresh) is not True:
            if M is True :
                continue
            else:
                return (100,100)
        if N:
            self.get_screen(img2,region)
        d = Image.open(img1).size

        im = cv2.imread(img1,T)
        if thresh!=None:
            im = cv2.adaptiveThreshold(im,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
        im2 = cv2.imread(img2,T)
        if thresh!=None:
            im2 = cv2.adaptiveThreshold(im2,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
        res = cv2.matchTemplate(im2,im,cv2.TM_CCOEFF_NORMED)
        loc = np.where(res==res.max())
        
        X = int(loc[1]+d[0]/2)
        Y = int(loc[0]+d[1]/2)
        if region != 'default':
            X += region[0]
            Y += region[1]
        return (X,Y)
    
    def click(self,x,y,t=20,button='primary'):
        '''
        点击给定点附近,所有非强制要求点应使用该方法
        -------
        x,y : 位置(x,y)
        t : 默认20
        button : 默认'primary',可设为'left'/'right'/'middle'
        '''
        pyautogui.click(x+randint(-t, t),y+randint(-t,t),button=button)
    
    def move(self,x,y,t=20):
        '''
        移到给定点附近
        '''
        pyautogui.moveTo(x+randint(-t, t),y+randint(-t, t))
    
    def drag(self,x0,y0,x1,y1,t=5):
        '''
        从当前位置(x0,y0)拖拽到指定位置(x1,y1)
        '''
        def mousedown(x,y,t):
            pyautogui.mouseDown(x+randint(-t, t),y+randint(-t, t))
        def mouseup(x,y,t):
            pyautogui.mouseUp(x+randint(-t, t),y+randint(-t, t))

        mousedown(x0, y0,t)
        self.move(x1,y1,t)
        time.sleep(0.5)
        mouseup(x1, y1,t)
   
    

D = {'one':(926,560,71,86),'two':(830,560,40,88),
     'three':(760,560,40,81),'four':(670,560,45,73),
     'five':(610,560,40,75),'six':(535,560,40,78),
     'seven':(465,560,40,73)}
Sc = Screen()

def chl():
    time.sleep(10) # 处理其它进程
    pyautogui.hotkey('winleft','d')
    time.sleep(1)
    x,y = Sc.get_position('baoxue.jpg')
    time.sleep(1)
    pyautogui.doubleClick(x,y)
    time.sleep(5)     
    x,y = Sc.get_position('jinru.jpg')
    time.sleep(1)
    pyautogui.doubleClick(259,181)
    time.sleep(1)
    pyautogui.doubleClick(x,y)     
    time.sleep(10)
    while True:
        if Sc.is_sim('duizhan.jpg'):
            break
        if Sc.is_sim('chlchg.jpg'):
            return 'chl'
    x,y = Sc.get_position('duizhan.jpg')  
    pyautogui.doubleClick(x,y) 
    time.sleep(3)
    
    
class GameAssist(Screen):   
    def __init__(self,classname,wdname):
        """初始化"""
        # 获取窗口句柄
        super().__init__()
        self.hwnd = win32gui.FindWindow(classname,wdname)
        if not self.hwnd:
            self.Chonglian()
        #窗口显示最前化
        self.SetAsForegroundWindow()
        # win32gui.SetActiveWindow(self.hwnd)
        # 使得分辨率为1920x1080
        self.get_screen()
        
        # 以下为一些参数信息,之后优化
        self.X_D = {8:[667,719,769,825,902,997,1059,1150],
                    7:[650,750,809,874,958,1038,1119],
                    6:[675,794,856,924,1022,1124],
                    5:[673,825,928,1041,1110],
                    4:[732,849,996,1108],
                    3:[805,897,1097],
                    2:[867,978],1:[889],0:[]}
        self.fy = []
        
        for m in range(11):
            img = f'shoupai\\{m}.jpg'
            self.fy.append(img)
        self.T = {} # 特殊牌
           
    
    def write(self,file1='game_X.xlsx',file2='game_J.xlsx',game_endtime='',game_time='',end='',num_h=''):
        wb = load_workbook(filename=file1)
        ws = wb.active
        ws.append([game_endtime,end,num_h,game_time])
        wb.save(file1)
        if end!='未知':
            wb = load_workbook(filename=file2)
            ws = wb.active
            end_ = int(ws.cell(row=2,column=1).value)

            ws.cell(row=2,column=1).value = end_+1
            if end=='胜利':
                end_ = int(ws.cell(row=2,column=2).value)
                ws.cell(row=2,column=2).value = end_+1
            else:
                end_ = int(ws.cell(row=2,column=3).value)
                ws.cell(row=2,column=3).value = end_+1
            ws.cell(row=2,column=4).value = f'{int(ws.cell(row=2,column=2).value)/int(ws.cell(row=2,column=1).value)*100:.2f}%'
            wb.save(file2)
        
    # 重连机制,通过查找窗口句柄确认
    def Chonglian(self,q=Queue()):
        '''
        重连机制,找不到窗口则回到桌面,打开应用
        -------
        q, 如果未找到窗口,不为空

        返回
        -------
        如果找到窗口就返回'in'

        '''
        self.hwnd = win32gui.FindWindow(classname,wdname)
        if not self.hwnd:
            q.put('error')
            A = chl() # 重连
            if A=='chl':
                self.renshu()
            self.hwnd = win32gui.FindWindow(classname,wdname)
        else:
            A = self.get_position('ljzhd.jpg',M=False)
            if A[0]==100:
                pass
            else:
                self.tuichu()
            A = self.get_position('chonglian.jpg',M=False)
            if A[0]==100:
                pass
            else:
                pyautogui.click(500,500,10)
    
    def SetAsForegroundWindow(self):
        win32gui.SetForegroundWindow(self.hwnd)



    def start(self,Q):
        '''
        Q : 用来将数据传出到其它进程
        '''
        Q.put({'num_h':0,'start_T':time.time()})

        pyautogui.moveTo(800,200)
        while self.is_sim(img1='in_game\\duishouxuanp.jpg'):
            time.sleep(0.5)
        pyautogui.moveTo(700,400)
        time.sleep(5)
        self.in_game(Q)
        
    def tuichu(self):
        '''
        使用cmd命令结束进程
        '''
        os.system("taskkill /f /t /im Hearthstone.exe")

    def jineng(self,mode=None):
        '''
        使用技能,每回合必进行一次,避免浪费费用.
        mode : None,仅仅的点击技能即可.此类适用于(猎人,战士,萨满,圣骑士)
                'fashi' : 法师技能.对敌方英雄使用.
                'mushi' : 牧师技能,对自己英雄使用.
                'shushi' : 术士技能,永不使用.
                'other' : 潜行者,德鲁伊,恶魔猎手技能,使用无嘲讽则攻击敌方英雄

        '''
        if mode==None:
            self.click(1140, 810)
        elif mode=='fashi':
            self.drag(1140,810,961,201)
        elif mode=='mushi':
            self.drag(1140,810,961,821)
        elif mode=='shushi':
            pass
        elif mode=='other':
            self.click(1140,810)
            x,y = self.chaofeng()
            if x==950:
                self.drag(961,821,x,y)
        
    def jiaoliu(self,i=0):
        # 已取消该机制
        '''
        交流
        
        i : 表示发出语言
        1 : 感谢      2 : 惊叹
        3 : 称赞      4 : 失误
        5 : 问候      6 : 威胁
        '''
        pyautogui.rightClick(976,867)
        chat = {1:(813,687),
         2:(1109,690),
         3:(758,778),
         4:(1155,772),
         5:(760,854),
         6:(1151,871)}
        if i in chat.keys():
            pass
        else:
            i = randint(1, 6)
        x,y= chat[i]
        time.sleep(1.5)
        self.click(x,y,5)
        self.move(1756,77)
        
        pass
    
    
    def yichang(self):
        x,y = self.get_position(M=False,img1='renwu.jpg')
        while x!=100:
            time.sleep(1)
            self.click(x,y,0)
            time.sleep(1)
            x,y = self.get_position(M=False,img1='renwu.jpg')
        x,y = self.get_position(M=False,img1='yichang.jpg')
        if x==100:
            x,y = self.get_position(N=False,img1='chxlj.jpg',img2='yichang_1.jpg',M=False)
            if x==100:
                return None
        self.click(x,y,0)
    
    def teshu(self):
        for j in self.T:
            if self.is_sim(N=False,img1=j,img2='shoupai\\new_big.jpg',K1=False)>0.8:
                return self.T[j]
        return {}
    

        
    # 重连后直接认输
    def renshu(self):
        pyautogui.press('esc')
        
        time.sleep(1)
        x,y = self.get_position('renshu.jpg',M=False)
        if x==100:
            self.tuichu()
        else:
            self.click(x,y,0)
        
    def min_SP(self,SP=[],mode='low',fee=None):
        '''
        返回手牌最小费用
        ----------
        SP : 手牌费用.
        mode : 默认'low'表示从低费开始
                若为高费,需要fee参数,用来返回不大于fee
                的最大费用
        返回
        -------
        去除0费后最小费用

        '''
                    

        if mode=='low':
            if min(SP)==0:
                for i in range(1,10):
                    if SP.count(i)!=0:
                        return i
            return min(SP)
        else:
            if max(SP)>fee:
                for i in range(fee,0,-1):
                    if SP.count(i)!=0:
                        return i
            return max(SP)

    def feiyong(self,num=4,position=None,M=[],G_F=None): 
        '''
        查找手牌费用,起始时根据num查找所有手牌费用,之后
        指定位置,查找获得手牌费用.
        实现为仅在第一回合获得手牌费用,这得于对出牌函数优化引入重读取手牌费用步骤
        实现对新牌费用查找
        获得牌费用,考虑到有时费用仅靠灰度识别误差较大,更改为二值化后再识别.
        ------
        num : 手牌数,由幸运币判断起始手牌数self.N
            之后根据出牌与获得牌更新,为1表示查找某处费用.
        position : 默认不需要,指定表示仅查找该处牌费用.
        G_F : 不为None,表示更新手牌费用列表.
        ------
        返回 手牌从左到右的费用
        '''
        if position==None or G_F!=None:
            if position==None:
                X = self.X_D[num]
            else:
                X = position
            num = len(X)
            if num==0:
                X = self.X_D[1]
            L,L_ = [],[]
            M = []
            for j in range(num): # 该部分是之后用于确定优先出牌使用
                img = f'shoupai\\in_{j}.jpg'
                L.append(img)
                self.move(X[j],1000,0)
                time.sleep(0.2)
                self.get_screen(img,region=(501,552,880,125))
                img_ = f'shoupai\\in_{j}_big.jpg'
                L_.append(img_)
                self.get_screen(img_,region=(X[j]-270,552,620,580))
                
            for i in L:
                F = self.is_sim(mul=True,mul_I=self.fy,N=False,img2=i,thresh=True,T=0)
                if max(F)>0.65:
                    M.append(F.index(max(F)))
            self.move(200,500,100)
            return M
        else:
            self.move(position[0],position[1],0)
            time.sleep(0.2)
            self.get_screen('shoupai\\new.jpg',region=(501,552,880,105))
            self.get_screen('shoupai\\new_big.jpg',region=(position[0]-270,552,620,580))
            
            Ex = self.is_sim(mul=True,mul_I=self.fy,N=False,img2='shoupai\\new.jpg',thresh=True,T=0)
            
           
            j = max(Ex)
            i = Ex.index(max(Ex))
            if j>0.65:
                M.append(i)
        if num==1:
            return i
        self.move(200,500,100)
        return M

    def chaofeng(self):
        '''
        已实现对嘲讽的快速查找
        ------
        修改mul_I
        '''
        mul_I=['in_game\\chaof1.jpg','in_game\\chaof2.jpg',
               'in_game\\chaof3.jpg','in_game\\chaof4.jpg',
               'in_game\\chaof5.jpg']
        region=(471,293,1007,199)
        x0,y0,T = self.get_position(mul=True,mul_I=mul_I,img2='chaofeng.jpg',region=region,s=0.75)

        if x0==100:
            return 950,190
        if T=='left':
            x0 += 50
            y0 += 20
        elif T=='right':
            x0 -= 50
            y0 += 20
        return x0,y0
    
    def init(self):
        '''
        用来获得关卡界面,与gongji函数一起实现推测
        己方随从位置.
        同时判断是否含有幸运币.
        进而获取此时所有手牌费用
        '''
        self.move(1700,77,10)
        time.sleep(2.5)
        self.get_screen('in_game\\beijing.jpg',region=D['one'])
        self.get_screen('in_game\\bj_1.jpg',region=D['two'])
        self.get_screen('in_game\\bj_2.jpg',region=D['three'])
        self.get_screen('in_game\\bj_3.jpg',region=D['four'])
        self.get_screen('in_game\\bj_4.jpg',region=D['five'])
        self.get_screen('in_game\\bj_5.jpg',region=D['six'])
        self.get_screen('in_game\\bj_6.jpg',region=D['seven'])
        if self.is_sim(img1='xyb.jpg',region=(982,924,132,137)):
            self.N = 6
        else:
            self.N = 4
            
    def chupai(self,x,position=None,position_=None,D={}):
        '''
        说明 : 目前属于设想阶段,将记录特殊的牌.
              为实现此应优化feiyong函数,实现对特殊牌的识别.
              
              -----
              对于识别,由于要识别的特殊牌目前来说终究是较小的,
              考虑到费用的不确定性,目前暂未确定检索方式.
              -----
              
        设想 : 
              position :
                  None : 表示出牌位置为敌方英雄处,所有随从\武器\以及一些可以打脸的法术.
                  'debuff' : 表示对敌方随从使用的法术.
                  'buff' : 表示对己方随从使用的法术.
                  'huixue' : 表示回血法术.
            
              position_ :
                  None : 表示该牌打出后无需其它操作.
                  'huixue' : 表示回血.
                  'buff' : 表示对己方随从使用.
                  'debuff' : 表示对敌方随从使用.
                  'debuff_2' : 表示对敌方英雄使用.
                  'choice' : 表示选择.
                  
        ------------------
        参数x : 出牌位置x,是由相关函数获得.
        position : 已实现对默认值与'huixue'的操作.
        position_ : 已实现对默认值,'huixue','debuff_2'的操作.
        

        '''
        position,position_ = None,None
        if len(D)==0:
            position,position_ = None,None
        else:
            for i in D:
                if i=='position':
                    position = D[i]
                if i=='position_':
                    position_ = D[i]
        if position==None:
            self.drag(x, 1000, 963, 190) # 出牌
            if position_==None:
                time.sleep(0.7)
            elif position_=='huixue':
                time.sleep(0.3)
                self.click(941,829,5)

            elif position_=='debuff_2':
                time.sleep(0.3)
                self.click(963,190,5)

        elif position=='huixue':
            self.drag(x,1000,941,829)
        
        elif position=='buff': #暂时没用到,故还未优化
            self.move(1000,500)
            time.sleep(0.5)
            L = self.gongji()
            if len(L)==0:
                print('这里需要实现优化,在自己场上没有随从时不使用该法术')
        
        time.sleep(0.5)
        
            
        
    def gongji(self):
        '''
        通过确定己方场上有几个随从来判断随从位置
        '''
        s = 0.7
        if self.is_sim('in_game\\beijing.jpg','beijing.jpg',s,region=D['one']):
            return []
        if self.is_sim('in_game\\bj_1.jpg','bj_1.jpg',s,region=D['two']):
            return [956]    
        if self.is_sim('in_game\\bj_2.jpg','bj_2.jpg',s,region=D['three']):
            return [900,1024]
        if self.is_sim('in_game\\bj_3.jpg','bj_3.jpg',s,region=D['four']):
            return [817,956,1104]
        if self.is_sim('in_game\\bj_4.jpg','bj_4.jpg',s,region=D['five']):
            return [735,900,1024,1170]
        if self.is_sim('in_game\\bj_5.jpg','bj_5.jpg',s,region=D['six']):
            return [680,817,956,1104,1237]
        if self.is_sim('in_game\\bj_6.jpg','bj_6.jpg',s,region=D['seven']):
            return [614,755,900,1024,1170,1306]
        return [539,680,817,956,1104,1237,1388]
    
    def in_game(self,Q):
        def func(fee,mode='low'):
            '''
            判断手牌中哪些牌可以出
            -------
            F : 接受手牌费用.
            fee : 接受目前可出费用.
            mode : 接受出牌模式.
            -------
            return None,None,{}
            第一个参数表示要出牌在手牌中的排序,None表示无牌可出
            第二个参数表示是否可继续,None表示无需继续,True表示可继续
            第三个参数不为空时表示需传给self.chupai.
            '''
            F_ = self.F[:]
            Err = False
            Err1 = False
            while not Err1:
                if Err:
                    Err1 = True
                po,T,D = None,None,{}
                if len(F_)==0:
                    return None,None,{}
                if len(F_)==1:
                    if F_[0]<=fee:
                        po = 0
                        M = F_[0]
                
                elif F_.count(fee)!=0:
                    po = F_.index(fee)
                    M = fee
    
                elif mode=='low':
                    # 从低费出起
                    M = self.min_SP(F_,mode,fee=fee)
                    if M<=fee:
                        po = F_.index(M)
                        fee = fee-M
                        F_.remove(M)
                        M2 = self.min_SP(F_,mode,fee=fee)
                        if M2<=fee:
                            T = True
                        
                else:
                    # 从高费开始
                    M = self.min_SP(F_,mode='h',fee=fee)
                    po = F_.index(M)
                    fee = fee-M
                    F_.remove(M)
                    M2 = self.min_SP(F_,mode='low',fee=fee)
                    if M2<=fee:
                        T = True
                if po==None:
                    return None,None,{}
                x = self.X_D[len(self.F)][po]
                N = self.feiyong(num=1,position=(x,1000))
                D = self.teshu()
                if N==M or N==0:
                    return po,T,D
                else:
                    self.F = self.feiyong(num=len(self.F),position=None)
                    F_ = self.F[:]
                    fee = fee+M
                    Err = True
                    continue
            return None,None,{}
                
        def chupai(fee=2):
            '''
            fee : 实际上是回合数.
            返回 : 手牌费用.
            ----------
            进入自己回合执行,获得新手牌费用,增加到F中
            如果费用大于4就从高费出起,否则从低费出起
            出完后更新F,与可使用费用,
            当无牌可出时结束
            '''
            position = self.X_D[len(self.F)+1][-1]
            self.F = self.feiyong(position=(position,1000),M=self.F)
            
            T = True
            t = time.time()
            while T:
                if time.time()-t>25:
                    print('异常,执行出牌时间过长')
                    break
                if self.F.count(0)!=0: # 幸运币机制,直接跳费使用
                    if self.F.count(fee+1)!=0:
                        x = self.X_D[len(self.F)][self.F.index(0)]
                        if self.feiyong(num=1,position=(x,1000))==0:
                            self.chupai(x)
                            self.F.remove(0)
                            po,T,D = func(fee+1)
                            x = self.X_D[len(self.F)][po]
                            self.chupai(x,D=D)
                            f = self.F.pop(po)
                            fee = fee+1-f
                        else:
                            self.F = self.feiyong(num=len(self.F),position=None)
                            continue
                if fee>4:
                    mode='h'
                else:
                    mode='low'
                po,T,D = func(fee,mode)
                if po==None:
                    break
                x = self.X_D[len(self.F)][po]
                try:
                    self.chupai(x,D=D)
                except IndexError:
                    print(po,x,self.F,'错误')
                    raise IndexError
                f = self.F.pop(po)
                fee = fee-f
                if len(self.F)==0:
                    self.F = self.feiyong(G_F='need',position=[889])
                
        def jieshu(g):
            '''
            g : 回合数.
            F : 手牌费用.
            结束本回合,
            再自己结束第一个回合后开始返回手牌费用.
            
            对方一个回合用时大于3*60 则直接退出
            --------
            返回 : 对手用时,手牌费用
            '''
            
            pos1 = {1:(1468,154),2:(489,130),3:(472,883)} # 对手回合鼠标位置
            
            pyautogui.click(1561,496)
            x,y = pos1[randint(1, 3)] 
            self.move(x, y, t=50)
            time.sleep(0.3)
            t0 = time.time()
            time.sleep(2)
                   
            while self.is_sim('in_game\\duishou.jpg',K1=False)>=0.55:
                if int(time.time()-t0)>3*60:
                    self.tuichu()
                continue
        
        def gongji():
            '''
            不再随机攻击,而是自两端向中间攻击
            '''
            y1 = 590
            L = self.gongji()
            L1 = L[:]
            t0 = time.time()
            j = 0
            while len(L)>0:
                if time.time()-t0>20: # 引入计时机制
                    break
                x0,y0 = self.chaofeng()
                x = L[0] if j%2==0 else L[-1]
                L.remove(x)
                self.drag(x, y1, x0, y0)
                time.sleep(0.8)
                self.click(1750,300,50,'right') # 右击取消选定,避免一个错误使得其它随从无法选定其它随从
                time.sleep(1) 
                j += 1
                if len(L1)!=len(self.gongji()):
                    L = self.gongji()
                    L1 = L[:]
                
        def mine(g):
            '''
            自己回合,
            执行完便回合结束,不再引入拖时间机制

            '''
            mul_I = ['in_game\\jieshu.jpg',
                     'in_game\\jieshu_2.jpg']
            L = self.is_sim(mul=True,mul_I=mul_I,img2='huihe.jpg')
            while max(L)<0.7:
                time.sleep(0.5)
                L = self.is_sim(mul=True,mul_I=mul_I,img2='huihe.jpg')
           
            self.move(1780,200)

        
            if g==1:
                self.init()
                time.sleep(0.5)
                self.F = self.feiyong(G_F='need',num=self.N)
                return None
             
            time.sleep(0.5)
            chupai(g)
            time.sleep(1.5)
            self.jineng('shushi')  # 技能释放改为出完牌后,随从攻击前
            time.sleep(1)
            gongji()
            # return self.F
        
        time.sleep(4)
        g = 1
        self.F = [] # 起始置空,实际上在第二回合结束由jieshu获取
        
        while True:
            A = Q.get()
            A['num_h'] = g
            Q.put(A)
            mine(g)#
            time.sleep(0.5)
            jieshu(g)#
            g = g+1
            
 

       
def Break(q=Queue(),q1=Queue(),q2=Queue()):
    '''
        根据返回值传递信息:
        返回 'error' ,表示未找到游戏窗口,这时应只有重连操作进行,
        其余相关进程应结束;
        返回 'in' , 表示这时进入开局,这时由进程"over"开始对局;
        返回 'start' , 表示这时开始对局,进行进程"start".
    '''
    if not q.empty():
        return 'error'
    if q2.empty():
        return None
    order = q2.get()
    q2.put(order)
    if q1.empty() is False:
        return True
    elif order=='start':
        return 'start'
    elif order=='in':
        return 'in'

def process_job(name,q=Queue(),q1=Queue(),q2=Queue(),Q=Queue(),List=[]):
    '''
    根据name决定进程,实现多进程
    ----------
    name : 目前有'over' : 用来确定一局结束,进行下一局;
                'start' : 用来开始对局,进行对局,并运行对局中的脚本;
                'error' : 监测窗口句柄,窗口句柄未找到则进行运行游戏相关操作
                .
    q : Queue(), 当未找到窗口句柄时,不为空,并在运行Break 返回'error'时,重新为空.
    q1 : Queue(), 不为空表示一局游戏结束,因终止'over'.
    q2 : Queue(), 接受信息有'in','start'.先由over输入'in'后,'start'接受'in'并输入'start'
        'in'表示已进入关内,'main'表示开始可进入关,'finishi'已完成关卡.
    List : 用来存储各相关进程所需分别参数.
    '''
    
    while True:
        if name=='over': #一把游戏结束
            if Break(q,q1,q2)=='error':
                break
            if time.time()-List[0]>=20*60: # 20分钟后直接退出并结束该进程
                print('该局时间大于20分钟',f'现在时间 {time.localtime()[3:5]}')
                # demo.renshu()
                demo.tuichu()
                break
            L = demo.is_sim(img2='jieshu.jpg',mul=True,mul_I=['shengli.jpg','shibai.jpg','in_game\\djjx.jpg'])
            if max(L)>0.6:
                q1.put(None)
                T = ''
                for i in time.localtime()[1:5]:
                    T = T+str(i)+'|'
                T = T[:-1]
                if L[0]>0.6:
                    end = '胜利'
                elif L[1]>0.6:
                    end = '失败'
                else:
                    end = '未知'
                if Q.empty() is True:
                    break
                A = Q.get()
                try:
                    num_h = A['num_h']
                    game_time = int(time.time()-A['start_T'])
                    game_time = f'{game_time//60}m {game_time%60}s'
                except KeyError:
                    print(A,'错误')
                    break
                except Exception as E:
                    print(E,'错误')
                    break
                
                if A['num_h']>3:
                    demo.write(game_endtime=T,end=end,num_h=num_h,game_time=game_time)
                break
            
            if demo.is_sim('start.jpg'):
                if Break(q,q1,q2)!='in':
                    
                    q2.put('start')
                    time.sleep(3)

        elif name=='start': # 开始游戏
            # 此进程一次结束,并在游戏结束
            if Break(q,q1,q2)=='start':
                q2.get()
                q2.put('in')
                x,y = demo.get_position('start.jpg')
                demo.click(x,y)
                time.sleep(1)
                while demo.is_sim('start.jpg'):
                    x,y = demo.get_position('start.jpg')
                    demo.click(x,y)
                    time.sleep(1)
                    
                T = '' # T = 'maoxian'表示与旅店老板战斗,进行优化
                
                if T=='maoxian': # 旅馆进行优化
                    x = 1400
                    y = choice([116,199,256,319,396,442,517,576,650,718])
                    demo.click(x,y,0)
                    x,y = demo.get_position('start.jpg')
                    demo.click(x,y)
                    time.sleep(1)
                    while demo.is_sim('start.jpg'):
                        x,y = demo.get_position('start.jpg')
                        demo.click(x,y)
                        time.sleep(1)
                t = time.time()
                
                
                
            if Break(q,q1,q2)=='in':
                while not demo.is_sim(img1='in_game\\qr.jpg',s=0.5):
                    if time.time()-t>5*60:
                        demo.tuichu()
                        break
                x,y = demo.get_position(img1='in_game\\qr.jpg',s=0.5)
                
                
                if  time.localtime()[2]<=20 and List[0]%2==0 and T!='maoxian' : # 每月20号之后不再自动投降
                    demo.renshu()
                    break
                
                time.sleep(1)
                t = 3.5
                if not demo.is_sim(img1='fee\\fee_2.jpg',region=(452,328,1026,95)):
                    X = [600,900,1300]
                    y0 = 538
                    for x0 in X:
                        demo.click(x0,y0)
                    t = 7.5
                demo.click(x,y)
                time.sleep(t)
                demo.start(Q)
    
        elif name=='error': # 异常    
            demo.Chonglian(q)
            demo.yichang()
            if not q.empty(): # 执行完重连后将q复位
                q.get()
   
        else:
            print('未设置该',name,'函数')


classname = "UnityWndClass"
wdname = "炉石传说"
demo = GameAssist(classname,wdname)


'''
猎人1000胜已完成,
现在是术士 : 无特殊牌,不使用技能
'''
if __name__=='__main__' and 1==1:
    J = 0
    while True:
        q,q1,q2,Q = Queue(),Queue(),Queue(),Queue()
        
        # pro进程,一直进行,实时查找游戏窗口
        pro = Process(target=process_job,args=('error',q,q1,q2,Q))
        pro.daemon = True
        pro.start()
        
        while True: #该循环不能退出,否则会增加pro进程
            while not q.empty():
                time.sleep(5)
                continue
            t0 = time.time()
            pr1 = Process(target=process_job,args=('over',q,q1,q2,Q,[t0],))
            pr1.start()
            
            pr2 = Process(target=process_job,args=('start',q,q1,q2,Q,[J]))
            J = J+1
            pr2.start()
            
            while win32gui.FindWindow(classname,wdname):
                if not pr1.is_alive():
                    if not pr2.is_alive():
                        pr2.terminate()
                    if not q1.empty():
                        t = time.time()
                        time.sleep(1)
                        while not demo.is_sim(img1='start.jpg'):
                            demo.click(700,900,50)
                            time.sleep(1.5)
                            if time.time()-t>3*60:
                                demo.tuichu()
                                time.sleep(1)
                                break
                    break
            if pr1.is_alive():
                pr1.terminate()
            if pr2.is_alive():
                pr2.terminate()
            time.sleep(5)
            
            
            q1,q2,Q = Queue(),Queue(),Queue()

最后的说明

请在有一定的编程能力与我交流,伸手党勿向本人要相关模板图像.
请勿转载!!请勿用于商业用途!!
仅用于将编程推广
如有疑问,请私(Q537406976,请备注 编程询问)
一些相关的模板图片由于可能侵权,请私我,以及运行出现异常请在评论留下或私我

基于Anaconda 写炉石传说脚本

上一篇:JVM垃圾回收算法与垃圾收集器介绍二


下一篇:深入浅出 TiDB 框架