예제 #1
0
 def get_required_nondepleted_items(self, required_items=None):
     if required_items is None:
         required_items = {}
     if Rule.NON_DEPLETED_ITEMS in self.rule_dict:
         required_items = dictutils.add_merge_dicts(
             required_items, self.rule_dict[Rule.NON_DEPLETED_ITEMS])
     return required_items
예제 #2
0
    def _set_active_rule_states(self):
        # Init
        self.end_items = {}
        self.extra_items = {}
        self.visited_rules = []
        self.distractor_rules = []

        # Randomly init environment, depending on options
        # Randomly choose rules and distractor rules
        if self.options['spawn']['mode'] == 'random_rules':
            # Randomly choose the rules to follow
            rule_idx = np.random.choice(len(self.world_rules),
                                        self.options['spawn']['num_rules'],
                                        replace=False)
            for idx in rule_idx:
                self.visited_rules.append(
                    self.world_rules[idx].rule_dict['name'])
            # TODO - Distractor rules is not implimented right now. Not actually sure what to do with it
            # TODO - Also, it only spawns itesms for world rules, not the proposed rules

            # Choose a random number of other items and add them to spawn
            for _ in range(self.options['spawn']['num_distractor_items']):
                item = random.choice(list(self.item_dict.keys()))
                self.extra_items = dictutils.add_merge_dicts(
                    self.extra_items, {item: 1})
        elif self.options['spawn']['mode'] == 'fixed_spawns':
            # Choose randomly from a list of possible spawns
            # All items to be spawned are specifically enumerated

            if self.load_items != None:
                self.extra_items = self.load_items["extra_items"]
                self.goal = self.load_items["goal"]
                self.recipe = self.load_items["recipe"]
            else:
                self.extra_items = random.choice(self.spawns)

                self.goal = 'temp'
                self.recipe = 'temp'

                for key, value in self.extra_items.items():
                    if key == 'goal':
                        self.goal = value
                    elif key == 'recipe':
                        self.recipe = value
        else:
            raise Exception('Have not implemented spawn mode %s' %
                            self.options['spawn']['num_distractor_items'])
