Exemplo n.º 1
0
    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        self.api = API(game_state, player_name)
        self.my_player = game_state.get_all_players()[player_name]
        self.board = game_state.get_pvp_board()
        self.curr_pos = self.my_player.get_position()

        self.logger.info("In make_decision")

        last_action, type = self.memory.get_value("last_action", str)
        if last_action is not None and last_action == "PICKUP":
            self.memory.set_value("last_action", "EQUIP")
            return CharacterDecision(
                decision_type="EQUIP",
                action_position=None,
                action_index=self.my_player.get_free_inventory_index())

        tile_items = self.board.get_tile_at(self.curr_pos).items
        if tile_items is not None or len(tile_items) > 0:
            self.memory.set_value("last_action", "PICKUP")
            return CharacterDecision(decision_type="PICKUP",
                                     action_position=None,
                                     action_index=0)

        weapon = self.my_player.get_weapon()
        enemies = self.api.find_enemies(self.curr_pos)
        if enemies is None or len(enemies) > 0:
            self.memory.set_value("last_action", "MOVE")
            return CharacterDecision(
                decision_type="MOVE",
                action_position=self.my_player.get_spawn_point(),
                action_index=None)

        enemy_pos = enemies[0].get_position()
        if self.curr_pos.manhattan_distance(enemy_pos) <= weapon.get_range():
            self.memory.set_value("last_action", "ATTACK")
            return CharacterDecision(decision_type="ATTACK",
                                     action_position=enemy_pos,
                                     action_index=None)

        self.memory.set_value("last_action", "MOVE")
        decision = CharacterDecision(decision_type="MOVE",
                                     action_position=find_position_to_move(
                                         self.my_player, enemy_pos),
                                     action_index=None)
        return decision
Exemplo n.º 2
0
class Strategy:
    def __init__(self, memory):
        self.memory = memory
        self.logger = logging.getLogger('strategy')
        self.logger.setLevel(logging.DEBUG)

    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        self.api = API(game_state, player_name)
        self.my_player = game_state.get_all_players()[player_name]
        self.board = game_state.get_pvp_board()
        self.curr_pos = self.my_player.get_position()

        self.logger.info("In make_decision")

        last_action, type = self.memory.get_value("last_action", str)
        if last_action is not None and last_action == "PICKUP":
            self.memory.set_value("last_action", "EQUIP")
            return CharacterDecision(
                decision_type="EQUIP",
                action_position=None,
                action_index=self.my_player.get_free_inventory_index())

        tile_items = self.board.get_tile_at(self.curr_pos).items
        if tile_items is not None or len(tile_items) > 0:
            self.memory.set_value("last_action", "PICKUP")
            return CharacterDecision(decision_type="PICKUP",
                                     action_position=None,
                                     action_index=0)

        weapon = self.my_player.get_weapon()
        enemies = self.api.find_enemies(self.curr_pos)
        if enemies is None or len(enemies) > 0:
            self.memory.set_value("last_action", "MOVE")
            return CharacterDecision(
                decision_type="MOVE",
                action_position=self.my_player.get_spawn_point(),
                action_index=None)

        enemy_pos = enemies[0].get_position()
        if self.curr_pos.manhattan_distance(enemy_pos) <= weapon.get_range():
            self.memory.set_value("last_action", "ATTACK")
            return CharacterDecision(decision_type="ATTACK",
                                     action_position=enemy_pos,
                                     action_index=None)

        self.memory.set_value("last_action", "MOVE")
        decision = CharacterDecision(decision_type="MOVE",
                                     action_position=find_position_to_move(
                                         self.my_player, enemy_pos),
                                     action_index=None)
        return decision

    # feel free to write as many helper functions as you need!
    def find_position_to_move(self, player: Position,
                              destination: Position) -> Position:
        path = self.api.find_path(player.get_position(), destination)
        pos = None
        if len(path) < player.get_speed():
            pos = path[-1]
        else:
            pos = path[player.get_speed() - 1]
        return pos
