# -*- coding: utf-8 -*- # Created by: Vincentzyx import win32gui import win32ui import win32api from ctypes import windll from PIL import Image import cv2 import pyautogui import matplotlib.pyplot as plt import numpy as np import os import time import threading from win32con import WM_LBUTTONDOWN, MK_LBUTTON, WM_LBUTTONUP, WM_MOUSEMOVE import multiprocessing as mp from PyQt5 import QtGui, QtWidgets, QtCore from PyQt5.QtCore import QTime, QEventLoop Pics = {} ReqQueue = mp.Queue() ResultQueue = mp.Queue() Processes = [] def GetSingleCardQueue(reqQ, resQ, Pics): while True: while not reqQ.empty(): image, i, sx, sy, sw, sh, checkSelect = reqQ.get() result = GetSingleCard(image, i, sx, sy, sw, sh, checkSelect, Pics) del image if result is not None: resQ.put(result) time.sleep(0.01) def ShowImg(image): plt.imshow(image) plt.show() def DrawRectWithText(image, rect, text): img = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR) x, y, w, h = rect img2 = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2) img2 = cv2.putText(img2, text, (x, y + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2) return Image.fromarray(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)) def CompareCard(card): order = {"3": 0, "4": 1, "5": 2, "6": 3, "7": 4, "8": 5, "9": 6, "T": 7, "J": 8, "Q": 9, "K": 10, "A": 11, "2": 12, "X": 13, "D": 14} return order[card] def CompareCardInfo(card): order = {"3": 0, "4": 1, "5": 2, "6": 3, "7": 4, "8": 5, "9": 6, "T": 7, "J": 8, "Q": 9, "K": 10, "A": 11, "2": 12, "X": 13, "D": 14} return order[card[0]] def CompareCards(cards1, cards2): if len(cards1) != len(cards2): return False cards1.sort(key=CompareCard) cards2.sort(key=CompareCard) for i in range(0, len(cards1)): if cards1[i] != cards2[i]: return False return True def GetListDifference(l1, l2): temp1 = [] temp1.extend(l1) temp2 = [] temp2.extend(l2) for i in l2: if i in temp1: temp1.remove(i) for i in l1: if i in temp2: temp2.remove(i) return temp1, temp2 def FindImage(fromImage, template, threshold=0.9): w, h, _ = template.shape fromImage = cv2.cvtColor(np.asarray(fromImage), cv2.COLOR_RGB2BGR) res = cv2.matchTemplate(fromImage, template, cv2.TM_CCOEFF_NORMED) loc = np.where(res >= threshold) points = [] for pt in zip(*loc[::-1]): points.append(pt) return points def GetSingleCard(image, i, sx, sy, sw, sh, checkSelect, Pics): cardSearchFrom = 0 AllCardsNC = ['rD', 'bX', '2', 'A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3'] currCard = "" ci = cardSearchFrom while ci < len(AllCardsNC): if "r" in AllCardsNC[ci] or "b" in AllCardsNC[ci]: result = pyautogui.locate(needleImage=Pics["m" + AllCardsNC[ci]], haystackImage=image, region=(sx + 50 * i, sy - checkSelect * 25, sw, sh), confidence=0.9) if result is not None: cardPos = (sx + 50 * i + sw // 2, sy - checkSelect * 25 + sh // 2) cardSearchFrom = ci currCard = AllCardsNC[ci][1] cardInfo = (currCard, cardPos) return cardInfo break else: outerBreak = False for card_type in ["r", "b"]: result = pyautogui.locate(needleImage=Pics["m" + card_type + AllCardsNC[ci]], haystackImage=image, region=(sx + 50 * i, sy - checkSelect * 25, sw, sh), confidence=0.9) if result is not None: cardPos = (sx + 50 * i + sw // 2, sy - checkSelect * 25 + sh // 2) cardSearchFrom = ci currCard = AllCardsNC[ci] cardInfo = (currCard, cardPos) outerBreak = True return cardInfo break if outerBreak: break if ci == len(AllCardsNC) - 1 and checkSelect == 0: checkSelect = 1 ci = cardSearchFrom - 1 ci += 1 return None def RunThreads(): for file in os.listdir("pics"): info = file.split(".") if info[1] == "png": tmpImage = Image.open("pics/" + file) Pics.update({info[0]: tmpImage}) for ti in range(20): p = mp.Process(target=GetSingleCardQueue, args=(ReqQueue, ResultQueue, Pics)) p.start() def LocateOnImage(image, template, region=None, confidence=0.9): if region is not None: x, y, w, h = region imgShape = image.shape image = image[y:y+h, x:x+w,:] res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) if (res >= confidence).any(): return True else: return None def LocateAllOnImage(image, template, region=None, confidence=0.9): if region is not None: x, y, w, h = region imgShape = image.shape image = image[y:y+h, x:x+w,:] w, h = image.shape[1], image.shape[0] res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) loc = np.where( res >= confidence) points = [] for pt in zip(*loc[::-1]): points.append((pt[0], pt[1], w, h)) return points class GameHelper: def __init__(self): self.ScreenZoomRate = 1.25 self.Pics = {} self.PicsCV = {} self.Handle = win32gui.FindWindow("Hlddz", None) self.Interrupt = False self.RealRate = (1796, 1047) for file in os.listdir("./pics"): info = file.split(".") if info[1] == "png": tmpImage = Image.open("./pics/" + file) imgCv = cv2.imread("./pics/" + file) self.Pics.update({info[0]: tmpImage}) self.PicsCV.update({info[0]: imgCv}) def Screenshot(self, region=None): # -> (im, (left, top)) self.Handle = win32gui.FindWindow("Hlddz", None) hwnd = self.Handle # im = Image.open(r"C:\Users\q9294\Desktop\Snipaste_2021-09-05_00-52-51.png") # im = im.resize((1796, 1047)) # return im, (0,0) left, top, right, bot = win32gui.GetWindowRect(hwnd) width = right - left height = bot - top self.RealRate = (width, height) width = int(width / self.ScreenZoomRate) height = int(height / self.ScreenZoomRate) hwndDC = win32gui.GetWindowDC(hwnd) mfcDC = win32ui.CreateDCFromHandle(hwndDC) saveDC = mfcDC.CreateCompatibleDC() saveBitMap = win32ui.CreateBitmap() saveBitMap.CreateCompatibleBitmap(mfcDC, width, height) saveDC.SelectObject(saveBitMap) result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0) bmpinfo = saveBitMap.GetInfo() bmpstr = saveBitMap.GetBitmapBits(True) im = Image.frombuffer( "RGB", (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1) win32gui.DeleteObject(saveBitMap.GetHandle()) saveDC.DeleteDC() mfcDC.DeleteDC() win32gui.ReleaseDC(hwnd, hwndDC) im = im.resize((1798, 1047)) if region is not None: im = im.crop((region[0], region[1], region[0] + region[2], region[1] + region[3])) if result: return im, (left, top) else: return None, (0, 0) def LocateOnScreen(self, templateName, region, confidence=0.9): image, _ = self.Screenshot() return pyautogui.locate(needleImage=self.Pics[templateName], haystackImage=image, region=region, confidence=confidence) def ClickOnImage(self, templateName, region=None, confidence=0.9): image, _ = self.Screenshot() result = pyautogui.locate(needleImage=self.Pics[templateName], haystackImage=image, confidence=confidence, region=region) if result is not None: self.LeftClick((result[0], result[1])) def GetCardsState(self, image): st = time.time() imgCv = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR) states = [] cardStartPos = pyautogui.locate(needleImage=self.Pics["card_edge"], haystackImage=image, region=(313, 747, 1144, 200), confidence=0.85) if cardStartPos is None: return [] sx = cardStartPos[0] + 10 cardSearchFrom = 0 sy, sw, sh = 770, 50, 55 for i in range(0, 20): haveWhite = LocateOnImage(imgCv, self.PicsCV["card_white"], region=(sx + 50 * i, sy, 60, 60), confidence=0.9) if haveWhite is not None: break result = LocateOnImage(imgCv, self.PicsCV["card_upper_edge"], region=(sx + 50 * i, 720, sw, 50), confidence=0.9) checkSelect = 0 if result is not None: result = LocateOnImage(imgCv, self.PicsCV["card_overlap"], region=(sx + 50 * i, 750, sw, 50), confidence=0.85) if result is None: checkSelect = 1 states.append(checkSelect) print("GetStates Costs ", time.time()-st) return states def GetCards(self, image): st = time.time() imgCv = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR) tryCount = 10 cardStartPos = pyautogui.locate(needleImage=self.Pics["card_edge"], haystackImage=image, region=(313, 747, 1144, 200), confidence=0.80) while cardStartPos is None and tryCount > 0: cardStartPos = pyautogui.locate(needleImage=self.Pics["card_edge"], haystackImage=image, region=(313, 747, 1144, 200), confidence=0.80) print("找不到手牌起始位置") tryCount -= 1 self.sleep(150) print("start pos", cardStartPos) if cardStartPos is None: return [],[] sx = cardStartPos[0] + 10 AllCardsNC = ['rD', 'bX', '2', 'A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3'] hand_cards = [] select_map = [] cardSearchFrom = 0 sy, sw, sh = 770, 50, 55 for i in range(0, 20): # haveWhite = pyautogui.locate(needleImage=self.Pics["card_white"], haystackImage=image, # region=(sx + 50 * i, sy, 60, 60), confidence=0.8) haveWhite = LocateOnImage(imgCv, self.PicsCV["card_white"], region=(sx + 50 * i, sy, 60, 60), confidence=0.88) if haveWhite is not None: break result = LocateOnImage(imgCv, self.PicsCV["card_upper_edge"], region=(sx + 50 * i, 720, sw, 50), confidence=0.88) # result = pyautogui.locate(needleImage=self.Pics["card_upper_edge"], haystackImage=image, # region=(sx + 50 * i, 720, sw, 50), confidence=0.9) checkSelect = 0 if result is not None: # result = pyautogui.locate(needleImage=self.Pics['card_overlap'], haystackImage=image, # region=(sx + 50 * i, 750, sw, 50), confidence=0.85) result = LocateOnImage(imgCv, self.PicsCV["card_overlap"], region=(sx + 50 * i, 750, sw, 50), confidence=0.83) if result is None: checkSelect = 1 select_map.append(checkSelect) currCard = "" forBreak = False ci = cardSearchFrom while ci < len(AllCardsNC): if "r" in AllCardsNC[ci] or "b" in AllCardsNC[ci]: outerBreak = False result = LocateOnImage(imgCv, self.PicsCV["m" + AllCardsNC[ci]], region=(sx + 50 * i, sy - checkSelect * 25, sw, sh), confidence=0.89) # result = pyautogui.locate(needleImage=self.Pics["m" + AllCardsNC[ci]], haystackImage=image, # region=(sx + 50 * i, sy - checkSelect * 25, sw, sh), confidence=0.9) if result is not None: cardPos = (sx + 50 * i + sw // 2, sy - checkSelect * 25 + sh // 2) cardSearchFrom = ci currCard = AllCardsNC[ci][1] cardInfo = (currCard, cardPos) hand_cards.append(cardInfo) outerBreak = True break else: outerBreak = False for card_type in ["r", "b"]: result = LocateOnImage(imgCv, self.PicsCV["m" + card_type + AllCardsNC[ci]], region=(sx + 50 * i, sy - checkSelect * 25, sw, sh), confidence=0.91) # result = pyautogui.locate(needleImage=self.Pics["m" + card_type + AllCardsNC[ci]], # haystackImage=image, # region=(sx + 50 * i, sy - checkSelect * 25, sw, sh), confidence=0.9) if result is not None: cardPos = (sx + 50 * i + sw // 2, sy - checkSelect * 25 + sh // 2) cardSearchFrom = ci currCard = AllCardsNC[ci] cardInfo = (currCard, cardPos) hand_cards.append(cardInfo) outerBreak = True break if outerBreak: break if ci == len(AllCardsNC) - 1 and checkSelect == 0: checkSelect = 1 ci = cardSearchFrom - 1 ci += 1 if ci == len(AllCardsNC): forBreak = True if forBreak: break QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 10) print("GetCards Costs ", time.time()-st) return hand_cards, select_map def LeftClick(self, pos): x, y = pos x = (x / 1798) * self.RealRate[0] y = (y / 1047) * self.RealRate[1] x = int(x) y = int(y) lParam = win32api.MAKELONG(x, y) win32gui.PostMessage(self.Handle, WM_MOUSEMOVE, MK_LBUTTON, lParam) win32gui.PostMessage(self.Handle, WM_LBUTTONDOWN, MK_LBUTTON, lParam) win32gui.PostMessage(self.Handle, WM_LBUTTONUP, MK_LBUTTON, lParam) def SelectCards(self, cards, no_check=False): print("选择牌", cards) cards = [card for card in cards] tobeSelected = [] tobeSelected.extend(cards) image, windowPos = self.Screenshot() while image.size[0] == 0: image, windowPos = self.Screenshot() handCardsInfo, states = self.GetCards(image) cardSelectMap = [] for card in handCardsInfo: c = card[0] if c in tobeSelected: cardSelectMap.append(1) tobeSelected.remove(c) else: cardSelectMap.append(0) clickMap = [] handcards = [c[0] for c in handCardsInfo] for i in range(0, len(cardSelectMap)): if cardSelectMap[i] == states[i]: clickMap.append(0) else: clickMap.append(1) while 1 in clickMap: for i in range(0, len(clickMap)): if clickMap[i] == 1: self.LeftClick(handCardsInfo[i][1]) print("点击", handCardsInfo[i][1]) break time.sleep(0.1) if self.Interrupt: break if no_check: return image, _ = self.Screenshot() while image.size[0] == 0: image, windowPos = self.Screenshot() states = self.GetCardsState(image) clickMap = [] for i in range(0, len(cardSelectMap)): if cardSelectMap[i] == states[i]: clickMap.append(0) else: clickMap.append(1) QtWidgets.QApplication.processEvents(QEventLoop.AllEvents, 10)