Esempio n. 1
0
    def monster_present(self):
        """Check if a monster is present within 6 squares of us."""

        self.combat.next_square_to_monster = None

        if 'laden with moisture' in self.nh.top_line or self.nh.in_fog:
            verboseprint("In fog, so a monster is surrounding us.")
            return True

        # if combat env. has not found any monsters during its parsing, return False
        if self.combat.cur_monster_pos is None:
            return False
        try:
            # get a trajectory to the monster.
            trajectory_to_monster = self.nh.pathfind_to(
                self.combat.cur_monster_pos,
                explored_set=self.nh.explored,
                override_target_traversability=True)
        except:
            # not possible to move there, so don't try.
            verboseprint("Monster present but can't find a path to it")
            return False
        self.combat.next_square_to_monster = trajectory_to_monster[0]

        # If combat env. has detected a monster during parsing, it is an unsafe monster (i.e., hostile) and within 6 spaces of us, then we enter into combat mode.
        return True if (
            len(self.combat.cur_monsters) > 0
            and self.combat.cur_monster_pos is not None
            and self.combat.cur_monster_pos not in self.safe_monster_positions
        ) and len(trajectory_to_monster) <= 6 else False
Esempio n. 2
0
def assert_setup(starting_items, inventory, cur_monster, top_line, monster_positions, stats, start_ac):
    verboseprint("Asserting setup...")
    
    # check that we have all 5 items in our inventory...
    for item in starting_items:
        found = False
        if item[0].isdigit():
            item = item[item.index(" ")+1:-1] # get rid of ammo quantity
        for _, _, stripped_inven_item, matched_item, _ in inventory:
            #print("Trying to match the starting item,", item, "to the thing in our inventory, stripped name:", stripped_inven_item)
            itemname = matched_item.full_name.replace("poisoned ", "")
            if itemname == item.full_name: # item_match(item.full_name, stripped_inven_item):
                found = True
                break
        if not found:
            print("\nCould not find", item, "in inventory:")
            for inven_name, _, _, it, _ in inventory:
                print(inven_name, it.full_name.replace("poisoned ", ""))
            #append("Could not find " + item.full_name + " in inventory: " + str(inventory) + " (starting items: " + str(starting_items) + ")", self.savedir+"errors")
            #raise Exception
    
    # check that there is at least one monster...
    if len(monster_positions) < 1 and stats['hallu'] != 'Hallu':
        #append("Could not find monster " + cur_monster, self.savedir+"errors")
        raise Exception("No monster (was looking for " + cur_monster + ")!")
    
    # check that the monster is the correct char
    mon_names = [cur_monster.replace("_", "-").lower(), cur_monster.replace("_", " ").lower()]
    if all(mname not in top_line.lower() for mname in mon_names) and stats['hallu'] != 'Hallu':
        print("Monster (" + cur_monster + ") not in top line (" + top_line.lower() + ")!")
        #append("Monster (" + cur_monster + ") not in top line (" + top_line.lower() + ")", self.savedir+"errors")
        #return False
    
    verboseprint("Setup looks good.")
    return True
Esempio n. 3
0
    def get_status(self, msg):
        """Check for a terminal state (death), or terminal state for one of the combat or exploration environments (monster died, or level finished)."""

        status, goal_reached = self.combat.get_status(
            msg)  # check for player death
        if status is Terminals.PLAYER_DIED:
            self.terminate = True  # start a new level when reset() is next called.

        if status is Terminals.OK:
            # we didn't die.

            if self.in_combat:
                self.process_msg(msg)
                self.policy.exploration_policy.observe_action()
                self.state = self.get_state()

                # check for monster death (not handled by combat env since NH won't send monster death signal when doing level env. since it's harder to detect due to multiple monsters possibly being present)
                if len(self.nh.monster_positions) < len(
                        self.nh.prev_monster_positions) and not self.nh.in_fog:
                    status, goal_reached = Terminals.MONSTER_DIED, Goals.SUCCESS

            elif self.last_action == CMD.DIR.DOWN:
                # We reached a new level. Reset the exploration environment.
                verboseprint("Entering new level.")
                status, goal_reached = Terminals.SUCCESS, Goals.SUCCESS
                self.new_level = True

        return status, goal_reached
Esempio n. 4
0
 def get_status(self, msg):
     """Check if we are done exploring or not."""
     status = Terminals.OK
     goal_reached = None
     if self.policy.done_exploring():
         verboseprint("Success (finished exploring)")
         status = Terminals.SUCCESS
         goal_reached = Goals.SUCCESS
     return status, goal_reached
