def _reset(self):
        super(PushBlock, self)._reset()

        self.sw_loc = choice(creationutils.empty_locations(self))
        self.sw = mi.Switch(location=self.sw_loc)
        self._add_item(self.sw)

        x, y = choice(creationutils.empty_locations(self))
        self.pushable = mi.Pushable(location=(x, y))
        self._add_item(self.pushable)

        visited, p = creationutils.dijkstra(self, (x, y),
                                            creationutils.pushblock_movefunc)
        if self.sw_loc not in visited:
            raise MazeException("No path to sw")
        self.waypoints = pbwps(p, self.pushable.location, self.sw.location)

        x, y = choice(creationutils.empty_locations(self,
                                                    bad_blocks=[mi.Block]))
        self.agent = PushBlockAgent(location=(x, y))
        self._add_agent(self.agent, "PushBlockAgent")
        visited, _ = creationutils.dijkstra(self, (x, y),
                                            creationutils.agent_movefunc)
        if self.waypoints[0] not in visited:
            raise MazeException("No path to pushblock")
    def _reset(self):
        super(Exclusion, self)._reset()
        if self.visit_min < 1:
            raise MazeException("visit_min is not >= 1")
        if self.visit_max == -1:
            self.visit_max = self.n_goals
        self.visit = list(range(self.n_goals))
        shuffle(self.visit)
        to_visit = randint(self.visit_min, self.visit_max)
        self.exclude = self.visit[to_visit:]
        self.visit = self.visit[:to_visit]
        self.visit = dict((x, False) for x in self.visit)

        self.goals = []
        for i in range(self.n_goals):
            x, y = choice(creationutils.empty_locations(self))
            self.goals.append(mi.Goal(location=(x, y), id=i))
            self._add_item(self.goals[i])

        x, y = choice(creationutils.empty_locations(self,
                                                    bad_blocks=[mi.Block]))
        self.agent = TogglingAgent(location=(x, y))
        self._add_agent(self.agent, "ExclusionAgent")

        visited, _ = creationutils.dijkstra(self, (x, y),
                                            creationutils.agent_movefunc)

        if not all(goal.location in visited for goal in self.goals):
            raise MazeException("No path to goal")
    def _reset(self):
        super(ConditionedGoals, self)._reset()

        x, y = choice(creationutils.empty_locations(self))
        self.sw = mi.Switch(
            location=(x, y),
            nstates=self.n_colors,
            start_state=choice(range(self.n_colors)),
        )
        self._add_item(self.sw)

        self.goals = []
        for i in range(self.n_goals):
            x, y = choice(creationutils.empty_locations(self))
            self.goals.append(mi.Goal(location=(x, y), id=i))
            self._add_item(self.goals[i])
        self.conditions = [randint(0, self.n_goals - 1) for _ in self.goals]

        x, y = choice(creationutils.empty_locations(self,
                                                    bad_blocks=[mi.Block]))
        self.agent = TogglingAgent(location=(x, y))
        self._add_agent(self.agent, "ConditionedGoalsAgent")

        visited, _ = creationutils.dijkstra(self, (x, y),
                                            creationutils.agent_movefunc)

        if (self.sw.location not in visited or
            not any(self.goals[i].location in visited
                    for i in set(self.conditions))):
            raise MazeException("No path to goal")
    def _reset(self):
        hole, dim = add_vertical_wall(self)

        # Add the pushblock
        self.pushable = mi.Pushable(location=hole)
        self._add_item(self.pushable)

        # Add additional blocks and waters
        super(BlockedDoor, self)._reset()

        # Add the goal, and agent
        loc = choice(creationutils.empty_locations(
            self, bad_blocks=[mi.Block, mi.Door]))
        self.goal = mi.Goal(location=loc)
        self._add_item(self.goal)

        loc = choice(creationutils.empty_locations(
            self, bad_blocks=[mi.Block, mi.Door]))
        self.agent = PushBlockAgent(location=loc)
        self._add_agent(self.agent, "BlockedDoorAgent")

        self._remove_item(self.pushable.id)
        visited, _ = creationutils.dijkstra(self, loc,
                                            creationutils.agent_movefunc)
        if self.goal.location not in visited:
            raise MazeException("No path to goal")
        self._add_item(self.pushable)
    def _reset(self):
        hole, dim = add_vertical_wall(self)

        # Add the door
        self.door = mi.Door(location=hole,
                            state=choice(range(1, self.switch_states)))
        self._add_item(self.door)

        # Add additional blocks and waters
        super(LightKey, self)._reset()

        # Add the goal, agent, and switch
        loc = choice(creationutils.empty_locations(
            self, bad_blocks=[mi.Block, mi.Door]))
        self.goal = mi.Goal(location=loc)
        self._add_item(self.goal)
        side = choice([-1, 1])

        def mask_func(x, y):
            return side * ((x, y)[1 - dim] - hole[1 - dim]) > 0

        loc = choice(creationutils.empty_locations(
            self, bad_blocks=[mi.Block, mi.Door, mi.Goal], mask=mask_func))
        self.sw = mi.Switch(location=loc, nstates=self.switch_states)
        self._add_item(self.sw)

        loc = choice(creationutils.empty_locations(
            self, bad_blocks=[mi.Block, mi.Door], mask=mask_func))
        self.agent = SwitchesAgent(location=loc)
        self._add_agent(self.agent, "LightKeyAgent")

        visited, _ = creationutils.dijkstra(self, loc,
                                            creationutils.agent_movefunc)
        if self.goal.location not in visited or self.sw.location not in visited:
            raise MazeException("No path to goal")
    def _reset(self):
        super(PushBlockCardinal, self)._reset()

        x, y = choice(creationutils.empty_locations(self))
        self.pushable = mi.Pushable(location=(x, y))
        self._add_item(self.pushable)
        self.direction = choice([self.FEATURE.UP,
                                 self.FEATURE.DOWN,
                                 self.FEATURE.LEFT,
                                 self.FEATURE.RIGHT,
                                 ])
        if self.direction == self.FEATURE.UP:
            self.goals = set((i, self.height) for i in range(self.width))
        elif self.direction == self.FEATURE.DOWN:
            self.goals = set((i, 0) for i in range(self.width))
        elif self.direction == self.FEATURE.LEFT:
            self.goals = set((0, i) for i in range(self.height))
        elif self.direction == self.FEATURE.RIGHT:
            self.goals = set((self.width, i) for i in range(self.height))

        visited, p = creationutils.dijkstra(self, (x, y),
                                            creationutils.pushblock_movefunc)
        if not any(x in visited for x in self.goals):
            raise MazeException("No path to goal")
        closest = min(self.goals, key=lambda loc: visited[loc])
        self.waypoints = pbwps(p, self.pushable.location, closest)

        x, y = choice(creationutils.empty_locations(self,
                                                    bad_blocks=[mi.Block]))
        self.agent = PushBlockAgent(location=(x, y))
        self._add_agent(self.agent, "PushBlockCardinalAgent")
    def _reset(self):
        super(SingleGoal, self)._reset()

        loc = choice(creationutils.empty_locations(self))
        self.goal = mi.Goal(location=loc)
        self._add_item(self.goal)

        loc = choice(creationutils.empty_locations(self, bad_blocks=[mi.Block]))
        self.agent = MovingAgent(location=loc)
        self._add_agent(self.agent, "SingleGoalAgent")

        visited, _ = creationutils.dijkstra(self, loc,
                                            creationutils.agent_movefunc)
        if self.goal.location not in visited:
            raise MazeException("No path to goal")
    def _reset(self):
        super(MultiGoals, self)._reset()

        self.goals = []
        for i in range(self.n_goals):
            x, y = choice(creationutils.empty_locations(self))
            self.goals.append(mi.Goal(location=(x, y), id=i))
            self._add_item(self.goals[i])
        shuffle(self.goals)
        self.v = 0

        x, y = choice(creationutils.empty_locations(self,
                                                    bad_blocks=[mi.Block]))
        self.agent = MovingAgent(location=(x, y))
        self._add_agent(self.agent, "MultiGoalsAgent")

        visited, _ = creationutils.dijkstra(self, (x, y),
                                            creationutils.agent_movefunc)
        if not all(goal.location in visited for goal in self.goals):
            raise MazeException("No path to goal")
    def _reset(self):
        super(GotoHidden, self)._reset()

        self.goals = []
        for i in range(self.n_goals):
            x, y = choice(creationutils.empty_locations(self))
            self.goals.append(mi.Goal(location=(x, y), id=i, visible=False))
            self._add_item(self.goals[i])

        self.goal = choice(self.goals)

        x, y = choice(creationutils.empty_locations(self,
                                                    bad_blocks=[mi.Block]))
        self.agent = MovingAgent(location=(x, y))
        self._add_agent(self.agent, "GotoHiddenAgent")

        visited, _ = creationutils.dijkstra(self, (x, y),
                                            creationutils.agent_movefunc)
        if self.goal.location not in visited:
            raise MazeException("No path to goal")
    def _reset(self):
        super(Switches, self)._reset()

        loc = choice(creationutils.empty_locations(self, bad_blocks=[mi.Block]))
        self.agent = SwitchesAgent(location=loc)
        self._add_agent(self.agent, "SwitchesAgent")

        visited, _ = creationutils.dijkstra(self, loc,
                                            creationutils.agent_movefunc)

        self._switches = []
        for _ in range(self.n_switches):
            loc = choice(creationutils.empty_locations(self))
            self._switches.append(mi.Switch(
                location=loc,
                nstates=self.switch_states,
                start_state=choice(range(self.switch_states)),
            ))
            self._add_item(self._switches[-1])
            if loc not in visited:
                raise MazeException("No path to goal")
    def _calculate_approximate_reward(self):
        '''Greedy solution that visits each switch in turn'''
        best = 1e100
        sw_colors = [sw.state for sw in self._switches]
        print(sw_colors)
        for i, sw in enumerate(self._switches):
            tmp = [(sw.state - c) % self.switch_states for c in sw_colors]
            # Heuristic for perferring not needing to flip switches
            best = min(best, sum(x if x > 0 else -2 for x in tmp))

        to_visit = [sw.location for sw in self._switches]
        loc = self.agent.location
        r = 0
        for i, sw in enumerate(self._switches):
            visited, path = creationutils.dijkstra(
                self, loc, creationutils.agent_movefunc, True)
            ind, loc = min(enumerate(to_visit), key=lambda x: visited[x[1]])
            r -= visited[loc]  # Reward is negative of path
            to_visit.remove(loc)
        return super(Switches,
                     self)._calculate_approximate_reward() + \
                r - best * self.turn_penalty