예제 #3
0
    def calculate_items(self, end_items, visited_rules, distractor_rules,
                        extra_items, inventory_chance):
        # Return what should be in inventory, what should be on ground
        inventory_items = {}
        ground_items = {}

        # What containers to spawn
        containers = set()

        # What fonts to spawn (and how much resource it should have)
        fonts = {}

        # Keep track of if we've used rules
        used_rules = set()

        # Seperate required items into depleted and not
        # Need to add depleted but only max non-depleted
        req_depleted = {}
        req_nondepleted = {}

        # First calculate what we need for end items
        nondepleted_created = {}
        for item in end_items:
            item_count = end_items[item]
            node = self.nodes[item]
            new_req_depleted, new_req_nondepleted, new_req_fonts, new_containers, new_used_rules, nondepleted_created, depth_left = self.get_required_recursive(
                node, nondepleted_created, self.max_craft_depth)

            # Update required items
            for _ in item_count:
                req_depleted = dictutils.add_merge_dicts(
                    new_req_depleted, req_depleted)
                req_nondepleted = dictutils.max_merge_dicts(
                    new_req_nondepleted, req_nondepleted)
                fonts = dictutils.add_merge_dicts(new_req_fonts, fonts)
                containers |= new_containers
                used_rules |= new_used_rules

            # Assert req_nondepleted is a subset of nondepleted_created
            assert (dictutils.is_subset_of(req_nondepleted,
                                           nondepleted_created))

        # Now calculate for rules, if we haven't visited them yet
        for rule_name in visited_rules:
            # If we didn't incindentally visit the rule already
            if rule_name not in used_rules:
                node = self.nodes[rule_name]
                new_req_depleted, new_req_nondepleted, new_req_fonts, new_containers, new_used_rules, nondepleted_created, depth_left = self.get_required_recursive(
                    node, nondepleted_created, self.max_craft_depth)

                # Update required items
                req_depleted = dictutils.add_merge_dicts(
                    new_req_depleted, req_depleted)
                req_nondepleted = dictutils.max_merge_dicts(
                    new_req_nondepleted, req_nondepleted)
                fonts = dictutils.add_merge_dicts(new_req_fonts, fonts)
                containers |= new_containers
                used_rules |= new_used_rules

                # Assert req_nondepleted is a subset of nondepleted_created
                assert (dictutils.is_subset_of(req_nondepleted,
                                               nondepleted_created))

        # Now get items for our distractor rules
        # Check whether we actually share a common resource
        # Then give rest of ingredients
        for rule_name in distractor_rules:
            # Distractor must not have been visited already
            if rule_name in used_rules:
                continue

            # Distractor must consume a necessary resource
            node = self.nodes[rule_name]
            rule = node.data['rule']
            distractor_consumed = list(
                rule.get_required_depleted_items().keys())
            common_resources = set(distractor_consumed) & (set(
                req_depleted.keys()) | set(req_nondepleted.keys()))
            if len(common_resources) == 0:
                continue

            # Get items needed
            new_req_depleted, new_req_nondepleted, new_req_fonts, new_containers, new_used_rules, nondepleted_created, depth_left = self.get_required_recursive(
                node, nondepleted_created, self.max_craft_depth)

            # Remove common resource items, and only add extra depleted items that aren't common resources
            proc_depleted = {}
            for key in new_req_depleted:
                if key not in common_resources:
                    proc_depleted[key] = new_req_depleted[key]

            # Update required
            req_depleted = dictutils.add_merge_dicts(proc_depleted,
                                                     req_depleted)
            req_nondepleted = dictutils.max_merge_dicts(
                new_req_nondepleted, req_nondepleted)
            fonts = dictutils.add_merge_dicts(new_req_fonts, fonts)
            containers |= new_containers
            used_rules |= new_used_rules

            # Assert req_nondepleted is a subset of nondepleted_created
            assert (dictutils.is_subset_of(req_nondepleted,
                                           nondepleted_created))

        # Add distractor items
        for item in extra_items:

            if item == 'goal' or item == 'recipe' or item == 'count':
                continue

            item_type = self.item_dict[item]
            item_count = extra_items[item]

            # Add to nondepleted required item list
            if self.item_dict[item] == 'CraftingItem':
                if item not in req_nondepleted:
                    req_nondepleted[item] = item_count
                else:
                    req_nondepleted[item] += item_count

            # Add crafting containers not already in there
            elif self.item_dict[item] == 'CraftingContainer':
                containers.add(item)

            # Add font values
            elif self.item_dict[item] == 'ResourceFont':
                if item not in fonts:
                    fonts[item] = item_count
                else:
                    fonts[item] += item_count

        # Put items in either ground items or inventory items
        all_items = dictutils.add_merge_dicts(req_depleted, req_nondepleted)
        for item in all_items:
            for i in range(all_items[item]):
                if random.random() < inventory_chance:
                    if item in inventory_items:
                        inventory_items[item] += 1
                    else:
                        inventory_items[item] = 1
                else:
                    if item in ground_items:
                        ground_items[item] += 1
                    else:
                        ground_items[item] = 1

        # Return
        return inventory_items, ground_items, containers, fonts
