diff --git a/clashroyalebuildabot/bot/bot.py b/clashroyalebuildabot/bot/bot.py index 2225100..b86440f 100644 --- a/clashroyalebuildabot/bot/bot.py +++ b/clashroyalebuildabot/bot/bot.py @@ -1,8 +1,12 @@ +import os +import sys import time from loguru import logger +import yaml from clashroyalebuildabot.constants import ALLY_TILES +from clashroyalebuildabot.constants import DEBUG_DIR from clashroyalebuildabot.constants import DISPLAY_CARD_DELTA_X from clashroyalebuildabot.constants import DISPLAY_CARD_HEIGHT from clashroyalebuildabot.constants import DISPLAY_CARD_INIT_X @@ -11,6 +15,7 @@ from clashroyalebuildabot.constants import DISPLAY_HEIGHT from clashroyalebuildabot.constants import LEFT_PRINCESS_TILES from clashroyalebuildabot.constants import RIGHT_PRINCESS_TILES +from clashroyalebuildabot.constants import SRC_DIR from clashroyalebuildabot.constants import TILE_HEIGHT from clashroyalebuildabot.constants import TILE_INIT_X from clashroyalebuildabot.constants import TILE_INIT_Y @@ -43,6 +48,22 @@ def __init__( self.detector = Detector(cards, debug=self.debug) self.state = None + self._setup_logger() + + @staticmethod + def _setup_logger(): + config_path = os.path.join(SRC_DIR, "config.yaml") + with open(config_path, encoding="utf-8") as file: + config = yaml.safe_load(file) + log_level = config.get("bot", {}).get("log_level", "INFO").upper() + logger.remove() + logger.add(sys.stdout, level=log_level) + logger.add( + os.path.join(DEBUG_DIR, "bot.log"), + rotation="500 MB", + level=log_level, + ) + @staticmethod def _get_nearest_tile(x, y): tile_x = round(((x - TILE_INIT_X) / TILE_WIDTH) - 0.5) @@ -94,14 +115,11 @@ def get_actions(self): return actions def set_state(self): - try: - screenshot = self.emulator.take_screenshot() - self.state = self.detector.run(screenshot) - if self.auto_start and self.state.screen != Screens.IN_GAME: - self.emulator.click(*self.state.screen.click_xy) - time.sleep(2) - except Exception as e: - logger.error(f"Error occurred while taking screenshot: {e}") + screenshot = self.emulator.take_screenshot() + self.state = self.detector.run(screenshot) + if self.auto_start and self.state.screen != Screens.IN_GAME: + self.emulator.click(*self.state.screen.click_xy) + time.sleep(2) def play_action(self, action): card_centre = self._get_card_centre(action.index) diff --git a/clashroyalebuildabot/bot/example/custom_bot.py b/clashroyalebuildabot/bot/example/custom_bot.py index fcc4c27..4006179 100644 --- a/clashroyalebuildabot/bot/example/custom_bot.py +++ b/clashroyalebuildabot/bot/example/custom_bot.py @@ -1,20 +1,16 @@ -import os import random import subprocess -import sys import time from loguru import logger -import yaml +from clashroyalebuildabot import Screens from clashroyalebuildabot.bot import Bot from clashroyalebuildabot.bot.example.custom_action import CustomAction -from clashroyalebuildabot.constants import DEBUG_DIR from clashroyalebuildabot.constants import DISPLAY_HEIGHT from clashroyalebuildabot.constants import DISPLAY_WIDTH from clashroyalebuildabot.constants import SCREENSHOT_HEIGHT from clashroyalebuildabot.constants import SCREENSHOT_WIDTH -from clashroyalebuildabot.constants import SRC_DIR from clashroyalebuildabot.namespaces.cards import Cards @@ -31,25 +27,12 @@ class CustomBot(Bot): ] def __init__(self, cards=None, debug=False): - config_path = os.path.join(SRC_DIR, "config.yaml") - with open(config_path, encoding="utf-8") as file: - config = yaml.safe_load(file) - - log_level = config.get("bot", {}).get("log_level", "INFO").upper() - - logger.remove() - logger.add(sys.stdout, level=log_level) - logger.add( - os.path.join(DEBUG_DIR, "bot.log"), - rotation="500 MB", - level=log_level, - ) - - if cards is None: - cards = self.PRESET_DECK - if set(cards) != set(self.PRESET_DECK): - raise ValueError(f"CustomBot must use cards: {self.PRESET_DECK}") - super().__init__(cards, CustomAction, debug=debug) + if cards is not None: + raise ValueError( + f"CustomBot uses a preset deck: {self.PRESET_DECK}." + "Use cards=None instead." + ) + super().__init__(self.PRESET_DECK, CustomAction, debug=debug) self.end_of_game_clicked = False self.pause_until = 0 self.scale_x = DISPLAY_WIDTH / SCREENSHOT_WIDTH @@ -89,7 +72,7 @@ def _end_of_game(self): actions = self.get_actions() logger.info(f"Actions after end of game: {actions}") - if self.state["screen"] == "lobby": + if self.state.screen == Screens.LOBBY: logger.debug("Lobby detected, resuming normal operation.") return diff --git a/clashroyalebuildabot/bot/two_six_hog_cycle/two_six_hog_cycle_bot.py b/clashroyalebuildabot/bot/two_six_hog_cycle/two_six_hog_cycle_bot.py index 7c68641..27e538a 100644 --- a/clashroyalebuildabot/bot/two_six_hog_cycle/two_six_hog_cycle_bot.py +++ b/clashroyalebuildabot/bot/two_six_hog_cycle/two_six_hog_cycle_bot.py @@ -15,22 +15,24 @@ class TwoSixHogCycle(Bot): - def __init__(self, cards, debug=True): - preset_deck = { - Cards.HOG_RIDER, - Cards.THE_LOG, - Cards.FIREBALL, - Cards.ICE_SPIRIT, - Cards.ICE_GOLEM, - Cards.SKELETONS, - Cards.CANNON, - Cards.MUSKETEER, - } - if set(cards) != preset_deck: + PRESET_DECK = { + Cards.HOG_RIDER, + Cards.THE_LOG, + Cards.FIREBALL, + Cards.ICE_SPIRIT, + Cards.ICE_GOLEM, + Cards.SKELETONS, + Cards.CANNON, + Cards.MUSKETEER, + } + + def __init__(self, cards=None, debug=True): + if cards is not None: raise ValueError( - f"You must use the preset deck with cards {preset_deck} for TwoSixHogCycleBot" + f"CustomBot uses a preset deck: {self.PRESET_DECK}." + "Use cards=None instead." ) - super().__init__(cards, TwoSixHogCycleAction, debug=debug) + super().__init__(self.PRESET_DECK, TwoSixHogCycleAction, debug=debug) def _preprocess(self): """ @@ -39,7 +41,7 @@ def _preprocess(self): Estimate the tile of each unit to be the bottom of their bounding box """ for side in ["ally", "enemy"]: - for v in self.state["units"][side].values(): + for v in self.state.units[side].values(): for unit in v["positions"]: bbox = unit["bounding_box"] bbox[0] *= DISPLAY_WIDTH / SCREENSHOT_WIDTH diff --git a/clashroyalebuildabot/debugger.py b/clashroyalebuildabot/debugger.py index e4165b2..a016839 100644 --- a/clashroyalebuildabot/debugger.py +++ b/clashroyalebuildabot/debugger.py @@ -38,7 +38,7 @@ def __init__(self): def _write_label(image, state, basename): labels = [] for side in ["ally", "enemy"]: - for unit_name, v in state["units"][side].items(): + for unit_name, v in state.units[side].items(): for i in v["positions"]: bbox = i["bounding_box"] xc = (bbox[0] + bbox[2]) / (2 * image.width) @@ -67,12 +67,12 @@ def _draw_text(self, d, bbox, text, rgba=(0, 0, 0, 255)): def _write_image(self, image, state, basename): d = ImageDraw.Draw(image, "RGBA") - for v in state["numbers"].values(): + for v in state.numbers.values(): d.rectangle(tuple(v["bounding_box"])) self._draw_text(d, v["bounding_box"], str(v["number"])) for side in ["ally", "enemy"]: - for unit_name, v in state["units"][side].items(): + for unit_name, v in state.units[side].items(): colour_idx = self.unit_names.index(unit_name) % len( self._COLOUR_AND_RGBA ) @@ -80,9 +80,9 @@ def _write_image(self, image, state, basename): for i in v["positions"]: self._draw_text(d, i["bounding_box"], unit_name, rgba) - for card, position in zip(state["cards"], CARD_CONFIG): + for card, position in zip(state.cards, CARD_CONFIG): d.rectangle(tuple(position)) - self._draw_text(d, position, card["name"]) + self._draw_text(d, position, card.name) image.save(os.path.join(SCREENSHOTS_DIR, f"{basename}.png")) diff --git a/clashroyalebuildabot/detectors/onnx_detector.py b/clashroyalebuildabot/detectors/onnx_detector.py index 41956b6..bba967a 100644 --- a/clashroyalebuildabot/detectors/onnx_detector.py +++ b/clashroyalebuildabot/detectors/onnx_detector.py @@ -1,13 +1,18 @@ import numpy as np -import onnxruntime +import onnxruntime as ort class OnnxDetector: def __init__(self, model_path): self.model_path = model_path - self.sess = onnxruntime.InferenceSession( + + providers = list( + set(ort.get_available_providers()) + & {"CUDAExecutionProvider", "CPUExecutionProvider"} + ) + self.sess = ort.InferenceSession( self.model_path, - providers=["CPUExecutionProvider", "CUDAExecutionProvider"], + providers=providers, ) self.output_name = self.sess.get_outputs()[0].name diff --git a/clashroyalebuildabot/emulator.py b/clashroyalebuildabot/emulator.py index 473a2e8..a727a50 100644 --- a/clashroyalebuildabot/emulator.py +++ b/clashroyalebuildabot/emulator.py @@ -1,5 +1,4 @@ import os -import sys from adb_shell.adb_device import AdbDeviceTcp from loguru import logger @@ -17,15 +16,6 @@ def __init__(self): with open(config_path, encoding="utf-8") as file: config = yaml.safe_load(file) - log_level = config.get("bot", {}).get("log_level", "INFO").upper() - logger.remove() - logger.add(sys.stdout, level=log_level) - logger.add( - os.path.join(SRC_DIR, "bot.log"), - rotation="500 MB", - level=log_level, - ) - adb_config = config["adb"] device_ip = adb_config["ip"] device_port = adb_config["port"] diff --git a/clashroyalebuildabot/namespaces/units.py b/clashroyalebuildabot/namespaces/units.py index 4212a2d..eedc0c2 100644 --- a/clashroyalebuildabot/namespaces/units.py +++ b/clashroyalebuildabot/namespaces/units.py @@ -21,7 +21,7 @@ class _UnitsNamespace: BRAWLER: Unit = ("brawler", "troop", "ground", "ground") CANNON: Unit = ("cannon", "building", "ground", "ground") DARK_PRINCE: Unit = ("dark_prince", "troop", "ground", "ground") - ELIXIR_COLLECTOR: Unit = ("elixir_collector", "building") + ELIXIR_COLLECTOR: Unit = ("elixir_collector", "building", None, None) FURNACE: Unit = ("furnace", "building", None, None) GIANT: Unit = ("giant", "troop", "ground", "ground") GOBLIN: Unit = ("goblin", "troop", "ground", "ground") diff --git a/main.py b/main.py index a3e4be1..26950e3 100644 --- a/main.py +++ b/main.py @@ -1,14 +1,9 @@ from datetime import datetime -import os import sys import threading import time -from loguru import logger -import yaml - from clashroyalebuildabot.bot.example.custom_bot import CustomBot -from clashroyalebuildabot.constants import DEBUG_DIR start_time = datetime.now() @@ -30,20 +25,6 @@ def main(): if __name__ == "__main__": - config_path = os.path.join( - os.path.dirname(__file__), "clashroyalebuildabot/config.yaml" - ) - with open(config_path, encoding="utf-8") as file: - config = yaml.safe_load(file) - - log_level = config.get("bot", {}).get("log_level", "INFO").upper() - - logger.remove() - logger.add(sys.stdout, level=log_level) - logger.add( - os.path.join(DEBUG_DIR, "bot.log"), rotation="500 MB", level=log_level - ) - title_thread = threading.Thread(target=update_terminal_title, daemon=True) title_thread.start() main()