def _est(game, s, e):
    '''shorthand to estimate reward for an agent to move from s to e'''
    visited, path = creationutils.dijkstra(
        game, s, creationutils.agent_movefunc, True)
    return -visited[e]  # Returns distance, which is negation of reward
    def _reset(self):
        #super(CraftingGame, self)._reset()

        # Implimented in inheriting classes
        # Sets spawn environment with
        # self.end_items - items we want to make possible to create
        # self.visited_rules - rules we want our agent to be able to use
        # self.distractor_rules - rules we want to make possible to add that use necessary resources for other rules
        # self.extra_items - extra items to spawn in environment that aren't related
        self._set_active_rule_states()

        self.inventory = {}
        self.switch_states = 2

        hole, dim = self.add_vertical_wall()

        # Add the door
        self.door = mi.Door(location=hole,
                            state=choice(range(1, self.switch_states)))
        self._add_item(self.door)

        # Add additional blocks and waters
        #super(CraftingGame, self)._reset()

        # Add agent and switch
        side = choice([-1, 1])

        def mask_func(x, y):
            return side * ((x, y)[1 - dim] - hole[1 - dim]) > 0

        self.isSwitch = random.choice([True, False])

        if self.isSwitch:
            loc = choice(creationutils.empty_locations(
                self, bad_blocks=[mi.Block, mi.Door], mask=mask_func))
            self.sw = mi.Switch(location=loc, nstates=self.switch_states)
            self._add_item(self.sw)
        else:
            loc = choice(creationutils.empty_locations(
                self, bad_blocks=[mi.Block, mi.Door], mask=mask_func))
            self.sw = mi.Key("key",location=loc)
            self._add_item(self.sw)

        loc = choice(creationutils.empty_locations(
            self, bad_blocks=[mi.Block, mi.Door], mask=mask_func))
        self.agent = agents.CraftingAgent(location=loc)
        self.agent.inventory = self.inventory
        self._add_agent(self.agent, "CraftingAgent")

        visited, _ = creationutils.dijkstra(self, loc,
                                            creationutils.agent_movefunc)
        if self.sw.location not in visited:
            raise MazeException("No path to goal")

        # Figure our what we want to spawn
        inventory_items, ground_items, containers, fonts = self.crafting_graph.calculate_items(self.end_items, self.visited_rules, self.distractor_rules, self.extra_items, self.inventory_chance)

        # Create inventory objects
        for item in inventory_items:
            assert(self.item_dict[item] == mi.CraftingItem.__name__)
            item_count = inventory_items[item]
            self._create_item_in_inventory(self, item, item_count)

        # Add containers
        for container_name in containers:
            assert(self.item_dict[container_name] == mi.CraftingContainer.__name__)
            loc = choice(creationutils.empty_locations(self, bad_blocks=self.bad_blocks))
            self._add_item(mi.CraftingContainer(container_name, location=loc))

        # Add fonts
        for font_name in fonts:
            assert(self.item_dict[font_name] == mi.ResourceFont.__name__)
            font_item_count = fonts[font_name]

            # Randomly split into fonts with no more than 10 resources per font
            while font_item_count > 0:
                # Make font of random size
                cur_sz = randint(1, 10)
                if cur_sz > font_item_count:
                    cur_sz = font_item_count

                # Add font to environment
                loc = choice(creationutils.empty_locations(self, bad_blocks=self.bad_blocks))
                self._add_item(mi.ResourceFont(font_name, resource_count=cur_sz, location=loc))
                font_item_count -= cur_sz

        # Add ground items
        for item in ground_items:
            assert(self.item_dict[item] == mi.CraftingItem.__name__)
            item_count = ground_items[item]
            for ind in range(item_count):
                loc = choice(creationutils.empty_locations(self, bad_blocks=self.bad_blocks))
                self._add_item(mi.CraftingItem(item, location=loc))