Exemplo n.º 3
0
class Strategy:
    def __init__(self, memory):
        self.memory = memory
        self.logger = logging.getLogger('strategy')
        self.logger.setLevel(logging.DEBUG)
        logging.basicConfig(level=logging.INFO)

    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        self.api = API(game_state, player_name)
        self.my_player = game_state.get_all_players()[player_name]
        self.board = game_state.get_pvp_board()
        self.curr_pos = self.my_player.get_position()

        self.logger.info("In make_decision")

        self.logger.info(
            f"Currently at position: ({self.curr_pos.x},{self.curr_pos.y}) on board '{self.curr_pos.board_id}'"
        )

        last_action, type = self.memory.get_value("last_action", str)
        self.logger.info(f"last_action: '{last_action}'")

        if last_action is not None and last_action == "PICKUP":
            self.logger.info(
                "Last action was picking up, equipping picked up object")
            self.memory.set_value("last_action", "EQUIP")
            return CharacterDecision(
                decision_type="EQUIP",
                action_position=None,
                action_index=0  # self.my_player.get_free_inventory_index()
            )

        tile_items = self.board.get_tile_at(self.curr_pos).get_items()
        if tile_items is not None and len(tile_items) > 0:
            self.logger.info("There are items on my tile, picking up item")
            self.memory.set_value("last_action", "PICKUP")
            return CharacterDecision(decision_type="PICKUP",
                                     action_position=None,
                                     action_index=0)

        weapon = self.my_player.get_weapon()
        enemies = self.api.find_enemies_by_distance(self.curr_pos)
        if enemies is None or len(enemies) == 0:
            self.logger.info(
                "There is no enemies in range, moving to spawn point")
            self.memory.set_value("last_action", "MOVE")
            return CharacterDecision(
                decision_type="MOVE",
                action_position=self.my_player.get_spawn_point(),
                action_index=None)

        enemy_pos = enemies[0].get_position()
        if self.curr_pos.manhattan_distance(enemy_pos) <= weapon.get_range():
            self.logger.info(
                "There is an enemy within weapon range, attacking")
            self.memory.set_value("last_action", "ATTACK")
            return CharacterDecision(decision_type="ATTACK",
                                     action_position=enemy_pos,
                                     action_index=None)

        self.memory.set_value("last_action", "MOVE")
        self.logger.info("Moving towards the nearest enemy")
        decision = CharacterDecision(
            decision_type="MOVE",
            action_position=self.find_position_to_move(self.my_player,
                                                       enemy_pos),
            action_index=None)
        return decision

    # feel free to write as many helper functions as you need!
    def find_position_to_move(self, player: Player,
                              destination: Position) -> Position:
        path = self.api.find_path(player.get_position(), destination)
        # path can be empty if player.get_position() == destination
        if len(path) == 0:
            return player.get_position()
        pos = None
        if len(path) < player.get_speed():
            pos = path[-1]
        else:
            pos = path[player.get_speed() - 1]
        return pos
    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        ######################## Initialize ########################
        self.api = API(game_state, player_name)
        self.my_player = game_state.get_all_players()[player_name]
        self.current_board = game_state.get_board(
            self.my_player.get_position().board_id)
        self.curr_pos = self.my_player.get_position()
        self.monsters_on_board = {
            name: monster
            for name, monster in game_state.get_all_monsters().items()
            if monster.get_position().board_id == self.curr_pos.board_id
        }
        target_monster = None
        self.searching_graph = search.Graph(
            self.current_board,
            self.my_player.get_position().board_id)
        self.logger.info("In make_decision")

        # self.logger.info(helpers.TELL_ME_ME(self.my_player))

        ######################## TAKE TURN HERE ########################
        # self.logger.info(f"All monsters: {game_state.get_all_monsters()}")
        #decision = CharacterDecision(
        #        decision_type="MOVE",
        #        action_position=Position(self.curr_pos.x+2, self.curr_pos.y, "tb_tbdt_dt"),
        #        action_index=None)

        # Combine nearby items with inventory items
        available_items_tiles = helpers.non_api_find_items(
            self.my_player, self.current_board, self.my_player.get_speed(),
            self.logger)
        available_items = self.my_player.inventory + [
            tup[0] for tup in available_items_tiles
        ]
        self.logger.info(f"My Player: {self.my_player.__dict__}")
        self.logger.info(f"Current position: {self.curr_pos.__dict__}")
        self.logger.info(f"Available items around: {available_items_tiles}")
        self.logger.info(f"Our inventory: {self.my_player.inventory}")
        self.logger.info(
            f"Our weapon actual: {self.my_player.get_weapon().__dict__}")
        self.logger.info(
            f"Our weapon: {self.my_player.get_weapon().stats.__dict__}")
        self.logger.info(
            f"Our clothes: {self.my_player.get_clothes().stats.__dict__}")
        self.logger.info(
            f"Our shoes: {self.my_player.get_shoes().stats.__dict__}")
        self.logger.info(f"Our hat: {self.my_player.get_hat().stats.__dict__}")
        self.logger.info(
            f"Our accessory: {self.my_player.get_accessory().stats.__dict__}")

        # best item to equip here!
        item_index = helpers.should_we_equip(self.my_player, available_items,
                                             self.logger)
        self.logger.info(f"Item index: {item_index}")
        # Find non-consumable items
        droppable_items = [
            (index, item)
            for index, item in enumerate(self.my_player.inventory)
            if type(item) in [Weapon, Clothes, Shoes, Hat, Accessory]
        ]
        nearby_monsters = helpers.monsters_in_range(
            self.my_player, list(self.monsters_on_board.values()))

        if item_index != -1 and item_index >= len(self.my_player.inventory):
            target_item, x, y = available_items_tiles[
                item_index - len(self.my_player.inventory)]
            if x != self.curr_pos.x or y != self.curr_pos.y:
                pos = Position.create(x, y, self.curr_pos.board_id)
                decision = decision_maker.head_to(pos)
            else:
                if len(self.my_player.inventory) >= 16:
                    self.logger.warning(
                        "==================INVENTORY FULL??? pOG================"
                    )
                else:
                    decision = decision_maker.pickup(self.my_player,
                                                     target_item,
                                                     self.current_board)
        elif item_index != -1:
            decision = decision_maker.equip_given_item(item_index)
        elif nearby_monsters:
            decision, target_monster = decision_maker.make_our_combat_decision(
                self.api, self.my_player, self.logger, self.monsters_on_board,
                self.searching_graph)
        elif droppable_items:
            index, item = droppable_items[0]
            decision = decision_maker.drop_item(index)
        #elif available_items_tiles:
        #    decision = decision_maker.loot_items(self.api, self.my_player, self.logger, self.current_board, available_items_tiles)
        else:
            decision, target_monster = decision_maker.make_our_combat_decision(
                self.api, self.my_player, self.logger, self.monsters_on_board,
                self.searching_graph)

        #decision = decision_maker.make_our_weapon_decision(self.api, self.my_player, self.logger)
        #decision = decision_maker.head_to_portal_decision(self.api, self.my_player, self.logger)
        # decision_maker.head_to_portal_decision(self.api, self.my_player, self.logger)
        self.logger.info(f"We are doing {decision.__dict__}")
        try:
            self.logger.info(
                f"Position is: {decision.action_position.__dict__}")
        except:
            pass
        self.logger.info(
            f"Player experience: {self.my_player.get_total_experience()}")
        #try:
        # self.logger.info(f"====near target monsters=== {[mon.__dict__ for mon in list(self.monsters_on_board.values()) if abs(self.my_player.get_level() - mon.get_level()) < 4][:9]}")
        #except:
        #    self.logger.info(f"======================log failed")
        ######################## Logging ########################
        self.memory.set_value("last_decision", decision)
        self.memory.set_value("last_target_monster", target_monster)
        self.logger.info(
            f"{target_monster.__dict__ if target_monster else 'None'}")
        self.memory.set_value("last_current_health",
                              self.my_player.current_health)

        ######################## END TURN ########################
        return decision
        """
Exemplo n.º 5
0
    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        self.api = API(game_state, player_name)
        self.my_player = game_state.get_all_players()[player_name]
        self.board = game_state.get_pvp_board()
        self.curr_pos = self.my_player.get_position()

        self.logger.info("In make_decision")

        self.logger.info(
            f"Currently at position: ({self.curr_pos.x},{self.curr_pos.y}) on board '{self.curr_pos.board_id}'"
        )

        last_action, type = self.memory.get_value("last_action", str)
        self.logger.info(f"last_action: '{last_action}'")

        if last_action is not None and last_action == "PICKUP":
            self.logger.info(
                "Last action was picking up, equipping picked up object")
            self.memory.set_value("last_action", "EQUIP")
            return CharacterDecision(
                decision_type="EQUIP",
                action_position=None,
                action_index=0  # self.my_player.get_free_inventory_index()
            )

        tile_items = self.board.get_tile_at(self.curr_pos).get_items()
        if tile_items is not None and len(tile_items) > 0:
            self.logger.info("There are items on my tile, picking up item")
            self.memory.set_value("last_action", "PICKUP")
            return CharacterDecision(decision_type="PICKUP",
                                     action_position=None,
                                     action_index=0)

        weapon = self.my_player.get_weapon()
        enemies = self.api.find_enemies_by_distance(self.curr_pos)
        if enemies is None or len(enemies) == 0:
            self.logger.info(
                "There is no enemies in range, moving to spawn point")
            self.memory.set_value("last_action", "MOVE")
            return CharacterDecision(
                decision_type="MOVE",
                action_position=self.my_player.get_spawn_point(),
                action_index=None)

        enemy_pos = enemies[0].get_position()
        if self.curr_pos.manhattan_distance(enemy_pos) <= weapon.get_range():
            self.logger.info(
                "There is an enemy within weapon range, attacking")
            self.memory.set_value("last_action", "ATTACK")
            return CharacterDecision(decision_type="ATTACK",
                                     action_position=enemy_pos,
                                     action_index=None)

        self.memory.set_value("last_action", "MOVE")
        self.logger.info("Moving towards the nearest enemy")
        decision = CharacterDecision(
            decision_type="MOVE",
            action_position=self.find_position_to_move(self.my_player,
                                                       enemy_pos),
            action_index=None)
        return decision
Exemplo n.º 6
0
    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        self.logger.info(
            "==========================NEW TURN==========================")
        self.api = API(game_state, player_name)
        self.character = game_state.get_character(player_name)
        self.my_player = game_state.get_all_players()[player_name]
        #self.pvpboard = game_state.get_pvp_board()
        self.board = game_state.get_board(self.board_id)
        self.curr_pos = self.my_player.get_position()
        self.monsters = game_state.get_monsters_on_board(self.board_id)

        self.obstacles = self.get_obstacles(game_state)
        self.bad_monster_squares = self.get_monsters(game_state, self.board_id)
        # cycle through items
        items = self.my_player.get_inventory()
        self.logger.info("items: {}".format(items))
        cur_weapon = self.my_player.get_weapon
        self.logger.info('performing inventory check')
        try:
            if self.character.clothes is not None:
                self.print_stats(self.character.clothes)
        except:
            self.logger.info("no clothes to print")
            pass
        try:
            if self.character.hat is not None:
                self.print_stats(self.character.hat)
        except:
            self.logger.info("no hat to print")
            pass
        try:
            if self.character.weapon is not None:
                self.print_stats(self.character.weapon)
        except:
            self.logger.info("no weapon to print")
            pass
        try:
            if self.character.shoes is not None:
                self.print_stats(self.character.shoes)
        except:
            self.logger.info("no shoes to print")
            pass
        try:
            if self.character.accessory is not None:
                self.print_stats(self.character.accessory)
        except:
            self.logger.info("no accessory to print")
            pass
        for i, item in reversed(list(enumerate(items))):
            # self.logger.info('exp change: {}, {}'.format(item.get_flat_experience_change(), item.get_percent_experience_change()))
            # self.logger.info('atk change: {}, {}'.format(item.get_flat_attack_change(), item.get_percent_attack_change()))
            # if item.get_flat_attack_change() > cur_weapon.get_flat_attack_change():
            #     self.logger.info('equiping item')
            #     return CharacterDecision(
            #         decision_type="EQUIP",
            #         action_position=None,
            #         action_index=i
            #     )
            try:
                self.logger.info("grading index {} in the inventory".format(i))
                # self.logger.info(type(item))

                item_type = item.__class__.__name__
                # self.logger.info(item_type)

                if "Consumable" in item_type:
                    #idk do we equip the consumable before we fite the guy
                    #but also if its a health potion do it now
                    self.logger.info(
                        'index {} is a consumable, eating!'.format(i))
                    #actually drop consumables f**k em (no wee eat them right there)
                    return CharacterDecision(decision_type="EQUIP",
                                             action_position=None,
                                             action_index=i)
                    # continue
                self.logger.info(self.print_stats(item))
                stat_mod = item.get_stats()
                new_stats = 0
                try:
                    new_stats += stat_mod.get_flat_speed_change() * 0
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_speed_change() * 0
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_health_change() * .1
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_health_change() * 30
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_experience_change() * 10
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_experience_change() * 200
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_attack_change() * 10
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_attack_change() * 70
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_defense_change() * 2
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_defense_change() * 30
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_regen_per_turn() * 0
                except:
                    new_stats += 0
                self.logger.info("got stats for index {}".format(i))
                # new_stats = stat_mods.get_flat_speed_change() + stat_mods.get_percent_speed_change() + stat_mods.get_flat_health_change() + stat_mods.get_percent_health_change() + stat_mods.get_flat_defense_change() + stat_mods.get_flat_attack_change() + stat_mods.get_percent_attack_change()
                self.logger.info("stat check for index {} is {}".format(
                    i, new_stats))
                for typ in ["Clothes", "Hat", "Shoes", "Weapon", "Accessory"]:

                    if typ in item_type:
                        self.logger.info('index {} is a {}'.format(i, typ))
                        current_stats = self.stats[typ]
                        self.logger.info(
                            'old stats: {} , new stats: {}'.format(
                                current_stats, new_stats))
                        if new_stats > current_stats:
                            self.logger.info("equipping")
                            self.stats[typ] = new_stats
                            return CharacterDecision(decision_type="EQUIP",
                                                     action_position=None,
                                                     action_index=i)
                        else:
                            self.logger.info(
                                "this {} sucks, dropping it".format(typ))
                            return CharacterDecision(decision_type="DROP",
                                                     action_position=None,
                                                     action_index=i)
            except Exception as e:

                self.logger.error(e)
                return CharacterDecision(decision_type="DROP",
                                         action_position=None,
                                         action_index=0)

        # item pick up
        tile_items = self.board.get_tile_at(self.curr_pos).items
        if len(tile_items) > 0:
            self.memory.set_value("last_action", "PICKUP")
            self.logger.info("picking up item: {}".format(tile_items))
            try:
                for i in range(len(tile_items)):
                    self.logger.info("grading new item index {}".format(i))
                    if "Consumable" in tile_items[i].__class__.__name__:
                        return CharacterDecision(decision_type="PICKUP",
                                                 action_position=self.curr_pos,
                                                 action_index=i)
                    stat_mods = tile_items[i].get_stats()
                    stat_sum = stat_mods.get_flat_speed_change(
                    ) * 0 + stat_mods.get_percent_speed_change(
                    ) * 0 + stat_mods.get_flat_health_change(
                    ) * .1 + stat_mods.get_percent_health_change(
                    ) * 30 + stat_mods.get_flat_defense_change(
                    ) * 2 + stat_mods.get_flat_attack_change(
                    ) * 10 + stat_mods.get_percent_attack_change(
                    ) * 70 + stat_mods.get_percent_defense_change(
                    ) * 30 + stat_mods.get_flat_regen_per_turn(
                    ) * 0 + stat_mods.get_flat_experience_change(
                    ) * 10 + stat_mods.get_percent_experience_change() * 200
                    self.logger.info("new item stat: " + str(stat_sum))
                    self.logger.info(
                        "curr stat item: " +
                        str(self.stats[tile_items[i].__class__.__name__]))
                    if stat_sum > self.stats[tile_items[i].__class__.__name__]:
                        self.logger.info(
                            "picking up item at index {}".format(i))
                        return CharacterDecision(decision_type="PICKUP",
                                                 action_position=self.curr_pos,
                                                 action_index=i)
                    else:
                        self.logger.info(
                            "skipping index {}, shitty item".format(i))
            except Exception as e:
                self.logger.error(e)
                self.logger.info("picking up item at index 0")
                return CharacterDecision(decision_type="PICKUP",
                                         action_position=self.curr_pos,
                                         action_index=i)
        for d in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
            target_pos = Position.create(self.curr_pos.x + d[0],
                                         self.curr_pos.y + d[1],
                                         self.curr_pos.get_board_id())
            tile_items = self.board.get_tile_at(target_pos).items
            if len(tile_items) > 0:
                for i in range(len(tile_items)):
                    self.logger.info("grading new item index {}".format(i))
                    if "Consumable" in tile_items[i].__class__.__name__:
                        self.memory.set_value("last_action", "MOVE")
                        self.logger.info("moving to item")
                        return CharacterDecision(decision_type="MOVE",
                                                 action_position=target_pos,
                                                 action_index=0)
                    stat_mods = tile_items[i].get_stats()
                    stat_sum = stat_mods.get_flat_speed_change(
                    ) * 0 + stat_mods.get_percent_speed_change(
                    ) * 0 + stat_mods.get_flat_health_change(
                    ) * .1 + stat_mods.get_percent_health_change(
                    ) * 30 + stat_mods.get_flat_defense_change(
                    ) * 2 + stat_mods.get_flat_attack_change(
                    ) * 10 + stat_mods.get_percent_attack_change(
                    ) * 70 + stat_mods.get_percent_defense_change(
                    ) * 30 + stat_mods.get_flat_regen_per_turn(
                    ) * 0 + stat_mods.get_flat_experience_change(
                    ) * 10 + stat_mods.get_percent_experience_change() * 200
                    self.logger.info("new item stat: " + str(stat_sum))
                    self.logger.info(
                        "curr stat item: " +
                        str(self.stats[tile_items[i].__class__.__name__]))
                    if stat_sum > self.stats[tile_items[i].__class__.__name__]:
                        self.memory.set_value("last_action", "MOVE")
                        self.logger.info("moving to item")
                        return CharacterDecision(decision_type="MOVE",
                                                 action_position=target_pos,
                                                 action_index=0)
                    else:
                        self.logger.info(
                            "skipping index {}, shitty item".format(i))

        ## Choose weakest monster
        weakestMonster = self.findWeakest(self.monsters, self.curr_pos)
        weapon = self.my_player.get_weapon()
        ## Check if weakest monster is in attack range
        if self.curr_pos.manhattan_distance(
                weakestMonster.position) <= weapon.get_range():
            self.logger.info("Attacking monster: " +
                             str(weakestMonster.get_name()) + " with health " +
                             str(weakestMonster.get_current_health()) + "/" +
                             str(weakestMonster.get_max_health()))
            return CharacterDecision(
                decision_type="ATTACK",
                action_position=weakestMonster.get_position(),
                action_index=0)
        ## Move to weakest monster!
        self.logger.info("Chosen weakest monster: " +
                         str(weakestMonster.get_name()) + " || location: (" +
                         str(weakestMonster.get_position().x) + "," +
                         str(weakestMonster.get_position().y) + ")")

        positionToMove = self.zhou_astar_path_to_move(
            self.my_player, weakestMonster.get_position())
        # hard code walk back
        if positionToMove[0] >= 6:
            positionToMove[0] = 4
        positionObjectToMove = self.curr_pos
        newPos = positionObjectToMove.create(positionToMove[0],
                                             positionToMove[1], self.board_id)
        self.logger.info("Location to move now: (" + str(newPos.x) + ", " +
                         str(newPos.y) + ")")
        return CharacterDecision(decision_type="MOVE",
                                 action_position=newPos,
                                 action_index=0)
Exemplo n.º 7
0
class Strategy:
    def __init__(self, memory):
        self.buffoon = 0
        self.memory = memory
        self.logger = logging.getLogger('strategy')
        self.logger.setLevel(logging.DEBUG)
        logging.basicConfig(level=logging.INFO,
                            format='%(asctime)s %(levelname)s %(message)s')
        self.board_id = "buffoon"
        self.stats = collections.defaultdict(int)

    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        self.logger.info(
            "==========================NEW TURN==========================")
        self.api = API(game_state, player_name)
        self.character = game_state.get_character(player_name)
        self.my_player = game_state.get_all_players()[player_name]
        #self.pvpboard = game_state.get_pvp_board()
        self.board = game_state.get_board(self.board_id)
        self.curr_pos = self.my_player.get_position()
        self.monsters = game_state.get_monsters_on_board(self.board_id)

        self.obstacles = self.get_obstacles(game_state)
        self.bad_monster_squares = self.get_monsters(game_state, self.board_id)
        # cycle through items
        items = self.my_player.get_inventory()
        self.logger.info("items: {}".format(items))
        cur_weapon = self.my_player.get_weapon
        self.logger.info('performing inventory check')
        try:
            if self.character.clothes is not None:
                self.print_stats(self.character.clothes)
        except:
            self.logger.info("no clothes to print")
            pass
        try:
            if self.character.hat is not None:
                self.print_stats(self.character.hat)
        except:
            self.logger.info("no hat to print")
            pass
        try:
            if self.character.weapon is not None:
                self.print_stats(self.character.weapon)
        except:
            self.logger.info("no weapon to print")
            pass
        try:
            if self.character.shoes is not None:
                self.print_stats(self.character.shoes)
        except:
            self.logger.info("no shoes to print")
            pass
        try:
            if self.character.accessory is not None:
                self.print_stats(self.character.accessory)
        except:
            self.logger.info("no accessory to print")
            pass
        for i, item in reversed(list(enumerate(items))):
            # self.logger.info('exp change: {}, {}'.format(item.get_flat_experience_change(), item.get_percent_experience_change()))
            # self.logger.info('atk change: {}, {}'.format(item.get_flat_attack_change(), item.get_percent_attack_change()))
            # if item.get_flat_attack_change() > cur_weapon.get_flat_attack_change():
            #     self.logger.info('equiping item')
            #     return CharacterDecision(
            #         decision_type="EQUIP",
            #         action_position=None,
            #         action_index=i
            #     )
            try:
                self.logger.info("grading index {} in the inventory".format(i))
                # self.logger.info(type(item))

                item_type = item.__class__.__name__
                # self.logger.info(item_type)

                if "Consumable" in item_type:
                    #idk do we equip the consumable before we fite the guy
                    #but also if its a health potion do it now
                    self.logger.info(
                        'index {} is a consumable, eating!'.format(i))
                    #actually drop consumables f**k em (no wee eat them right there)
                    return CharacterDecision(decision_type="EQUIP",
                                             action_position=None,
                                             action_index=i)
                    # continue
                self.logger.info(self.print_stats(item))
                stat_mod = item.get_stats()
                new_stats = 0
                try:
                    new_stats += stat_mod.get_flat_speed_change() * 0
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_speed_change() * 0
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_health_change() * .1
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_health_change() * 30
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_experience_change() * 10
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_experience_change() * 200
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_attack_change() * 10
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_attack_change() * 70
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_defense_change() * 2
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_percent_defense_change() * 30
                except:
                    new_stats += 0
                try:
                    new_stats += stat_mod.get_flat_regen_per_turn() * 0
                except:
                    new_stats += 0
                self.logger.info("got stats for index {}".format(i))
                # new_stats = stat_mods.get_flat_speed_change() + stat_mods.get_percent_speed_change() + stat_mods.get_flat_health_change() + stat_mods.get_percent_health_change() + stat_mods.get_flat_defense_change() + stat_mods.get_flat_attack_change() + stat_mods.get_percent_attack_change()
                self.logger.info("stat check for index {} is {}".format(
                    i, new_stats))
                for typ in ["Clothes", "Hat", "Shoes", "Weapon", "Accessory"]:

                    if typ in item_type:
                        self.logger.info('index {} is a {}'.format(i, typ))
                        current_stats = self.stats[typ]
                        self.logger.info(
                            'old stats: {} , new stats: {}'.format(
                                current_stats, new_stats))
                        if new_stats > current_stats:
                            self.logger.info("equipping")
                            self.stats[typ] = new_stats
                            return CharacterDecision(decision_type="EQUIP",
                                                     action_position=None,
                                                     action_index=i)
                        else:
                            self.logger.info(
                                "this {} sucks, dropping it".format(typ))
                            return CharacterDecision(decision_type="DROP",
                                                     action_position=None,
                                                     action_index=i)
            except Exception as e:

                self.logger.error(e)
                return CharacterDecision(decision_type="DROP",
                                         action_position=None,
                                         action_index=0)

        # item pick up
        tile_items = self.board.get_tile_at(self.curr_pos).items
        if len(tile_items) > 0:
            self.memory.set_value("last_action", "PICKUP")
            self.logger.info("picking up item: {}".format(tile_items))
            try:
                for i in range(len(tile_items)):
                    self.logger.info("grading new item index {}".format(i))
                    if "Consumable" in tile_items[i].__class__.__name__:
                        return CharacterDecision(decision_type="PICKUP",
                                                 action_position=self.curr_pos,
                                                 action_index=i)
                    stat_mods = tile_items[i].get_stats()
                    stat_sum = stat_mods.get_flat_speed_change(
                    ) * 0 + stat_mods.get_percent_speed_change(
                    ) * 0 + stat_mods.get_flat_health_change(
                    ) * .1 + stat_mods.get_percent_health_change(
                    ) * 30 + stat_mods.get_flat_defense_change(
                    ) * 2 + stat_mods.get_flat_attack_change(
                    ) * 10 + stat_mods.get_percent_attack_change(
                    ) * 70 + stat_mods.get_percent_defense_change(
                    ) * 30 + stat_mods.get_flat_regen_per_turn(
                    ) * 0 + stat_mods.get_flat_experience_change(
                    ) * 10 + stat_mods.get_percent_experience_change() * 200
                    self.logger.info("new item stat: " + str(stat_sum))
                    self.logger.info(
                        "curr stat item: " +
                        str(self.stats[tile_items[i].__class__.__name__]))
                    if stat_sum > self.stats[tile_items[i].__class__.__name__]:
                        self.logger.info(
                            "picking up item at index {}".format(i))
                        return CharacterDecision(decision_type="PICKUP",
                                                 action_position=self.curr_pos,
                                                 action_index=i)
                    else:
                        self.logger.info(
                            "skipping index {}, shitty item".format(i))
            except Exception as e:
                self.logger.error(e)
                self.logger.info("picking up item at index 0")
                return CharacterDecision(decision_type="PICKUP",
                                         action_position=self.curr_pos,
                                         action_index=i)
        for d in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
            target_pos = Position.create(self.curr_pos.x + d[0],
                                         self.curr_pos.y + d[1],
                                         self.curr_pos.get_board_id())
            tile_items = self.board.get_tile_at(target_pos).items
            if len(tile_items) > 0:
                for i in range(len(tile_items)):
                    self.logger.info("grading new item index {}".format(i))
                    if "Consumable" in tile_items[i].__class__.__name__:
                        self.memory.set_value("last_action", "MOVE")
                        self.logger.info("moving to item")
                        return CharacterDecision(decision_type="MOVE",
                                                 action_position=target_pos,
                                                 action_index=0)
                    stat_mods = tile_items[i].get_stats()
                    stat_sum = stat_mods.get_flat_speed_change(
                    ) * 0 + stat_mods.get_percent_speed_change(
                    ) * 0 + stat_mods.get_flat_health_change(
                    ) * .1 + stat_mods.get_percent_health_change(
                    ) * 30 + stat_mods.get_flat_defense_change(
                    ) * 2 + stat_mods.get_flat_attack_change(
                    ) * 10 + stat_mods.get_percent_attack_change(
                    ) * 70 + stat_mods.get_percent_defense_change(
                    ) * 30 + stat_mods.get_flat_regen_per_turn(
                    ) * 0 + stat_mods.get_flat_experience_change(
                    ) * 10 + stat_mods.get_percent_experience_change() * 200
                    self.logger.info("new item stat: " + str(stat_sum))
                    self.logger.info(
                        "curr stat item: " +
                        str(self.stats[tile_items[i].__class__.__name__]))
                    if stat_sum > self.stats[tile_items[i].__class__.__name__]:
                        self.memory.set_value("last_action", "MOVE")
                        self.logger.info("moving to item")
                        return CharacterDecision(decision_type="MOVE",
                                                 action_position=target_pos,
                                                 action_index=0)
                    else:
                        self.logger.info(
                            "skipping index {}, shitty item".format(i))

        ## Choose weakest monster
        weakestMonster = self.findWeakest(self.monsters, self.curr_pos)
        weapon = self.my_player.get_weapon()
        ## Check if weakest monster is in attack range
        if self.curr_pos.manhattan_distance(
                weakestMonster.position) <= weapon.get_range():
            self.logger.info("Attacking monster: " +
                             str(weakestMonster.get_name()) + " with health " +
                             str(weakestMonster.get_current_health()) + "/" +
                             str(weakestMonster.get_max_health()))
            return CharacterDecision(
                decision_type="ATTACK",
                action_position=weakestMonster.get_position(),
                action_index=0)
        ## Move to weakest monster!
        self.logger.info("Chosen weakest monster: " +
                         str(weakestMonster.get_name()) + " || location: (" +
                         str(weakestMonster.get_position().x) + "," +
                         str(weakestMonster.get_position().y) + ")")

        positionToMove = self.zhou_astar_path_to_move(
            self.my_player, weakestMonster.get_position())
        # hard code walk back
        if positionToMove[0] >= 6:
            positionToMove[0] = 4
        positionObjectToMove = self.curr_pos
        newPos = positionObjectToMove.create(positionToMove[0],
                                             positionToMove[1], self.board_id)
        self.logger.info("Location to move now: (" + str(newPos.x) + ", " +
                         str(newPos.y) + ")")
        return CharacterDecision(decision_type="MOVE",
                                 action_position=newPos,
                                 action_index=0)

    # Find position to move using API
    def find_position_to_move(self, player: Position,
                              destination: Position) -> Position:
        path = self.api.find_path(player.get_position(), destination)
        pos = None
        if len(path) < player.get_speed():
            pos = path[-1]
        else:
            pos = path[player.get_speed() - 1]
        return pos

    # Just moving down
    def move_down_position(self):
        target_pos = self.curr_pos
        target_pos.create(target_pos.x, target_pos.y,
                          target_pos.get_board_id())
        self.logger.info("yes2")
        return target_pos

    # Use astar to find path that doesn't aggro to target destination
    def zhou_astar_path_to_move(self, player: Position, destination: Position):
        frontier = []
        path = []
        pp = player.get_position()
        heapq.heapify(frontier)
        start = Node(0, None, (pp.x, pp.y))
        heapq.heappush(frontier, (0, (pp.x, pp.y), start))
        visited = set()
        visited.add((pp.x, pp.y))
        while frontier:
            curr = heapq.heappop(frontier)[2]
            if curr.coord == (destination.x, destination.y):
                while curr:
                    path.append(curr.coord)
                    self.logger.info(str(curr.coord))
                    curr = curr.prev
                path.reverse()
                break
            neighbors = self.getNeighbors(curr.coord[0], curr.coord[1])
            for n in neighbors:
                if n not in visited:
                    dist = abs(destination.y - n[1]) + abs(destination.x -
                                                           n[0])
                    newNode = Node(curr.cost + 1, curr, n)
                    heapq.heappush(
                        frontier,
                        (newNode.cost + dist, newNode.coord, newNode))
                    visited.add(n)
        return path[1]

    # Find valid neighbors given coordinates
    def getNeighbors(self, getx, gety):
        neighbors = []
        for n in [(0, 1), (0, -1), (-1, 0), (1, 0)]:
            coord = (getx + n[0], gety + n[1])
            # if coord in self.obstacles or coord in self.bad_monster_squares:
            if coord in self.obstacles:
                continue
            else:
                neighbors.append(coord)
        return neighbors

    # Get coords of obstacles
    def get_obstacles(self, game_state):
        grid = self.board.get_grid()
        obstacles = set()
        for x_idx in range(len(grid)):
            for y_idx in range(len(grid[x_idx])):
                curr_position = self.curr_pos
                newPos = curr_position.create(x_idx, y_idx, self.board_id)
                tile = self.board.get_tile_at(newPos)
                if tile.type == "VOID" or tile.type == "IMPASSIBLE":
                    obstacles.add((x_idx, y_idx))
        return obstacles

    # Get coords of monster aggro squares
    def get_monsters(self, game_state, board_id):
        monsters = game_state.get_monsters_on_board(board_id)
        bad_monster_squares = set()
        weakest = self.findWeakest(self.monsters, self.curr_pos)
        weakest_position = weakest.get_position()
        for monster in monsters:
            m_p = monster.get_position()
            # skip over weakest monster
            if m_p.x == weakest_position.x and m_p.y == weakest_position.y:
                continue
            for direction in [(0, 1), (0, -1), (-1, 0), (1, 0)]:
                for step in range(monster.aggro_range):
                    bad_monster_squares.add((direction[0] * step + m_p.x,
                                             direction[1] * step + m_p.y))
        return bad_monster_squares

    # Find weakest monster
    def findWeakest(self, monsters, curr_pos):
        sortedM = sorted(monsters, key=lambda x: x.get_level())
        minLevel = 5  #self.my_player.get_level()
        sameLevel = []
        j = 0
        while sortedM[j].get_level() <= minLevel:
            dist = curr_pos.manhattan_distance(sortedM[j].get_position())
            sameLevel.append((dist, sortedM[j]))
            j = j + 1
        nextMonster = sorted(sameLevel, key=lambda x: x[0])
        i = 0
        while nextMonster[i][1].get_current_health() <= 0:
            i = i + 1
        return nextMonster[i][1]

    def print_stats(self, item):

        stat_mods = item.get_stats()

        self.logger.info("item class: {}".format(item.__class__.__name__))
        try:
            self.logger.info("speed: {}, {}".format(
                stat_mods.get_flat_speed_change(),
                stat_mods.get_percent_speed_change()))
        except Exception as e:
            self.logger.error(e)
            self.logger.info("default speed: {}, {}".format(0, 0))
        try:
            self.logger.info("health: {}, {}".format(
                stat_mods.get_flat_health_change(),
                stat_mods.get_percent_health_change()))
        except Exception as e:
            self.logger.error(e)
            self.logger.info("defaulthealth: {}, {}".format(0, 0))
        try:
            self.logger.info("def: {}, {}".format(
                stat_mods.get_flat_defense_change(),
                stat_mods.get_percent_defense_change()))
        except Exception as e:
            self.logger.error(e)
            self.logger.info("default def: {}, {}".format(0, 0))
        try:
            self.logger.info("exp: {}, {}".format(
                stat_mods.get_flat_experience_change(),
                stat_mods.get_percent_experience_change()))
        except Exception as e:
            self.logger.error(e)
            self.logger.info("default exp: {}, {}".format(0, 0))
        try:
            self.logger.info("atk: {}, {}".format(
                stat_mods.get_flat_attack_change(),
                stat_mods.get_percent_attack_change()))
        except Exception as e:
            self.logger.error(e)
            self.logger.info("default atk: {}, {}".format(0, 0))

    def get_stat_num(self, new_item):
        new_stats = 0
        try:
            new_stats += stat_mod.get_flat_speed_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_percent_speed_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_flat_health_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_percent_health_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_flat_experience_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_percent_experience_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_flat_attack_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_percent_attack_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_flat_defense_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_percent_defense_change()
        except:
            new_stats += 0
        try:
            new_stats += stat_mod.get_flat_regen_per_turn()
        except:
            new_stats += 0
        return new_stats
Exemplo n.º 8
0
    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        self.api = API(game_state, player_name)
        self.game_state = game_state
        self.my_player = game_state.get_all_players()[player_name]
        # self.board = game_state.get_pvp_board()
        self.player_board = game_state.get_board(player_name)
        self.curr_pos = self.my_player.get_position()

        self.logger.info("Version: 4.0")

        # Figure out role
        my_health = self.my_player.get_current_health()
        spawn_point = self.my_player.get_spawn_point()
        if my_health <= 10 or (
                self.equal_pos(self.curr_pos, spawn_point)
                and my_health <= self.my_player.get_max_health() - 10):
            self.role = roles.GAIN_XP
        else:
            self.role = roles.GAIN_XP

        self.logger.info("Player at " + self.get_position_str(self.curr_pos) +
                         " | Health: " + str(my_health) + " | XP: " +
                         str(self.my_player.get_experience()) +
                         " | Total XP: " +
                         str(self.my_player.get_total_experience()))
        self.logger.info("ATK: {}, SPD: {}, DEF: {}".format(
            self.my_player.get_attack(), self.my_player.get_speed(),
            self.my_player.get_defense()))
        last_action, type = self.memory.get_value("last_action", str)
        last_role, type = self.memory.get_value("role", str)
        self.memory.set_value("role", "test_val")
        self.logger.info("last action " + str(last_action))
        self.logger.info("last role " + str(last_role))

        ### store our current stats and inven ###
        weapon: Weapon = self.my_player.get_weapon(
        )  # should always be index 0
        hat: Hat = self.my_player.get_hat()  # always index 1
        shoes: Shoes = self.my_player.get_shoes()
        clothes: Clothes = self.my_player.get_clothes()  # always index 2
        accessory: Accessory = self.my_player.get_accessory()

        self.logger.info("Curr Weapon: {}".format(
            self.get_item_stats_str(weapon)))
        self.logger.info("Curr Clothes: {}".format(
            self.get_item_stats_str(clothes)))
        self.logger.info("Curr Hat: {}".format(self.get_item_stats_str(hat)))
        self.logger.info("Curr Shoes: {}".format(
            self.get_item_stats_str(shoes)))
        self.logger.info("Curr Accessory: {}".format(
            self.get_item_stats_str(accessory)))

        # if inventory has better weapon, equip it
        inven: list[Item] = self.my_player.get_inventory()
        self.logger.info("Have {} inventory items".format(len(inven)))
        best_wep_to_equip: Weapon = None
        best_wep_to_equip_index: int = None
        best_gear_to_equip: Wearable = None
        best_gear_to_equip_index: int = None
        for i, item in enumerate(inven):
            self.logger.info("Inven {} - {} - stats: {}".format(
                i, item, self.get_item_stats_str(item)))
            if isinstance(item, Consumable):
                self.logger.info("Equipping consumable")
                return decisions.equip_item(i)
            elif isinstance(item, Weapon):
                item_val = self.value_of_wearable(item)
                if item_val > self.value_of_wearable(weapon):
                    self.logger.info(
                        "Equipping weapon at index {} with stats: {}".format(
                            i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(weapon):
                    self.logger.info("Dropping weapon at {}".format(i))
                    return decisions.drop_item(i)
            elif isinstance(item, Clothes):
                item_val = self.value_of_wearable(item)
                if (item_val > self.value_of_wearable(clothes)):
                    self.logger.info(
                        "Equipping clothes at index {} with stats: {}".format(
                            i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(clothes):
                    self.logger.info("Dropping clothes at {}".format(i))
                    return decisions.drop_item(i)
            elif isinstance(item, Hat):
                item_val = self.value_of_wearable(item)
                if (item_val > self.value_of_wearable(hat)):
                    self.logger.info(
                        "Equipping hat at index {} with stats: {}".format(
                            i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(hat):
                    self.logger.info("Dropping hat at {}".format(i))
                    return decisions.drop_item(i)
            elif isinstance(item, Shoes):
                item_val = self.value_of_wearable(item)
                if (item_val > self.value_of_wearable(shoes)):
                    self.logger.info(
                        "Equipping shoes at index {} with stats: {}".format(
                            i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(shoes):
                    self.logger.info("Dropping shoes at {}".format(i))
                    return decisions.drop_item(i)
            elif isinstance(item, Accessory):
                item_val = self.value_of_wearable(item)
                if (item_val > self.value_of_wearable(accessory)):
                    self.logger.info(
                        "Equipping accessory at index {} with stats: {}".
                        format(i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(accessory):
                    self.logger.info("Dropping accessory at {}".format(i))
                    return decisions.drop_item(i)

        # BFS search around for stuff
        deltas_128 = bfs_deltas[128]
        best_gears_found: dict[str, Wearable] = {
            'weapon': None,
            'clothes': None,
            'shoes': None,
            'hat': None,
            'accessory': None,
            'con': None,
        }
        best_gears_found_pos: dict[str, Position] = {
            'weapon': None,
            'clothes': None,
            'shoes': None,
            'hat': None,
            'accessory': None,
            'con': None,
        }
        best_gears_found_index: dict[str, Position] = {
            'weapon': None,
            'clothes': None,
            'shoes': None,
            'hat': None,
            'accessory': None,
            'con': None,
        }
        for delta in deltas_128:
            dx = delta[0]
            dy = delta[1]
            check_pos = self.create_pos(self.curr_pos.x + dx,
                                        self.curr_pos.y + dy)
            # self.logger.info("Checking " + self.get_position_str(check_pos))
            if (check_pos.x >= self.player_board.width or check_pos.x < 0
                    or check_pos.y < 0
                    or check_pos.y >= self.player_board.height):
                continue
            # self.logger.info("in map")
            tile: Tile = self.player_board.get_tile_at(check_pos)
            items_on_tile = tile.get_items()
            # search for better items
            for i, item in enumerate(items_on_tile):
                self.logger.info("At " + self.get_position_str(check_pos) +
                                 ", item - " + self.get_item_stats_str(item))
                if isinstance(item, Consumable):
                    con: Consumable = item
                    if (con.effect.turns_left > 4):
                        best_gears_found['con'] = con
                        best_gears_found_index['con'] = i
                        best_gears_found_pos['con'] = check_pos
                elif isinstance(item, Wearable):
                    time_to_delete = item.turns_to_deletion
                    if (self.curr_pos.manhattan_distance(check_pos) >=
                            time_to_delete - 2):
                        continue
                    if isinstance(item, Weapon):
                        self.logger.info("Found weapon")
                        # dont pick up weapons that take too long to retrieve

                        # best weapon to pickup is one that deals more damage, and more damage than all weapons on map that are found
                        item_val = self.value_of_wearable(item)
                        if (item_val > self.value_of_wearable(weapon)):
                            if (best_gears_found['weapon'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['weapon'])):
                                best_gears_found['weapon'] = item
                                best_gears_found_pos['weapon'] = check_pos
                                best_gears_found_index['weapon'] = i
                    elif isinstance(item, Clothes):
                        item_val = self.value_of_wearable(item)
                        if item_val > self.value_of_wearable(clothes):
                            if (best_gears_found['clothes'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['clothes'])):
                                best_gears_found['clothes'] = item
                                best_gears_found_pos['clothes'] = check_pos
                                best_gears_found_index['clothes'] = i
                    elif isinstance(item, Shoes):
                        item_val = self.value_of_wearable(item)
                        if item_val > self.value_of_wearable(shoes):
                            if (best_gears_found['shoes'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['shoes'])):
                                best_gears_found['shoes'] = item
                                best_gears_found_pos['shoes'] = check_pos
                                best_gears_found_index['shoes'] = i
                    elif isinstance(item, Hat):
                        item_val = self.value_of_wearable(item)
                        if item_val > self.value_of_wearable(hat):
                            if (best_gears_found['hat'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['hat'])):
                                best_gears_found['hat'] = item
                                best_gears_found_pos['hat'] = check_pos
                                best_gears_found_index['hat'] = i
                    elif isinstance(item, Accessory):
                        item_val = self.value_of_wearable(item)
                        if item_val > self.value_of_wearable(accessory):
                            if (best_gears_found['accessory'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['accessory'])):
                                best_gears_found['accessory'] = item
                                best_gears_found_pos['accessory'] = check_pos
                                best_gears_found_index['accessory'] = i
        gears_to_pickup = 0
        for i, (k, v) in enumerate(best_gears_found.items()):
            item: Wearable = v
            if item != None:
                gears_to_pickup += 1
                if isinstance(item, Weapon):
                    self.logger.info("best gear: weapon, ATK {}".format(
                        item.attack))
                else:
                    self.logger.info("best gear: {}".format(
                        self.get_item_stats_str(item)))
        # analyze enemies, remove those that would kill us
        sorted_difficulty_enemies: list[Monster] = self.get_all_enemies(
            self.curr_pos)
        enemies: list[Monster] = []
        dangerous_pos_hashes: set[int] = set()
        for enemy in sorted_difficulty_enemies:
            m_health = enemy.get_current_health()
            m_attack = enemy.get_attack()
            m_wep_attack = enemy.get_weapon().get_attack()
            m_defence = enemy.get_defense()
            p_wep_attack = weapon.get_attack()
            p_attack = self.my_player.get_attack()
            p_defence = self.my_player.get_defense()
            p_health = self.my_player.get_current_health()
            m_damage_per_turn = m_wep_attack * ((25 + m_attack) / 100)
            m_actual_damage_per_turn = math.ceil(m_damage_per_turn -
                                                 min(p_defence, 0.8 *
                                                     m_damage_per_turn))

            p_damage_per_turn = p_wep_attack * ((75 + p_attack) / 100)
            p_actual_damage_per_turn = math.ceil(p_damage_per_turn -
                                                 min(m_defence, 0.8 *
                                                     p_damage_per_turn))
            # self.logger.info("Monster at {} deals {} dmg/turn; atk:{}, p_def:".format(self.get_position_str(enemy.get_position()), m_actual_damage_per_turn, m_attack, p_defence))
            # self.logger.info("Player deals {} dmg/turn".format(p_actual_damage_per_turn))
            enemy_turns_to_win = p_health / m_actual_damage_per_turn
            my_turns_to_win = m_health / p_actual_damage_per_turn
            if (my_turns_to_win < enemy_turns_to_win - 1):
                enemies.append(enemy)
            else:
                if (enemy.position.manhattan_distance(self.curr_pos) > 2):
                    dangerous_pos_hashes.add(self.hash_pos(enemy.position))
                    deltas = [(0, 1), (-1, 0), (0, -1), (1, 0)]
                    # TODO: dont hardcode aggro range and the deltas to use
                    if enemy.get_aggro_range() > 1:
                        deltas = bfs_deltas[4]
                    else:
                        deltas = bfs_deltas[1]
                    for delta in deltas:
                        dx = delta[0]
                        dy = delta[1]
                        check_pos: Position = self.create_pos(
                            enemy.position.x + dx, enemy.position.y + dy)
                        # tile: Tile = self.player_board.get_tile_at(check_pos)
                        dangerous_pos_hashes.add(self.hash_pos(check_pos))

        if (len(enemies) == 0):
            self.logger.info("no killable enemies found, resting")
            self.role = roles.REST

        if (gears_to_pickup > 0):
            self.role = roles.PICK_UP_GEAR
            pass

        # if last_action is not None and last_action == "PICKUP":
        #     self.memory.set_value("last_action", "EQUIP")
        #     self.logger.info("Equipping item")
        #     return CharacterDecision(
        #         decision_type="EQUIP",
        #         action_position=0,
        #         action_index=self.my_player.get_free_inventory_index()
        #     )
        self.logger.info("Picking up maybe")
        tile_items = self.player_board.get_tile_at(self.curr_pos).items
        self.logger.info("Items on position: " + str(len(tile_items)))
        # if tile_items is not None or len(tile_items) > 0:
        #     self.logger.info("Picking up item")
        #     self.memory.set_value("last_action", "PICKUP")
        #     return CharacterDecision(
        #         decision_type="PICKUP",
        #         action_position=None,
        #         action_index=0
        #     )
        self.logger.info("====ROLE " + self.role + "====")
        if (self.role == roles.REST):
            sp = self.my_player.get_spawn_point()
            path = self.get_path(self.curr_pos, sp, dangerous_pos_hashes)
            self.logger.info("Moving to " + self.get_position_str(path[0]) +
                             " to get to spawn point to rest at " +
                             self.get_position_str(sp))

            path_index = min(
                max(self.my_player.get_speed(), 1) - 1,
                len(path) - 1)

            decision = decisions.move(path[path_index])
            self.logger.info("Moving!")
            return decision
        elif (self.role == roles.PICK_UP_GEAR):
            target_pos = self.curr_pos
            target_index = 0
            for i, (k, v) in enumerate(best_gears_found.items()):
                item: Wearable = v
                if item != None:
                    target_pos = best_gears_found_pos[k]
                    target_index = best_gears_found_index[k]
                    break
            if (self.equal_pos(target_pos, self.curr_pos)):
                self.logger.info(
                    "Picking up gear under player with index {}".format(
                        target_index))
                decision = decisions.pick_up_item(target_index)
                return decision
            self.logger.info("Moving to pick up gear at " +
                             self.get_position_str(target_pos) + ", index: " +
                             str(target_index))
            path = self.get_path(self.curr_pos, target_pos,
                                 dangerous_pos_hashes)
            path_index = min(
                max(self.my_player.get_speed(), 1) - 1,
                len(path) - 1)

            decision = decisions.move(path[path_index])
            return decision
        elif (self.role == roles.GAIN_XP):
            self.logger.info("Moving to enemy maybe")
            self.logger.info("Found " + str(len(enemies)) + " enemies")
            if enemies is None or len(enemies) > 0:
                enemy_pos = enemies[0].position
                self.logger.info("Closest enemy at " +
                                 self.get_position_str(enemy_pos))
                if weapon.get_range() >= self.curr_pos.manhattan_distance(
                        enemy_pos):
                    self.logger.info("Enemy at " +
                                     self.get_position_str(enemy_pos) +
                                     " within range to attack")
                    return decisions.attack_monster(enemies[0])

                path = self.get_path(self.curr_pos, enemies[0].position,
                                     dangerous_pos_hashes)
                path_index = min(
                    max(self.my_player.get_speed(), 1) - 1,
                    len(path) - 1)
                next_pos = path[path_index]
                self.logger.info("Moving to enemy " +
                                 str(self.get_position_str(next_pos)))
                return decisions.move(next_pos)

            self.logger.info("Moving maybe")
            self.memory.set_value("last_action", "MOVE")

            move_pos = self.pick_open_spot_to_move()
            self.logger.info("MovePos: " + self.get_position_str(move_pos))
            decision = decisions.move(move_pos)
            self.logger.info("Moving!")

            return decision
Exemplo n.º 9
0
class Strategy:

    logger: Logger
    board: Board
    curr_pos: Position
    player_board: Board
    my_player: Player
    api: API
    game_state: GameState

    role: str

    def __init__(self, memory):
        self.memory = memory
        self.logger = logging.getLogger('strategy')
        self.logger.setLevel(logging.DEBUG)
        logging.basicConfig(level=logging.INFO)
        self.role = roles.GAIN_XP

    def make_decision(self, player_name: str,
                      game_state: GameState) -> CharacterDecision:
        """
        Parameters:
        player_name (string): The name of your player
        game_state (GameState): The current game state
        """
        self.api = API(game_state, player_name)
        self.game_state = game_state
        self.my_player = game_state.get_all_players()[player_name]
        # self.board = game_state.get_pvp_board()
        self.player_board = game_state.get_board(player_name)
        self.curr_pos = self.my_player.get_position()

        self.logger.info("Version: 4.0")

        # Figure out role
        my_health = self.my_player.get_current_health()
        spawn_point = self.my_player.get_spawn_point()
        if my_health <= 10 or (
                self.equal_pos(self.curr_pos, spawn_point)
                and my_health <= self.my_player.get_max_health() - 10):
            self.role = roles.GAIN_XP
        else:
            self.role = roles.GAIN_XP

        self.logger.info("Player at " + self.get_position_str(self.curr_pos) +
                         " | Health: " + str(my_health) + " | XP: " +
                         str(self.my_player.get_experience()) +
                         " | Total XP: " +
                         str(self.my_player.get_total_experience()))
        self.logger.info("ATK: {}, SPD: {}, DEF: {}".format(
            self.my_player.get_attack(), self.my_player.get_speed(),
            self.my_player.get_defense()))
        last_action, type = self.memory.get_value("last_action", str)
        last_role, type = self.memory.get_value("role", str)
        self.memory.set_value("role", "test_val")
        self.logger.info("last action " + str(last_action))
        self.logger.info("last role " + str(last_role))

        ### store our current stats and inven ###
        weapon: Weapon = self.my_player.get_weapon(
        )  # should always be index 0
        hat: Hat = self.my_player.get_hat()  # always index 1
        shoes: Shoes = self.my_player.get_shoes()
        clothes: Clothes = self.my_player.get_clothes()  # always index 2
        accessory: Accessory = self.my_player.get_accessory()

        self.logger.info("Curr Weapon: {}".format(
            self.get_item_stats_str(weapon)))
        self.logger.info("Curr Clothes: {}".format(
            self.get_item_stats_str(clothes)))
        self.logger.info("Curr Hat: {}".format(self.get_item_stats_str(hat)))
        self.logger.info("Curr Shoes: {}".format(
            self.get_item_stats_str(shoes)))
        self.logger.info("Curr Accessory: {}".format(
            self.get_item_stats_str(accessory)))

        # if inventory has better weapon, equip it
        inven: list[Item] = self.my_player.get_inventory()
        self.logger.info("Have {} inventory items".format(len(inven)))
        best_wep_to_equip: Weapon = None
        best_wep_to_equip_index: int = None
        best_gear_to_equip: Wearable = None
        best_gear_to_equip_index: int = None
        for i, item in enumerate(inven):
            self.logger.info("Inven {} - {} - stats: {}".format(
                i, item, self.get_item_stats_str(item)))
            if isinstance(item, Consumable):
                self.logger.info("Equipping consumable")
                return decisions.equip_item(i)
            elif isinstance(item, Weapon):
                item_val = self.value_of_wearable(item)
                if item_val > self.value_of_wearable(weapon):
                    self.logger.info(
                        "Equipping weapon at index {} with stats: {}".format(
                            i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(weapon):
                    self.logger.info("Dropping weapon at {}".format(i))
                    return decisions.drop_item(i)
            elif isinstance(item, Clothes):
                item_val = self.value_of_wearable(item)
                if (item_val > self.value_of_wearable(clothes)):
                    self.logger.info(
                        "Equipping clothes at index {} with stats: {}".format(
                            i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(clothes):
                    self.logger.info("Dropping clothes at {}".format(i))
                    return decisions.drop_item(i)
            elif isinstance(item, Hat):
                item_val = self.value_of_wearable(item)
                if (item_val > self.value_of_wearable(hat)):
                    self.logger.info(
                        "Equipping hat at index {} with stats: {}".format(
                            i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(hat):
                    self.logger.info("Dropping hat at {}".format(i))
                    return decisions.drop_item(i)
            elif isinstance(item, Shoes):
                item_val = self.value_of_wearable(item)
                if (item_val > self.value_of_wearable(shoes)):
                    self.logger.info(
                        "Equipping shoes at index {} with stats: {}".format(
                            i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(shoes):
                    self.logger.info("Dropping shoes at {}".format(i))
                    return decisions.drop_item(i)
            elif isinstance(item, Accessory):
                item_val = self.value_of_wearable(item)
                if (item_val > self.value_of_wearable(accessory)):
                    self.logger.info(
                        "Equipping accessory at index {} with stats: {}".
                        format(i, self.get_item_stats_str(item)))
                    return decisions.equip_item(i)
                elif item_val < self.value_of_wearable(accessory):
                    self.logger.info("Dropping accessory at {}".format(i))
                    return decisions.drop_item(i)

        # BFS search around for stuff
        deltas_128 = bfs_deltas[128]
        best_gears_found: dict[str, Wearable] = {
            'weapon': None,
            'clothes': None,
            'shoes': None,
            'hat': None,
            'accessory': None,
            'con': None,
        }
        best_gears_found_pos: dict[str, Position] = {
            'weapon': None,
            'clothes': None,
            'shoes': None,
            'hat': None,
            'accessory': None,
            'con': None,
        }
        best_gears_found_index: dict[str, Position] = {
            'weapon': None,
            'clothes': None,
            'shoes': None,
            'hat': None,
            'accessory': None,
            'con': None,
        }
        for delta in deltas_128:
            dx = delta[0]
            dy = delta[1]
            check_pos = self.create_pos(self.curr_pos.x + dx,
                                        self.curr_pos.y + dy)
            # self.logger.info("Checking " + self.get_position_str(check_pos))
            if (check_pos.x >= self.player_board.width or check_pos.x < 0
                    or check_pos.y < 0
                    or check_pos.y >= self.player_board.height):
                continue
            # self.logger.info("in map")
            tile: Tile = self.player_board.get_tile_at(check_pos)
            items_on_tile = tile.get_items()
            # search for better items
            for i, item in enumerate(items_on_tile):
                self.logger.info("At " + self.get_position_str(check_pos) +
                                 ", item - " + self.get_item_stats_str(item))
                if isinstance(item, Consumable):
                    con: Consumable = item
                    if (con.effect.turns_left > 4):
                        best_gears_found['con'] = con
                        best_gears_found_index['con'] = i
                        best_gears_found_pos['con'] = check_pos
                elif isinstance(item, Wearable):
                    time_to_delete = item.turns_to_deletion
                    if (self.curr_pos.manhattan_distance(check_pos) >=
                            time_to_delete - 2):
                        continue
                    if isinstance(item, Weapon):
                        self.logger.info("Found weapon")
                        # dont pick up weapons that take too long to retrieve

                        # best weapon to pickup is one that deals more damage, and more damage than all weapons on map that are found
                        item_val = self.value_of_wearable(item)
                        if (item_val > self.value_of_wearable(weapon)):
                            if (best_gears_found['weapon'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['weapon'])):
                                best_gears_found['weapon'] = item
                                best_gears_found_pos['weapon'] = check_pos
                                best_gears_found_index['weapon'] = i
                    elif isinstance(item, Clothes):
                        item_val = self.value_of_wearable(item)
                        if item_val > self.value_of_wearable(clothes):
                            if (best_gears_found['clothes'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['clothes'])):
                                best_gears_found['clothes'] = item
                                best_gears_found_pos['clothes'] = check_pos
                                best_gears_found_index['clothes'] = i
                    elif isinstance(item, Shoes):
                        item_val = self.value_of_wearable(item)
                        if item_val > self.value_of_wearable(shoes):
                            if (best_gears_found['shoes'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['shoes'])):
                                best_gears_found['shoes'] = item
                                best_gears_found_pos['shoes'] = check_pos
                                best_gears_found_index['shoes'] = i
                    elif isinstance(item, Hat):
                        item_val = self.value_of_wearable(item)
                        if item_val > self.value_of_wearable(hat):
                            if (best_gears_found['hat'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['hat'])):
                                best_gears_found['hat'] = item
                                best_gears_found_pos['hat'] = check_pos
                                best_gears_found_index['hat'] = i
                    elif isinstance(item, Accessory):
                        item_val = self.value_of_wearable(item)
                        if item_val > self.value_of_wearable(accessory):
                            if (best_gears_found['accessory'] == None
                                    or item_val > self.value_of_wearable(
                                        best_gears_found['accessory'])):
                                best_gears_found['accessory'] = item
                                best_gears_found_pos['accessory'] = check_pos
                                best_gears_found_index['accessory'] = i
        gears_to_pickup = 0
        for i, (k, v) in enumerate(best_gears_found.items()):
            item: Wearable = v
            if item != None:
                gears_to_pickup += 1
                if isinstance(item, Weapon):
                    self.logger.info("best gear: weapon, ATK {}".format(
                        item.attack))
                else:
                    self.logger.info("best gear: {}".format(
                        self.get_item_stats_str(item)))
        # analyze enemies, remove those that would kill us
        sorted_difficulty_enemies: list[Monster] = self.get_all_enemies(
            self.curr_pos)
        enemies: list[Monster] = []
        dangerous_pos_hashes: set[int] = set()
        for enemy in sorted_difficulty_enemies:
            m_health = enemy.get_current_health()
            m_attack = enemy.get_attack()
            m_wep_attack = enemy.get_weapon().get_attack()
            m_defence = enemy.get_defense()
            p_wep_attack = weapon.get_attack()
            p_attack = self.my_player.get_attack()
            p_defence = self.my_player.get_defense()
            p_health = self.my_player.get_current_health()
            m_damage_per_turn = m_wep_attack * ((25 + m_attack) / 100)
            m_actual_damage_per_turn = math.ceil(m_damage_per_turn -
                                                 min(p_defence, 0.8 *
                                                     m_damage_per_turn))

            p_damage_per_turn = p_wep_attack * ((75 + p_attack) / 100)
            p_actual_damage_per_turn = math.ceil(p_damage_per_turn -
                                                 min(m_defence, 0.8 *
                                                     p_damage_per_turn))
            # self.logger.info("Monster at {} deals {} dmg/turn; atk:{}, p_def:".format(self.get_position_str(enemy.get_position()), m_actual_damage_per_turn, m_attack, p_defence))
            # self.logger.info("Player deals {} dmg/turn".format(p_actual_damage_per_turn))
            enemy_turns_to_win = p_health / m_actual_damage_per_turn
            my_turns_to_win = m_health / p_actual_damage_per_turn
            if (my_turns_to_win < enemy_turns_to_win - 1):
                enemies.append(enemy)
            else:
                if (enemy.position.manhattan_distance(self.curr_pos) > 2):
                    dangerous_pos_hashes.add(self.hash_pos(enemy.position))
                    deltas = [(0, 1), (-1, 0), (0, -1), (1, 0)]
                    # TODO: dont hardcode aggro range and the deltas to use
                    if enemy.get_aggro_range() > 1:
                        deltas = bfs_deltas[4]
                    else:
                        deltas = bfs_deltas[1]
                    for delta in deltas:
                        dx = delta[0]
                        dy = delta[1]
                        check_pos: Position = self.create_pos(
                            enemy.position.x + dx, enemy.position.y + dy)
                        # tile: Tile = self.player_board.get_tile_at(check_pos)
                        dangerous_pos_hashes.add(self.hash_pos(check_pos))

        if (len(enemies) == 0):
            self.logger.info("no killable enemies found, resting")
            self.role = roles.REST

        if (gears_to_pickup > 0):
            self.role = roles.PICK_UP_GEAR
            pass

        # if last_action is not None and last_action == "PICKUP":
        #     self.memory.set_value("last_action", "EQUIP")
        #     self.logger.info("Equipping item")
        #     return CharacterDecision(
        #         decision_type="EQUIP",
        #         action_position=0,
        #         action_index=self.my_player.get_free_inventory_index()
        #     )
        self.logger.info("Picking up maybe")
        tile_items = self.player_board.get_tile_at(self.curr_pos).items
        self.logger.info("Items on position: " + str(len(tile_items)))
        # if tile_items is not None or len(tile_items) > 0:
        #     self.logger.info("Picking up item")
        #     self.memory.set_value("last_action", "PICKUP")
        #     return CharacterDecision(
        #         decision_type="PICKUP",
        #         action_position=None,
        #         action_index=0
        #     )
        self.logger.info("====ROLE " + self.role + "====")
        if (self.role == roles.REST):
            sp = self.my_player.get_spawn_point()
            path = self.get_path(self.curr_pos, sp, dangerous_pos_hashes)
            self.logger.info("Moving to " + self.get_position_str(path[0]) +
                             " to get to spawn point to rest at " +
                             self.get_position_str(sp))

            path_index = min(
                max(self.my_player.get_speed(), 1) - 1,
                len(path) - 1)

            decision = decisions.move(path[path_index])
            self.logger.info("Moving!")
            return decision
        elif (self.role == roles.PICK_UP_GEAR):
            target_pos = self.curr_pos
            target_index = 0
            for i, (k, v) in enumerate(best_gears_found.items()):
                item: Wearable = v
                if item != None:
                    target_pos = best_gears_found_pos[k]
                    target_index = best_gears_found_index[k]
                    break
            if (self.equal_pos(target_pos, self.curr_pos)):
                self.logger.info(
                    "Picking up gear under player with index {}".format(
                        target_index))
                decision = decisions.pick_up_item(target_index)
                return decision
            self.logger.info("Moving to pick up gear at " +
                             self.get_position_str(target_pos) + ", index: " +
                             str(target_index))
            path = self.get_path(self.curr_pos, target_pos,
                                 dangerous_pos_hashes)
            path_index = min(
                max(self.my_player.get_speed(), 1) - 1,
                len(path) - 1)

            decision = decisions.move(path[path_index])
            return decision
        elif (self.role == roles.GAIN_XP):
            self.logger.info("Moving to enemy maybe")
            self.logger.info("Found " + str(len(enemies)) + " enemies")
            if enemies is None or len(enemies) > 0:
                enemy_pos = enemies[0].position
                self.logger.info("Closest enemy at " +
                                 self.get_position_str(enemy_pos))
                if weapon.get_range() >= self.curr_pos.manhattan_distance(
                        enemy_pos):
                    self.logger.info("Enemy at " +
                                     self.get_position_str(enemy_pos) +
                                     " within range to attack")
                    return decisions.attack_monster(enemies[0])

                path = self.get_path(self.curr_pos, enemies[0].position,
                                     dangerous_pos_hashes)
                path_index = min(
                    max(self.my_player.get_speed(), 1) - 1,
                    len(path) - 1)
                next_pos = path[path_index]
                self.logger.info("Moving to enemy " +
                                 str(self.get_position_str(next_pos)))
                return decisions.move(next_pos)

            self.logger.info("Moving maybe")
            self.memory.set_value("last_action", "MOVE")

            move_pos = self.pick_open_spot_to_move()
            self.logger.info("MovePos: " + self.get_position_str(move_pos))
            decision = decisions.move(move_pos)
            self.logger.info("Moving!")

            return decision

    def get_all_enemies(self, pos: Position):
        enemies = []

        # player: Player = self.my_player
        # for k in game_state.monster_names:
        #     monster: Monster = game_state.monster_names[k]
        #     enemies.append(monster)
        all_monsters: list[Monster] = self.game_state.get_monsters_on_board(
            self.curr_pos.get_board_id())
        for monster in all_monsters:
            if monster.get_current_health() > 0:
                enemies.append(monster)
        # sort by level then distance
        enemies = sorted(
            enemies,
            key=lambda m:
            (m.position.manhattan_distance(pos), 9999999 - m.get_level()))
        return enemies
        # for delta in deltas:
        #     check_pos = pos.create(pos.x + delta[0], pos.y + delta[1], pos.get_board_id())
        #     tile: Tile = self.player_board.get_tile_at(check_pos)

    # feel free to write as many helper functions as you need!
    def find_position_to_move(self, player: Player,
                              destination: Position) -> Position:
        path = self.api.find_path(player.get_position(), destination)
        # path can be empty if player.get_position() == destination
        if len(path) == 0:
            return player.get_position()
        pos = None
        if len(path) < player.get_speed():
            pos = path[-1]
        else:
            pos = path[player.get_speed() - 1]
        return pos

    def get_position_str(self, pos: Position):
        return str(pos.get_board_id()) + " | " + str(pos.get_x()) + ", " + str(
            pos.get_y())

    # greedy for now
    def get_path(self, start: Position, end: Position, avoid_hashes):
        deltas = [(0, 1), (-1, 0), (0, -1), (1, 0)]
        # deltas = bfs_deltas[1024]
        lowest = start.manhattan_distance(end)
        path = [start]

        queue = deque([start])
        visited = {
        }  # map node to its next node, so start maps to next_node, 2nd to last node maps to end
        start_hash = self.hash_pos(start)
        visited[start_hash] = None
        while (len(queue) > 0):
            pos = queue.popleft()
            # reach end, return next node
            curr_hash = self.hash_pos(pos)
            if self.equal_pos(pos, end):
                # backtrack
                bt_hash = self.hash_pos(pos)
                bt_path = deque([])
                while bt_hash in visited:
                    prev_node_hash = visited[bt_hash]
                    next_node = self.read_pos_hash(bt_hash)
                    bt_path.appendleft(next_node)
                    if prev_node_hash == self.hash_pos(start):
                        self.logger.info(
                            "Bfs path start {} next {} end {}".format(
                                self.get_position_str(start),
                                self.get_position_str(next_node),
                                self.get_position_str(end)))
                        return bt_path
                    bt_hash = prev_node_hash
                break
            for delta in deltas:
                dx = delta[0]
                dy = delta[1]
                check_pos: Position = self.create_pos(pos.x + dx, pos.y + dy)
                tile: Tile = self.player_board.get_tile_at(check_pos)
                if tile.type == "BLANK":
                    check_hash = self.hash_pos(check_pos)
                    if check_hash not in visited and check_hash not in avoid_hashes:
                        queue.append(check_pos)
                        visited[check_hash] = curr_hash
                else:
                    pass
                # dist = check_pos.manhattan_distance(end)
                # if dist < lowest:
                #     path[0] = check_pos
                #     lowest = dist
        self.logger.info("exhausted bfs...")
        return path

    def read_pos_hash(self, hash: int):
        return self.create_pos(hash // 10000, hash % 10000)

    def hash_pos(self, pos: Position):
        return pos.x * 10000 + pos.y

    def pick_open_spot_to_move(self) -> Position:
        deltas = [(0, 1), (-1, 0), (0, -1), (1, 0)]
        movable_deltas = []
        for delta in deltas:
            dx = delta[0]
            dy = delta[1]
            pos: Position = self.create_pos(self.curr_pos.x + dx,
                                            self.curr_pos.y + dy)
            tile: Tile = self.player_board.get_tile_at(pos)
            if tile.type == "BLANK":
                movable_deltas.append(delta)

        if (len(movable_deltas) == 0):
            return self.curr_pos
        f_delta = movable_deltas[random.randint(0, len(movable_deltas) - 1)]

        return self.curr_pos.create(self.curr_pos.x + f_delta[0],
                                    self.curr_pos.y + f_delta[1],
                                    self.curr_pos.get_board_id())

    def create_pos(self, x, y):
        pos: Position = self.curr_pos.create(x, y,
                                             self.curr_pos.get_board_id())
        return pos

    def equal_pos(self, pos1: Position, pos2: Position):
        return pos1.x == pos2.x and pos1.y == pos2.y and pos1.get_board_id(
        ) == pos2.get_board_id()

    def get_item_stats_str(self, item: Item):
        if (isinstance(item, Weapon)):
            return "Weapon: ATK {atk}, RANGE: {range}, SPLASH: {splash}, {sts}".format(
                atk=item.get_attack(),
                range=item.get_range(),
                splash=item.get_splash_radius(),
                sts=self.stats_str(item))
        elif (isinstance(item, Clothes)):

            # item.stats.percent_speed_change = kwargs['percent_speed_change']
            # item.stats.percent_health_change = kwargs['percent_health_change']
            # item.stats.percent_experience_change = kwargs['percent_experience_change']

            # item.stats.percent_attack_change = kwargs['percent_attack_change']
            # item.stats.percent_defense_change = kwargs['percent_defense_change']
            return "Clothes: Stats {}".format(self.stats_str(item))
        elif (isinstance(item, Accessory)):
            return "Accessory: Stats {}".format(self.stats_str(item))
        elif (isinstance(item, Hat)):
            return "Hat: Stats {}".format(self.stats_str(item))
        elif (isinstance(item, Shoes)):
            return "Shoes: Stats {}".format(self.stats_str(item))
        elif (isinstance(item, Consumable)):
            return "Con: Stats {}".format(
                self.stats_str_consumable(item.effect))
        return "smth else"

    def stats_str(self, item: Item):
        return 'fgpt {}, spd {}, hp {}, xp {}, atk {}, def {}, %atk {}, %def {}, %hp {}'.format(
            item.stats.flat_regen_per_turn, item.stats.flat_speed_change,
            item.stats.flat_health_change, item.stats.flat_experience_change,
            item.stats.flat_attack_change, item.stats.flat_defense_change,
            item.stats.percent_attack_change,
            item.stats.percent_defense_change,
            item.stats.percent_health_change)

    def stats_str_consumable(self, stats: TempStatusModifier):
        return 'fgpt {}, spd {}, hp {}, xp {}, atk {}, def {}, %atk {}, %def {}, %hp {}, turns left {}'.format(
            stats.flat_regen_per_turn, stats.flat_speed_change,
            stats.flat_health_change, stats.flat_experience_change,
            stats.flat_attack_change, stats.flat_defense_change,
            stats.percent_attack_change, stats.percent_defense_change,
            stats.percent_health_change, stats.turns_left)

    def value_of_wearable(self, item: Item):
        if isinstance(item, Wearable):
            replacedItem = None
            if (isinstance(item, Weapon)):
                return item.get_attack() * item.stats.percent_attack_change
            elif (isinstance(item, Clothes)):
                replacedItem = self.my_player.get_clothes()
            elif (isinstance(item, Accessory)):
                replacedItem = self.my_player.get_accessory()
            elif (isinstance(item, Hat)):
                replacedItem = self.my_player.get_hat()
            elif (isinstance(item, Shoes)):
                replacedItem = self.my_player.get_shoes()
            stats = item.stats
            # p_health_change = stats.percent_health_change * (self.my_player.get_max_health() - replacedItem.stats.flat_health_change)
            return stats.flat_defense_change * 30 + stats.flat_experience_change * 10 + stats.flat_health_change + stats.flat_regen_per_turn * 45 + stats.percent_attack_change * 5 + stats.flat_speed_change * 60
        return 0