截至24/10/15,由于使用投降法上分的人太多,传说人数已经突破历史了,今天的热更新官方似乎更改了匹配逻辑,投降法的适用性可能得重新评估了o(TヘTo)~
前言
炉石终于回归啦ヾ(≧▽≦*)o,虽然过去也没有经常玩但是既然回归那么福利肯定要好好吃进肚子啦 (╹ڡ╹ ),正好赠送了全年的卡牌,仔细研究了一下准备试试天梯,不出意外是被大家的开局投降操作震惊到了,不对!一定有什么漏掉的知识!一番研究过后终于是找到了答案,快速投降降低胜率,再利用匹配机制实现快速无痛上分,那么仅有的努力只剩下枯燥的点击过程了,鉴于胜率降低需要大基数对局量,那么不如顺天意承因果,简单写一个脚本实现"睡后上分"!
当然必须声明写脚本不含自动决策且在炉石中投降不影响其他玩家,因此想要冲击排名的小伙伴还是需要靠自己强大的思维决策和快速准确的计算能力哦 (ง •_•)ง !
演示
- 游戏分辨率为 2560*1080
- 窗口化
原理
基本思路非常简单,由于只是娱乐并不追求极快速的灵敏识别,因此识别这方面不需要自己再进行算法的原型开发和优化,简单调用cv库即可,在此基础上再加上一些鼠标逻辑的控制就可以实现整个投降逻辑啦~,当然针对游戏的人机识别(实测主要体现在当短时间进行多次对局后会极大降低匹配优先级导致长时间处于等待队列),因此算法中需要一些小的细节优化来重置优先级,当然由于刚回归服务器不稳定(大大的吐槽一下!相信这两天每一个玩家都有同感~),偶尔炉石会有bug导致意外退出,后续也会视情况加上自动重启逻辑。
源码
import random
import PIL.Image
import cv2
import pyautogui
import numpy as np
import time
from screeninfo import get_monitors
# 加载按钮图像
# 按钮的模板图像
img_start_button = cv2.imread('st_button.png', 0)
img_end_button = cv2.imread('end_button.png', 0)
img_confirm_button = cv2.imread('confirm_button.png', 0)
img_victory_button = cv2.imread('victory.png', 0)
threshold = 0.8 # 匹配精度阈值
screen_width, screen_height = pyautogui.size()
center_position = (screen_width // 2, screen_height // 2)
screen_region = (-5000, -5000, 10000, 10000)
monitors = get_monitors()
def find_st_button_and_click():
# 截取屏幕截图
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2GRAY)
# 在截图中查找按钮图像
result = cv2.matchTemplate(screenshot, img_start_button, cv2.TM_CCOEFF_NORMED)
# 获取匹配结果的位置
loc = np.where(result >= threshold)
if len(loc[0]) > 0: # 如果找到了匹配项
# 获取第一个匹配的位置,并加上位置偏移量
button_position = (loc[1][0] + img_start_button.shape[1] / 2, loc[0][0] + img_start_button.shape[0] / 2)
# 点击按钮
pyautogui.click(button_position)
# print("START Button clicked at position:", button_position)
return True
# print("START Button not found.")
return False
def find_extra_task_dialog():
time.sleep(0.5) # 这里需要加上短暂延迟,否则对话框加载不完全像素模糊会降低识别精度
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2GRAY)
result = cv2.matchTemplate(screenshot, img_confirm_button, cv2.TM_CCOEFF_NORMED)
# 获取匹配结果的位置
loc = np.where(result >= threshold)
if len(loc[0]) > 0: # 如果找到了匹配项
# 获取第一个匹配的位置,并加上位置偏移量
button_position = (loc[1][0] + img_confirm_button.shape[1] / 2, loc[0][0] + img_confirm_button.shape[0] / 2)
# 点击按钮
pyautogui.click(button_position)
# print("Confirm Button clicked at position:", button_position)
def solve_victory():
time.sleep(0.2) # 这里需要加上短暂延迟,否则对话框消失不完全会降低识别精度
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2GRAY)
result = cv2.matchTemplate(screenshot, img_victory_button, cv2.TM_CCOEFF_NORMED)
# 获取匹配结果的位置
loc = np.where(result >= threshold)
if len(loc[0]) > 0: # 如果找到了匹配项
# 获取第一个匹配的位置,并加上位置偏移量
button_position = (loc[1][0] + img_confirm_button.shape[1] / 2, loc[0][0] + img_confirm_button.shape[0] / 2)
# 点击
pyautogui.click(center_position)
return True
# print("Confirm Button clicked at position:", button_position)
return False
def find_end_button_and_click():
while True:
pyautogui.press('esc')
time.sleep(0.5) # 这里需要加上短暂延迟,否则对话框加载不完全像素模糊会降低识别精度
# 截取屏幕截图
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2GRAY)
# 在截图中查找按钮图像
result = cv2.matchTemplate(screenshot, img_end_button, cv2.TM_CCOEFF_NORMED)
# 获取匹配结果的位置
loc = np.where(result >= threshold)
if len(loc[0]) > 0: # 如果找到了匹配项
# 获取第一个匹配的位置,并加上位置偏移量
button_position = (loc[1][0] + img_end_button.shape[1] / 2, loc[0][0] + img_end_button.shape[0] / 2)
# 点击按钮
pyautogui.click(button_position)
# print("END Button clicked at position:", button_position)
find_extra_task_dialog()
# 结束循环
break
else:
time.sleep(5)
pyautogui.press('esc')
flag = solve_victory()
if flag:
break
if __name__ == "__main__":
summ = 0
start_time = time.time()
while True:
res = find_st_button_and_click()
if res is False:
time.sleep(random.randrange(1, 2))
pyautogui.click(center_position)
continue
else:
summ += 1
if summ == 1:
print("投降战法第:", summ, "轮 ", "当前平均效率:", "-------暂无数据-------", "s/epoch")
else:
print("投降战法第:", summ, "轮 ", "当前平均效率:", (time.time() - start_time) / (summ - 1), "s/epoch")
time.sleep(15) # 等待匹配时间
find_end_button_and_click()