diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 15e6b610..7ef6517e 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -7,7 +7,7 @@ jobs: runs-on: windows-latest strategy: matrix: - python-version: ["3.10"] + python-version: ["3.11"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} diff --git a/arena.py b/arena.py index 617c9ee7..108fe663 100644 --- a/arena.py +++ b/arena.py @@ -40,11 +40,13 @@ def fix_bench_state(self) -> None: for index, slot in enumerate(self.bench): if slot is None and bench_occupied[index]: mk_functions.right_click(screen_coords.BENCH_LOC[index].get_coords()) - champ_name: str = ocr.get_text( - screenxy=screen_coords.PANEL_NAME_LOC.get_coords(), - scale=3, - psm=7, - whitelist=ocr.ALPHABET_WHITELIST, + champ_name: str = arena_functions.valid_champ( + ocr.get_text( + screenxy=screen_coords.PANEL_NAME_LOC.get_coords(), + scale=3, + psm=7, + whitelist=ocr.ALPHABET_WHITELIST, + ) ) if self.champs_to_buy.get(champ_name, 0) > 0: print( @@ -60,6 +62,9 @@ def fix_bench_state(self) -> None: ) self.champs_to_buy[champ_name] -= 1 else: + print( + f" The unknown champion {champ_name} not exists in comps, selling it." + ) self.bench[index] = "?" continue if isinstance(slot, str) and not bench_occupied[index]: @@ -357,6 +362,31 @@ def spend_gold(self, speedy=False) -> None: mk_functions.reroll() print(" Rerolling shop") shop: list = arena_functions.get_shop() + + # For set 11 encounter round shop delay and choose items popup + for _ in range(15): + if speedy: + return + if all(champ[1] == "" for champ in shop): + print(" Waiting encounter round animation ends") + sleep(1) + anvil_msg: str = ocr.get_text( + screenxy=screen_coords.ANVIL_MSG_POS.get_coords(), + scale=3, + psm=7, + whitelist=ocr.ALPHABET_WHITELIST, + ) + if anvil_msg in ["ChooseOne", "Feelinglucky"]: + sleep(2) + print(" Choosing item") + mk_functions.left_click(screen_coords.BUY_LOC[2].get_coords()) + sleep(1.5) + shop: list = arena_functions.get_shop() + break + shop: list = arena_functions.get_shop() + else: + break + print(f" Shop: {shop}") for champion in shop: if ( @@ -406,7 +436,7 @@ def pick_augment(self) -> None: screenxy=coords.get_coords(), scale=3, psm=7 ) augments.append(augment) - print(augments) + print(f" Augments: {augments}") if len(augments) == 3 and "" not in augments: break diff --git a/arena_functions.py b/arena_functions.py index 914fe3b4..e7717d19 100644 --- a/arena_functions.py +++ b/arena_functions.py @@ -74,7 +74,7 @@ def get_champ( ) -> str: """Returns a tuple containing the shop position and champion name""" champ: str = screen_capture.crop(name_pos.get_coords()) - champ: str = ocr.get_text_from_image(image=champ, whitelist="") + champ: str = ocr.get_text_from_image(image=champ, whitelist=ocr.ALPHABET_WHITELIST) shop_array.append((shop_pos, valid_champ(champ))) @@ -100,7 +100,8 @@ def empty_slot() -> int: for slot, positions in enumerate(screen_coords.BENCH_HEALTH_POS): screen_capture = ImageGrab.grab(bbox=positions.get_coords()) screenshot_array = np.array(screen_capture) - if not (np.abs(screenshot_array - (0, 255, 18)) <= 3).all(axis=2).any(): + is_health_color = np.all(screenshot_array == [0, 255, 18], axis=-1) + if not any(np.convolve(is_health_color.reshape(-1), np.ones(5), mode='valid')): return slot # Slot 0-8 return -1 # No empty slot @@ -111,10 +112,9 @@ def bench_occupied_check() -> list: for positions in screen_coords.BENCH_HEALTH_POS: screen_capture = ImageGrab.grab(bbox=positions.get_coords()) screenshot_array = np.array(screen_capture) - if not (np.abs(screenshot_array - (0, 255, 18)) <= 2).all(axis=2).any(): - bench_occupied.append(False) - else: - bench_occupied.append(True) + is_health_color = np.all(screenshot_array == [0, 255, 18], axis=-1) + occupied = any(np.convolve(is_health_color.reshape(-1), np.ones(5), mode='valid')) + bench_occupied.append(occupied) return bench_occupied diff --git a/comps.py b/comps.py index a5b89090..51453c8b 100644 --- a/comps.py +++ b/comps.py @@ -2,70 +2,66 @@ Team composition used by the bot Comps come from https://tftactics.gg/tierlist/team-comps Items are in camel case and a-Z -The "headliner" tag represents a trait from bottom to top. -Set to True if you want it in your team. -Only final comp champion will become headliner and need to set the corresponding 'headliner' tag to True. -e.g. Only want "Sentinel" Ekko, set it to "headliner": [True, False, False] -e.g.2 want either "Sentinel" or "True Damage" Ekko, set it to "headliner": [True, False, True] +Items will be placed on the top champion first, and prioritize building items on the left. """ COMP = { - "Irelia": { + "Tristana": { "board_position": 6, - "items": ["GiantSlayer", "GuinsoosRageblade", "InfinityEdge"], + "items": ["GuinsoosRageblade", "InfinityEdge", "LastWhisper"], "level": 3, "final_comp": True }, - "Galio": { - "board_position": 25, - "items": ["BrambleVest", "DragonsClaw", "WarmogsArmor"], + "Volibear": { + "board_position": 27, + "items": ["Bloodthirster", "TitansResolve", "WarmogsArmor"], "level": 3, "final_comp": True }, - "Sivir": { - "board_position": 5, + "Irelia": { + "board_position": 0, "items": ["GiantSlayer", "GuinsoosRageblade", "InfinityEdge"], "level": 2, "final_comp": True }, - "Zoe": { - "board_position": 4, - "items": ["JeweledGauntlet","RabadonsDeathcap","SpearofShojin"], + "Wukong": { + "board_position": 22, + "items": ["HandofJustice"], "level": 2, "final_comp": True }, - "Illaoi": { - "board_position": 23, - "items": ["BrambleVest", "DragonsClaw", "WarmogsArmor"], + "LeeSin": { + "board_position": 25, + "items": [], "level": 2, "final_comp": True }, - "Zyra": { - "board_position": 1, - "items": [], + "Diana": { + "board_position": 26, + "items": ["BrambleVest", "DragonsClaw", "WarmogsArmor"], "level": 2, "final_comp": True }, - "Riven": { - "board_position": 27, - "items": ["ThiefsGloves"], + "Qiyana": { + "board_position": 24, + "items": [], "level": 2, "final_comp": True }, - "Garen": { - "board_position": 21, + "Darius": { + "board_position": 23, "items": [], "level": 2, "final_comp": True }, - "Kobuko": { - "board_position": 22, + "Yorick": { + "board_position": 20, "items": [], "level": 2, "final_comp": False }, - "Teemo": { - "board_position": 13, + "Yasuo": { + "board_position": 19, "items": [], "level": 2, "final_comp": False @@ -78,7 +74,9 @@ # For those augments names with suffixes like I, II, III, such as 'Cybernetic Uplink II', # You only need to add 'Cybernetic Uplink' in the list to cover all three levels. AUGMENTS: list[str] = [ - "That's Jazz Baby!", + "Tiny but Deadly", + "Pumping up", + "Extended Duel", "You Have My Bow", "Blistering Strikes", "Buried Treasures", @@ -133,7 +131,8 @@ "Scapegoat", "Wandering Trainer", "Recombobulator", - "Forge" + "Forge", + "Crest Test Dummies" ] diff --git a/game.py b/game.py index 69be262a..2aa07d64 100644 --- a/game.py +++ b/game.py @@ -22,7 +22,7 @@ class Game: def __init__(self, message_queue: multiprocessing.Queue) -> None: self.message_queue = message_queue self.arena = Arena(self.message_queue) - self.round = "0-0" + self.round: str = "0-0" self.time: None = None self.forfeit_time: int = settings.FORFEIT_TIME + random.randint(50, 150) self.found_window = False @@ -71,9 +71,11 @@ def check_failed_to_connect_window(self) -> bool: """Check "Failed to Connect" windows and try to reconnect""" hwnd = win32gui.FindWindow(None, "Failed to Connect") if hwnd: - print(" Found \"Failed to Connect\" window, trying to exit and reconnect") + print(' Found "Failed to Connect" window, trying to exit and reconnect') if reconnect_button := win32gui.FindWindowEx(hwnd, 0, "Button", None): - if cancel_button := win32gui.FindWindowEx(hwnd, reconnect_button, "Button", None): + if cancel_button := win32gui.FindWindowEx( + hwnd, reconnect_button, "Button", None + ): print(" Exiting the game.") win32gui.SendMessage(cancel_button, BM_CLICK, 0, 0) return True @@ -82,7 +84,6 @@ def check_failed_to_connect_window(self) -> bool: print(" Reconnect button not found.") return False - def game_loop(self) -> None: """Loop that runs while the game is active, handles calling the correct tasks for round and exiting game""" ran_round: str = None @@ -105,7 +106,7 @@ def game_loop(self) -> None: break last_game_health = game_health - self.round: str = game_functions.get_round() + self.round = game_functions.get_round() if ( settings.FORFEIT @@ -140,7 +141,8 @@ def second_round(self) -> None: if any(result): break self.arena.bench[result.index(True)] = "?" - self.arena.move_unknown() + for _ in range(arena_functions.get_level()): + self.arena.move_unknown() self.end_round_tasks() def carousel_round(self) -> None: @@ -202,10 +204,7 @@ def pvp_round(self) -> None: self.arena.bench_cleanup() if self.round in game_assets.ANVIL_ROUNDS: self.arena.clear_anvil() - if self.round in game_assets.PICKUP_ROUNDS: - self.arena.spend_gold(speedy=True) - else: - self.arena.spend_gold() + self.arena.spend_gold(speedy=self.round in game_assets.PICKUP_ROUNDS) self.arena.move_champions() self.arena.replace_unknown() if self.arena.final_comp: