diff --git a/.gitignore b/.gitignore index 3699c08..5184f55 100644 --- a/.gitignore +++ b/.gitignore @@ -129,4 +129,7 @@ dmypy.json .pyre/ # Visual Studio -.vs \ No newline at end of file +.vs + +# swy-bot +saved diff --git a/README.md b/README.md index c35105b..4cb82e1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ PS: 每次做菜都剩下一大堆食材好烦啊 PSS: 那个活动的跑酷恶心死我了啊啊啊啊 +**空桑好, 策划爬** + ## 食用方式 详见程序内指引 @@ -45,6 +47,8 @@ PSS: 那个活动的跑酷恶心死我了啊啊啊啊 2021/2/21 使用装饰器注册挂机任务, 完成Scrcpy模式, 更新版本号至V1.3 +2021/2/23 重做客潮挂机任务, 更新版本号至V1.4 + ## 厨师 详见CONTRIBUTOR.md diff --git a/main.py b/main.py index cfbed26..a36f8ef 100644 --- a/main.py +++ b/main.py @@ -19,7 +19,7 @@ def main(): setnodpi() settitle("欢迎使用食物语挂机脚本") print('''============================================= -食物语挂机脚本 V1.3 作者: WC +食物语挂机脚本 V1.4 作者: WC 本脚本仅供个人代肝使用, 严禁用于商业用途 使用本脚本造成的一切法律纠纷由使用者自行承担 项目地址: https://github.com/DawningW/swy-bot @@ -31,8 +31,9 @@ def main(): 2. ADB模式(需手机连接电脑开启调试模式并打开食物语) 3. 混合模式(使用scrcpy快速获取手机截屏并模拟点击)(*推荐*) 4. 调试模式(将读取程序目录下的test.png并进行图像识别) -8. 线性规划做菜计算器 -9. 用默认浏览器打开食物语wiki +7. 线性规划做菜计算器 +8. 用默认浏览器打开食物语wiki +9. 打开截图文件夹 0. 退出''') str = input("请选择: ") global player @@ -46,12 +47,15 @@ def main(): player = PlayerScrcpy() elif str == "4": player = PlayerTest() - elif str == "8": + elif str == "7": print("正在开发中") continue - elif str == "9": + elif str == "8": os.system("start https://wiki.biligame.com/swy"); continue + elif str == "9": + os.system("start saved"); + continue else: continue if (player.init()): @@ -87,6 +91,7 @@ def run(): while not canceled: times += 1 print("第 {} 次运行脚本: {}".format(times, task.name)) + task.init() origin = time.perf_counter() phase = Phases.BEGIN while True: diff --git a/player.py b/player.py index 0cc4276..8b3f943 100644 --- a/player.py +++ b/player.py @@ -217,7 +217,7 @@ class PlayerScrcpy(PlayerBase): def init(self): super().init() - self.client = ScrcpyClient() + self.client = ScrcpyClient(max_fps=30, queue_length=2) if not self.client.start(): print("连接失败") return False diff --git a/scrcpy.py b/scrcpy.py index 1c56979..4d6d4cd 100644 --- a/scrcpy.py +++ b/scrcpy.py @@ -33,6 +33,7 @@ def __init__(self, max_size=0, bit_rate=8000000, max_fps=0, crop='-', :param max_size: frame width that will be broadcast from android server :param bit_rate: :param max_fps: 0 means not max fps + :param crop: :param libs_path: path to 'scrcpy-server.jar' :param adb_path: path to ADB :param ip: scrcpy server IP diff --git a/task.py b/task.py index 56032c5..4145a7f 100644 --- a/task.py +++ b/task.py @@ -6,7 +6,7 @@ import math import numpy import cv2 -from utils import readimage +from utils import readimage, writeimage class Phases(IntEnum): """任务执行的阶段""" @@ -47,6 +47,9 @@ class TaskBase(object): def __init__(self): """初始化""" + return + + def init(self): self.image = None return @@ -69,64 +72,132 @@ def getImageCache(self): @Task("自动客潮", "请将界面停留在餐厅") class TaskKeChao(TaskBase): """客潮自动化""" + def __init__(self): super().__init__() self.templateButton = readimage("kechao_btn") self.templateDish = readimage("kechao_dish_part") self.templateTitle = readimage("kechao_title_part") + return + def init(self): + super().init() self.lastTime = 0 - self.dialog = False + self.step = 0 + # 0:餐厅界面 1:客潮对话框 2:客潮进行中 3:客潮结束结算界面 self.pointCache = [] return def begin(self, player, t): """需要玩家位于餐厅界面""" self.image = player.screenshot() - if not self.dialog: + if self.step == 0: points = findcircle(self.image, 25) for x, y in points: if x > (self.image.shape[1] * 0.9) and y < (self.image.shape[0] * 0.2): + # 找到客潮按钮 player.clickaround(x, y) - self.dialog = True - else: - points = findtemplate(self.image, self.templateButton) - for x, y in points: - player.clickaround(x, y) - time.sleep(1) - return Results.SUCCESS + self.lastTime = t + self.step = 1 + return Results.PASS + if t - self.lastTime > 3: + # 没找到客潮按钮且超时 + print("未找到客潮按钮, 请确认您正位于餐厅界面") + return Results.FAIL + elif self.step == 1 or self.step == 2: + if t - self.lastTime > 2: + points = findtemplate(self.image, self.templateButton) + for x, y in points: + # 找到开始按钮 + if self.step == 2: + print("点击按钮后没有开始, 可能是客潮开启次数不足") + return Results.FAIL + player.clickaround(x, y) + self.lastTime = t + self.step = 2 + return Results.PASS + # 找不到开始按钮 + if self.step == 2: + # 点过开始按钮了, 进入客潮 + self.lastTime = t + print("进入客潮") + return Results.SUCCESS + # 还没点过开始按钮, 回退到第0步 + self.lastTime = t + self.step = 0 + return Results.PASS return Results.PASS def run(self, player, t): """客潮挂机中""" self.image = player.screenshot() + # 处理点的缓存 + self.pointCache = [(x, y, time - 1) for x, y, time in self.pointCache if time > 1] + # 识别圆来寻找菜(旧版本用模版匹配, 效果不好) points = findcircle(self.image, 25) points2 = [] for x, y in points: if x > (self.image.shape[1] * 0.9): continue - if y > (self.image.shape[0] * 0.8): return Results.SUCCESS + if y > (self.image.shape[0] * 0.8): + # 客潮结束回到餐厅 + self.lastTime = t + self.step = 3 + print("客潮结束") + return Results.SUCCESS cv2.circle(self.image, (x, y), 25, (0, 0, 255), 3) - points2.append((x, y)) + if not self.containPoint(x, y): + points2.append((x, y)) if len(points2) > 0: x, y = random.choice(points2) player.clickaround(x, y) + self.pointCache.append((x, y, 10)) + self.lastTime = t + return Results.PASS + if t - self.lastTime > 15: + # 没人点菜, 停止挂机? + print("超过15秒钟没有客人点菜了, 停止挂机") + return Results.FAIL return Results.PASS def end(self, player, t): """结束客潮""" self.image = player.screenshot() - points = findtemplate(self.image, self.templateTitle) - for x, y in points: - time.sleep(2) - player.clickaround(x, y) - time.sleep(2) - return Results.SUCCESS + if self.step == 3: + if t - self.lastTime > 2: + points = findtemplate(self.image, self.templateTitle) + for x, y in points: + # 正位于客潮结算界面 + filename = "KeChao_" + time.strftime("%Y-%m-%d-%H-%M-%S") + writeimage(filename, self.image) + print("已将客潮结算界面截图保存至: saved/%s.png" % filename) + player.clickaround(x, y) + self.lastTime = t + return Results.PASS + # 结算完了 + self.lastTime = t + return Results.SUCCESS return Results.PASS + def containPoint(self, x, y): + for cx, cy, time in self.pointCache: + if math.sqrt(math.pow(int(x) - int(cx), 2) + math.pow(int(y) - int(cy), 2)) < 5: + return True + return False + @Task("自动小游戏", "尚未编写") class TaskMiniGames(TaskBase): """活动小游戏 算了放弃了 毁灭吧赶紧的""" + def __init__(self): + super().__init__() + self.lastTime = 0 + return + + def begin(self, player, t): + """开始小游戏""" + print("我不想做了") + return Results.FAIL + def findtemplate(image, template, threshold = 0.75, outline = False): theight, twidth = template.shape[:2] result = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) diff --git a/utils.py b/utils.py index 03d5eac..23a0f4d 100644 --- a/utils.py +++ b/utils.py @@ -6,5 +6,5 @@ def readimage(name): return cv2.imread("./data/" + name + ".png", cv2.IMREAD_UNCHANGED) def writeimage(name, image): - cv2.imwrite("./data/saved/" + name + ".png", image, [int(cv2.IMWRITE_PNG_COMPRESSION), 3]) + cv2.imwrite("./saved/" + name + ".png", image, [int(cv2.IMWRITE_PNG_COMPRESSION), 3]) return