예제 #1
0
class Game:
    """Representation of a Game run."""

    def __init__(self, level=1, timeout=TIMEOUT, player=None):
        logger.info("Game(level=%s)", level)
        self.puzzles = 0 #puzzles completed
        self.level = level
        if player:
            self._running = True
            self._player_name = player
        else:
            self._running = False
        self._timeout = timeout
        self._step = 0
        self._total_steps = 0
        self._state = {}
        self._papertrail = ""  # keeps track of all steps made by the player
        self._moves = 0
        self._pushes = 0
        self.map = None
        self._lastkeypress = ""

        self.next_level(self.level)

    def info(self):
        """Initial Static information about the game."""
        return {
            "fps": GAME_SPEED,
            "timeout": self._timeout,
            "map": f"levels/{self.level}.xsb",
        }

    @property
    def papertrail(self):
        """String containing all pressed keys by agent."""
        return self._papertrail

    @property
    def running(self):
        """Status on game."""
        return self._running

    @property
    def score(self):
        """Calculus of the current score."""
        return self.puzzles, self._moves, self._pushes, self._total_steps + self._step, self.map.on_goal

    def stop(self):
        """Stop the game."""
        if self._step:
            logger.info("GAME OVER at %s", self._step)
        self._running = False

    def next_level(self, level):
        """Update all state variables to a new level."""
        self.puzzles += 1
        self._total_steps += self._step
        self._step = 0
        self._lastkeypress = ""
        self._papertrail += "," 
        self.level = level
        try:
            self.map = Map(f"levels/{level}.xsb")
            logger.info("NEXT LEVEL: %s", level)
        except FileNotFoundError:
            logger.info("No more levels... You WIN!")
            self.stop()
            return

    def keypress(self, key):
        """Update locally last key pressed."""
        self._lastkeypress = key

    def move(self, cur, direction):
        """Move an entity in the game."""
        assert direction in "wasd", f"Can't move in {direction} direction"

        cx, cy = cur
        ctile = self.map.get_tile(cur)

        npos = cur
        if direction == "w":
            npos = cx, cy - 1
        if direction == "a":
            npos = cx - 1, cy
        if direction == "s":
            npos = cx, cy + 1
        if direction == "d":
            npos = cx + 1, cy

        # test blocked
        if self.map.is_blocked(npos):
            logger.debug("Blocked ahead")
            return False
        if self.map.get_tile(npos) in [
            Tiles.BOX,
            Tiles.BOX_ON_GOAL,
        ]:  # next position has a box?
            if ctile & Tiles.MAN == Tiles.MAN:  # if you are the keeper you can push
                if not self.move(npos, direction):  # as long as the pushed box can move
                    return False
            else:  # you are not the Keeper, so no pushing
                return False

            self._moves += 1

        # actually update map
        self.map.set_tile(npos, ctile)
        self.map.clear_tile(cur)
        return True

    def update_keeper(self):
        """Update the location of the Keeper."""
        if self._lastkeypress == "":
            return GameStatus.NO_OPERATION
        try:
            # Update position
            self.move(self.map.keeper, self._lastkeypress)
            self._papertrail += self._lastkeypress
        except AssertionError:
            logger.error(
                "Invalid key <%s> pressed. Valid keys: w,a,s,d", self._lastkeypress
            )
        finally:
            self._lastkeypress = ""  # remove inertia

        if self.map.completed:
            logger.info("Level %s completed", self.level)
            self.next_level(self.level + 1)
            return GameStatus.NEW_MAP

        return GameStatus.RUNNING

    async def next_frame(self):
        """Calculate next frame."""
        await asyncio.sleep(1.0 / GAME_SPEED)

        if not self._running:
            logger.info("Waiting for player 1")
            return

        self._step += 1
        if self._step >= self._timeout:
            self.stop()

        if self._step % 100 == 0:
            logger.debug("[%s] SCORE %s", self._step, self.score)

        game_status = self.update_keeper()

        self._state = {
            "player": self._player_name,
            "level": self.level,
            "step": self._step,
            "score": self.score,
            "keeper": self.map.keeper,
            "boxes": self.map.boxes,
        }

        return game_status

    @property
    def state(self):
        """Contains the state of the Game."""
        # logger.debug(self._state)
        return json.dumps(self._state)
예제 #2
0
class SokobanDomain(SearchDomain):
    def __init__(self, filename):
        self.level = filename
        self.map = Map(filename)
        self.states = []
        self.emptyMap()

    def fillMap(self, state):
        for box in state["boxes"]:
            self.map.set_tile(box, Tiles.BOX)
        self.map.set_tile(state["player"], Tiles.MAN)

    def emptyMap(self):
        self.map.clear_tile(self.map.keeper)
        boxs = self.map.boxes
        for box in boxs:
            self.map.clear_tile(box)

    def actions(self, state):
        self.fillMap(state)
        actions = []
        for direction in ["w", "a", "s", "d"]:
            if (self.can_move(self.map.keeper, direction)):
                actions += [direction]
        self.emptyMap()
        return actions

    def can_move(self, cur, direction):
        """Move an entity in the game."""
        assert direction in "wasd", f"Can't move in {direction} direction"

        cx, cy = cur
        ctile = self.map.get_tile(cur)

        npos = cur
        if direction == "w":
            npos = cx, cy - 1
        if direction == "a":
            npos = cx - 1, cy
        if direction == "s":
            npos = cx, cy + 1
        if direction == "d":
            npos = cx + 1, cy

        # test blocked
        if self.map.is_blocked(npos):
            return False
        if self.map.get_tile(npos) in [
                Tiles.BOX,
                Tiles.BOX_ON_GOAL,
        ]:  # next position has a box?
            if ctile & Tiles.MAN == Tiles.MAN:  # if you are the keeper you can push
                if not self.move(
                        npos, direction):  # as long as the pushed box can move
                    return False
            else:  # you are not the Keeper, so no pushing
                return False

        return True

    def move(self, cur, direction):
        """Move an entity in the game."""
        assert direction in "wasd", f"Can't move in {direction} direction"

        cx, cy = cur
        ctile = self.map.get_tile(cur)

        npos = cur
        if direction == "w":
            npos = cx, cy - 1
        if direction == "a":
            npos = cx - 1, cy
        if direction == "s":
            npos = cx, cy + 1
        if direction == "d":
            npos = cx + 1, cy

        # test blocked
        if self.map.is_blocked(npos):
            return False
        if self.map.get_tile(npos) in [
                Tiles.BOX,
                Tiles.BOX_ON_GOAL,
        ]:  # next position has a box?
            if ctile & Tiles.MAN == Tiles.MAN:  # if you are the keeper you can push
                if not self.move(
                        npos, direction):  # as long as the pushed box can move
                    return False
            else:  # you are not the Keeper, so no pushing
                return False

        # actually update map
        self.map.set_tile(npos, ctile)
        self.map.clear_tile(cur)

        return True

    def result(self, state, action):
        self.fillMap(state)
        self.move(self.map.keeper, action)
        newstate = {}
        newstate["player"] = self.map.keeper
        newstate["boxes"] = self.map.boxes
        self.emptyMap()
        return newstate

    def cost(self, state, action):
        return 1

    def heuristic(self, state, goal):
        sum = 0
        list1 = state["boxes"]
        list2 = goal["boxes"]
        for i in range(len(list1)):
            sum += minimal_distance(list1[i], list2[i])
        return sum

    def satisfies(self, state, goal):
        self.fillMap(state)
        return self.map.completed

    def satisfies_box(self, box, goal):
        boxes = goal["boxes"]
        if box in boxes:
            return True
        return False