Esempio n. 5
0
def save_nh_conf(proc_id, secret_rooms=False, character="Bar", race="Human", clvl=1, st=0, dx=0, mtype=None, create_mons=False, ac=999, inven=[], dlvl=1, lyc=None, stateffs=1, adj_mlvl=True, create_items=True, seed=-1):
    if sys.platform == "win32":
        sysconf_fname = nethack_dir + DIR_CHAR + "defaults.nh"
    else:
        sysconf_fname = nethack_dir + DIR_CHAR + "sysconf" + str(proc_id)
    verboseprint("Writing to sysconf file:", sysconf_fname)
    with open(sysconf_fname, 'w') as sysconf:
        sysconf.write("OPTIONS=!autopickup, !bones, pushweapon, pettype:none, time, disclose:-i -a -v -g -c -o, ")
        if secret_rooms:
            sysconf.write("secret_rooms, ")
        if not adj_mlvl:
            sysconf.write("!")
        sysconf.write("adjust_mlvl, ")
        if not create_items:
            sysconf.write("!")
        sysconf.write("create_items, ")
        if mtype is not None:
            sysconf.write("combat_setup, create_mons, ")
        elif create_mons:
            sysconf.write("!combat_setup, create_mons, ")
        else:
            sysconf.write("!combat_setup, !create_mons, ")
        sysconf.write("character:"+character+", race:"+race+", gender:male, name:Merlin, align:chaotic, ")
        sysconf.write("reqlevel:"+str(clvl)+", reqstr:"+str(st)+", reqdex:"+str(dx)+", reqdlvl:"+str(dlvl))
        if ac < 999:
            sysconf.write(", reqac:"+str(ac))
        if lyc is not None:
            sysconf.write(", reqlyc:"+str(int(lyc)))
        if stateffs > 1:
            sysconf.write(", stateffs:"+str(stateffs))
        if mtype is not None:
            sysconf.write(", mtypeid:"+str(mtype))
        if seed > -1:
            sysconf.write(", seed:"+str(seed))
        sysconf.write("\nWIZKIT=wizkit" + str(proc_id) + ".txt\n")
    wizkit_fname = nethack_dir + DIR_CHAR + "wizkit" + str(proc_id) + ".txt"
    with open(wizkit_fname, 'w') as wizkit:
        for item in inven:
            #enc = 3 if 'wand' in item else 0 # charges for wand
            #buc = 'uncursed' if 'holy water' not in item else 'blessed'
            #
            #if item[0].isdigit(): # if quantity specified
            #    quantity = item.split(" ")[0]
            #    item = item[item.index(" "):]
            #else:
            #    quantity = 1
            #
            #wizkit.write(str(quantity) + " " + buc + " +" + str(enc) + " " + item + "\n")
            wizkit.write(item + "\n")
Esempio n. 6
0
 def process_msg(self, msg, slim_charset=False):
     """Processes the map screen outputted by NetHack."""
     
     super().process_msg(msg, parse_monsters=False)
     
     self.total_num_rooms = self.nh.stats['rooms']
     self.total_sdoors_scorrs = self.nh.stats['sdoor']
     
     if self.nh.in_room() and self.nh.explored_current_room():
         self.mark_room_explored()
     
     self.nh.update_pathfinding_grid()
     
     verboseprint("Rooms:", str(len(self.explored_rooms)) + "/" + str(self.total_num_rooms), self.explored_rooms)
     assert len(self.explored_rooms) <= self.total_num_rooms