예제 #4
0
    def get_required_recursive(self, node, nondepleted_created, depth):
        # Init return values
        req_depleted = {}
        req_nondepleted = {}
        fonts = {}
        containers = set()
        used_rules = set()

        # If node is rule, follow rules for updating
        if node.data['type'] == 'rule':
            # If we're at a rule, decrement depth
            depth -= 1

            # Add rule to used rules
            used_rules.add(node.key)
            rule = node.data['rule']

            # Get what is needed depleted/nondepleted for rule
            rule_req_depleted = rule.get_required_depleted_items()
            rule_req_nondepleted = rule.get_required_nondepleted_items()

            # Add the required location (font or container)
            if 'required_location' in rule.rule_dict:
                location = rule.rule_dict['required_location']
                if self.item_dict[location] == 'CraftingContainer':
                    containers.add(location)
                elif self.item_dict[location] == 'ResourceFont':
                    # Have font count 1. The traceback expects only 1 output item, so it will do the multiplication above in the stack
                    fonts[location] = 1

            # If there are no required items, we're done (should only be fonts)
            if len(node.incoming) == 0:
                pdb.set_tace()
                # Should be font I think
                assert (self.item_dict[location] == 'ResourceFont')
                return req_depleted, req_nondepleted, fonts, containers, used_rules, nondepleted_created, depth
            # If there are required items left
            else:
                # If we're at the last depth, generate everything we need at this point
                if depth == 0:
                    req_depleted = rule_req_depleted
                    req_nondepleted = rule_req_nondepleted
                    nondepleted_created = dictutils.max_merge_dicts(
                        nondepleted_created, req_nondepleted)
                    return req_depleted, req_nondepleted, fonts, containers, used_rules, nondepleted_created, depth
                # Make the recursive call to get all the required items
                else:
                    # Recursive calls
                    depth_left = depth
                    for item_node in node.incoming:
                        # Figure out how many we need, and if its depleted or not
                        item_name = item_node.key

                        # Get how many of the item we need that are depleted
                        depl_count = 0
                        nondepl_count = 0
                        assert (item_name in rule_req_depleted
                                or item in rule_req_nondepleted)
                        if item_name in rule_req_depleted:
                            depl_count = rule_req_depleted[item_name]

                        # Add nondepleted items
                        if item_name in rule_req_nondepleted:
                            # Don't create any more of the item if we've already created enough
                            if item_name in nondepleted_created:
                                nondepl_count = max(
                                    0, rule_req_nondepleted[item_name] -
                                    nondepleted_created[item_name])
                            else:
                                nondepl_count = rule_req_nondepleted[item_name]

                            # Update nondepleted_created if this rule means we have to create more
                            if nondepl_count > 0:
                                nondepleted_created[
                                    item_name] = rule_req_nondepleted[
                                        item_name]

                        # If item_count is 0 because we had a nondepleted item already, skip item
                        item_count = depl_count + nondepl_count
                        if item_count == 0:
                            continue

                        # If it's a terminal item, we need to add the item itself
                        if len(item_node['in'] == 0):
                            if item_name in rule_req_depleted:
                                item_req_depleted = {
                                    item_name: rule_req_depleted[item_name]
                                }
                                req_depleted = dictutils.add_merge_dicts(
                                    item_req_depleted, req_depleted)
                            if item_name in rule_req_nondepleted:
                                item_req_nondepleted = {
                                    item_name: rule_req_nondepleted[item_name]
                                }
                                req_nondepleted = dictutils.max_merge_dicts(
                                    item_req_nondepleted, req_nondepleted)
                        # Otherwise, we update based on what the item uses
                        else:
                            item_req_depleted, item_req_nondepleted, item_fonts, item_containers, item_used_rules, item_depth_left = get_required_recursive(
                                item_node, depth)

                            # Update items and lists based on return from item
                            for ind in range(item_count):
                                req_depleted = dictutils.add_merge_dicts(
                                    item_req_depleted, req_depleted)
                                fonts = dictutils.add_merge_dicts(
                                    item_fonts, fonts)
                            req_nondepleted = dictutils.max_merge_dicts(
                                item_req_nondepleted, req_nondepleted)
                            containers = item_containers | containers
                            used_rules = item_used_rules | used_rules
                            depth_left = min(depth_left, item_depth_left)

                    # Return
                    return req_depleted, req_nondepleted, fonts, containers, used_rules, nondepleted_created, depth_left
        # Follow recursive rules for items
        else:
            assert (node.data['type'] == 'item')

            # This should be handled in rule node
            if len(node.incoming) == 0:
                assert (False)

            # If multiple rules, choose one to follow randomly
            # TODO - is this the right behavior actually? Can't think of a better way to do this
            rule_node = random.choice(node.incoming)

            # Follow the rule node
            return get_required_recursive(rule_node, depth)