Esempio n. 7
0
    def set_config(self,
                   grid_search=False,
                   top_models=False,
                   num_episodes_per_combo=200,
                   proc_id=0,
                   num_procs=1,
                   param_combos=None,
                   param_abbrvs=None):
        """Set config.
        
        Args:
            grid_search: whether to change parameters every certain number of episodes.
            top_models: whether to load from a text file and use the specified param combos inside. (Must have grid_search=True)
            num_episodes_per_combo: if grid search, number of episodes per each combination of alg. parameters.
            proc_id: if grid search, process ID of this environment, to be matched with the argument passed to the daemon launching script.
            num_procs: if grid search, number of processes that will be running in parallel
            param_combos: list of lists of parameter combinations
            param_abbrvs: abbreviated parameter names (for directory name)
        """
        self.grid_search = grid_search
        assert not top_models or grid_search

        if grid_search:
            self.num_episodes_per_combo = num_episodes_per_combo
            self.param_abbrvs = param_abbrvs

            if os.path.isfile('combos_to_try.txt'):
                param_combos = read_list('combos_to_try')

            elif top_models and os.path.isfile(self.base_dir +
                                               'top_models.txt'):
                param_combos = [
                    get_params_for_dir(dirname)
                    for dirname in read_list(self.env.basedir + 'top_models')
                ]

            else:
                num_combos_per_proc = max(len(param_combos) // num_procs, 1)
                param_combos_per_proc = [
                    param_combos[i:i + num_combos_per_proc]
                    for i in range(0, len(param_combos), num_combos_per_proc)
                ]
                self.combos_to_set = param_combos_per_proc[
                    proc_id if num_procs > 1 else 0]
                verboseprint(param_combos_per_proc)
        else:
            self.set_params(self.get_default_params())
Esempio n. 8
0
    def set_trajectory_to_possible_exit(self):
        """Try to find the down stairs ('>') on the NetHack map. If it is not directly visible, look under monsters and dungeon features. Update exploration policy to trajectory towards the position."""
        assert not self.moving_to_exit

        self.env.nh.update_pathfinding_grid()

        exit_pos = self.env.nh.find_char_on_base_map('>')
        if exit_pos is None:
            # can't reach exit from here, must explore more.
            verboseprint("Can't find path to exit")

            possibilities = []

            # add monster tiles to frontier
            for i, row in enumerate(self.env.nh.map):
                for j, col in enumerate(row):
                    if col in MONS_CHARS:
                        possibilities.append((i, j))

            # add masking dungeon features
            for i, row in enumerate(self.env.nh.map):
                for j, col in enumerate(row):
                    if col in ['}', '_']:
                        possibilities.append((i, j))

            self.exploration_policy.frontier_list.extend(possibilities)

            frontier_paths_to_player = [
                (self.env.nh.pathfind_to(frontier,
                                         override_target_traversability=True),
                 frontier)
                for frontier in self.exploration_policy.frontier_list
            ]
            frontier_dists_to_player = [
                (len(path), frontier)
                for path, frontier in frontier_paths_to_player if len(path) > 0
            ]

            exit_pos = min(frontier_dists_to_player)[1]

        self.exploration_policy.target = exit_pos
        self.exploration_policy.current_trajectory = deque(
            self.env.nh.pathfind_to(self.exploration_policy.target,
                                    explored_set=self.env.nh.explored,
                                    override_target_traversability=True))
Esempio n. 9
0
def get_inventory(socket):
    try:
        send_msg(socket, CMD.INVENTORY)
        raw = rcv_msg(socket)
    except zmq.error.Again:
        raise Exception("Error occurred communicating with NetHack to get inventory.")
    
    items = raw.split("--")
    inventory = []
    matched_names = []
    for item_str in items[1:-1]:
        if len(item_str.split(",")) != 2:
            print("Couldn't interpret:", item_str)
        item_name, item_char = item_str.split(",")
        item_name = item_name.replace("-2", "-1").replace("+2", "+1").replace("-3", "-1").replace("+3", "+1").replace("+4", "+1").replace("-4", "-1").replace("thoroughly ", "").replace("very ", "")
        
        qty, stripped_name = get_stripped_itemname(item_name)
        if stripped_name in IGNORED_ITEMS:
            continue
        
        matched_item = None
        for i, weap in enumerate(ALL_ITEMS):
            if item_match(weap.full_name, stripped_name):
                #if weap.full_name in matched_names:
                #    print(weap.full_name, "already present in inven! :", inventory)
                
                matched_names.append(weap.full_name)
                matched_item = weap
                break
    
        if matched_item is None:
            verboseprint("\nMatched item was none! Item_str:", item_str, "and stripped:", stripped_name)
            continue
        assert item_char is not None
        
        if ':' in item_name:
            ind = item_name.index(':')
            qty = item_name[ind+1:ind+2]
                
        inventory.append((item_name, item_char, stripped_name, matched_item, 1 if len(qty) == 0 else int(qty)))
    return inventory
Esempio n. 10
0
    def reset(self):
        """Prepare the environment for a new episode. (Call reset() on combat and exploration envs.)"""

        self.combat.reset()

        if not self.terminate:
            if self.new_level:
                self.new_level = False
                self.in_combat = True  # assume monster is present at start(?)
                self.safe_monster_positions = []
                self.nh.reset()
                self.expl.reset()
                self.policy.reset()
                verboseprint("New level, resetting map data structures...")
                return self.combat.state, [self.abilities.index("wait")]
            else:
                # killed monster, but keep current NH process running.
                verboseprint("Continuing current ep on monster death...")
                return self.combat.state, self.combat.get_valid_action_indices(
                )

        self.safe_monster_positions = []
        self.in_combat = True  # assume monster is present at start(?)

        self.terminate = False
        self.new_level = False

        self.num_combat_actions = 0
        self.num_combat_encounters_this_game = 0
        self.num_expl_actions = 0

        self.expl.reset()
        s, v = super().reset()
        self.policy.reset()

        # We updated the socket variable during our base::reset(), so reflect changes in the envs.
        self.expl.socket = self.socket
        self.combat.socket = self.socket
        self.abilities = self.combat.abilities

        return s, v
Esempio n. 11
0
    def switch_encounter(self):
        """Alter alg. parameters if using grid search."""
        if not self.grid_search:
            return
        elif self.combos_to_set is not None:  # initial setup
            self.set_combos(self.combos_to_set)
            self.combos_to_set = None
        elif self.cur_combo > -1 and self.num_games_this_combo < self.num_episodes_per_combo:
            verboseprint("Not finished with current param combo yet")
            return
        while True:
            if self.num_games_this_combo > 0:
                self.env.save_records(
                )  # save current records to the current directroy

            self.cur_combo += 1
            if self.cur_combo >= len(self.param_combos):
                verboseprint("Past max combo, going back to combo 0")
                self.cur_combo = 0

            print("Combo", self.cur_combo, "/", len(self.param_combos))

            cur_params = self.param_combos[self.cur_combo]
            self.set_params(cur_params)
            self.env.savedir = self.env.basedir + get_dir_for_params(
                cur_params, self.param_abbrvs)
            self.env.load_records(
            )  # load any existing records from the new directory
            self.num_games_this_combo = 0
            self.env.total_num_games += len(self.env.records['expl'])
            break

        verboseprint("Cur params:", self.env.savedir)
Esempio n. 12
0
    def select_action(self, **kwargs):
        """Return an action to be taken, using either the exploration or combat policy depending on whether we are in combat or not."""
        if self.env.expl.total_actions_this_episode == 0:
            return CMD.WAIT

        if self.env.in_combat:
            if not self.prev_in_combat:
                verboseprint(
                    "\n***Switching to combat from exploration. Monster:",
                    self.env.combat.cur_monsters)
                self.env.num_combat_encounters_this_game += 1
                self.combat_policy.env.start_episode()
                self.combat_policy.env.save_encounter_info()

            verboseprint("In combat this step")
            if self.combat_policy.env.total_actions_this_episode > self.NUM_COMBAT_ACTIONS_PEACEFUL and not self.env.nh.in_fog:
                # Spent too long on this monster, so decide it is peaceful.
                verboseprint(
                    "Combat actions exceeded max - marking this monster as safe"
                )
                self.env.safe_monster_positions.append(
                    self.combat_policy.env.cur_monster_pos)

            # query the combat policy
            return self.combat_policy.select_action(**kwargs)

        else:
            if self.prev_in_combat:
                # Update the current trajectory, since we have just come out of combat.
                verboseprint("\n***Switching to exploration from combat")
                if self.exploration_policy.target is not None and not self.env.nh.in_fog:
                    verboseprint("Updating stale trajectory")
                    self.exploration_policy.current_trajectory = deque(
                        self.env.nh.pathfind_to(
                            self.exploration_policy.target,
                            explored_set=self.env.nh.explored,
                            override_target_traversability=True))

            verboseprint("Exploring this step")

            if self.env.nh.stats['blind'] == 'Blind' or self.env.nh.stats[
                    'conf'] == 'Conf' or self.env.nh.stats[
                        'stun'] == 'Stun' or self.env.nh.stats[
                            'hallu'] == 'Hallu':
                # Use WAIT action if we have a bad status effect.
                verboseprint("Waiting for bad status effect to go away.")
                return self.combat_policy.select_action(
                    valid_action_indices=[self.env.abilities.index("wait")])

            if self.exploration_policy.done_exploring(
            ) and not self.env.nh.on_stairs():
                # Done exploring, so we have to go stairs to go to next level.

                verboseprint("Time to go to exit!")
                if not self.moving_to_exit:
                    self.set_trajectory_to_possible_exit()
                    self.moving_to_exit = True

            return self.exploration_policy.select_action(**kwargs)