Exemplo n.º 1
0
    def actions(self, current_state):
        '''
            RECEIVES: the current state
            RETURNS: a list containing all the valid actions for the state
            
            Calculates all the valid actions for a state:
                -> Receives a dictionary containing the current state
                -> For every direction in "wasd" calculates the next state (positions of the keeper and boxes)
                -> Verifies wether the boxes are in a deadlock
                -> Return a list of valid actions (directions)
        '''
        valid_directions = []

        for direction in DIRECTIONS:
            next_state = calc_next_state(current_state, direction)
            keeper = next_state['keeper']
            boxes = next_state['boxes']
            valid_directions.append(direction)

            # Check wether we are placing a box outside of the map or
            # placing a box on top of another box
            if Map.is_blocked(self.level_map,
                              keeper) or len(list(set(boxes))) < len(boxes):
                valid_directions.remove(direction)
                continue

            for box in boxes:
                if box in self.deadlocks_pos:
                    valid_directions.remove(direction)
                    continue
                elif self.isDeadlock(box):
                    self.deadlocks_pos.append(box)
                    self.deadlocks.append(next_state)
                    valid_directions.remove(direction)
        return list(set(valid_directions))
Exemplo n.º 2
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)
Exemplo n.º 3
0
class Game:
    def __init__(self, level=1, lives=LIVES, timeout=TIMEOUT, size=MAP_SIZE):
        logger.info(f"Game(level={level}, lives={lives})")
        self.initial_level = level
        self._running = False
        self._timeout = timeout
        self._score = 0
        self._step = 0
        self._total_steps = 0
        self._state = {}
        self._initial_lives = lives
        self.map = Map(size=size, empty=True)
        self._enemies = []

    def info(self):
        return {
            "size": self.map.size,
            "map": self.map.map,
            "fps": GAME_SPEED,
            "timeout": TIMEOUT,
            "lives": LIVES,
            "score": self.score,
        }

    @property
    def running(self):
        return self._running

    @property
    def score(self):
        return self._score

    @property
    def total_steps(self):
        return self._total_steps

    def start(self, player_name):
        logger.debug("Reset world")
        self._player_name = player_name
        self._running = True
        self._total_steps = 0
        self._score = INITIAL_SCORE
        self._bomberman = Bomberman(self.map.bomberman_spawn,
                                    self._initial_lives)
        for powerup in range(1, self.initial_level):
            self._bomberman.powerup(LEVEL_POWERUPS[powerup])
        logger.debug("Bomberman Powerups: %s", self._bomberman.powers)

        self.next_level(self.initial_level)

    def stop(self):
        logger.info("GAME OVER")
        self._total_steps += self._step
        self._running = False

    def next_level(self, level):
        if level > len(LEVEL_ENEMIES):
            logger.info("You WIN!")
            self.stop()
            return

        logger.info("NEXT LEVEL")
        self.map = Map(level=level,
                       size=self.map.size,
                       enemies=len(LEVEL_ENEMIES[level]))
        self._bomberman.respawn()
        self._total_steps += self._step
        self._step = 0
        self._bombs = []
        self._powerups = []
        self._bonus = []
        self._exit = []
        self._lastkeypress = ""
        self._enemies = [
            t(p) for t, p in zip(LEVEL_ENEMIES[level], self.map.enemies_spawn)
        ]
        logger.debug("Enemies: %s", [(e._name, e.pos) for e in self._enemies])
        logger.debug("Walls: %s", self.map.walls)

    def quit(self):
        logger.debug("Quit")
        self._running = False

    def keypress(self, key):
        self._lastkeypress = key

    def update_bomberman(self):
        try:
            if self._lastkeypress.isupper():
                # Parse action
                if self._lastkeypress == "A" and len(self._bombs) > 0:
                    self._bombs[0].detonate(
                    )  # always detonate the oldest bomb
                elif (self._lastkeypress == "B" and len(self._bombs) <
                      self._bomberman.powers.count(Powerups.Bombs) + 1
                      and not self.map.is_blocked(self._bomberman.pos)):
                    self._bombs.append(
                        Bomb(
                            self._bomberman.pos,
                            self.map,
                            MIN_BOMB_RADIUS + self._bomberman.flames(),
                            detonator=Powerups.Detonator
                            in self._bomberman.powers,
                        ))  # must be dependent of powerup
            else:
                # Update position
                new_pos = self.map.calc_pos(
                    self._bomberman.pos, self._lastkeypress,
                    self._bomberman.wallpass)  # don't bump into stones/walls
                if self._bomberman.bombpass or new_pos not in [
                        b.pos for b in self._bombs
                ]:  # don't pass over bombs
                    self._bomberman.pos = new_pos
                for pos, _type in self._powerups:  # consume powerups
                    if new_pos == pos:
                        self._bomberman.powerup(_type)
                        self._powerups.remove((pos, _type))

        except AssertionError:
            logger.error("Invalid key <%s> pressed. Valid keys: w,a,s,d A B",
                         self._lastkeypress)
        finally:
            self._lastkeypress = ""  # remove inertia

        if len(self._enemies) == 0 and self._bomberman.pos == self._exit:
            logger.info(f"Level {self.map.level} completed")
            #self._score += self._timeout - self._step
            self.next_level(self.map.level + 1)

    def kill_bomberman(self):
        logger.info(f"bomberman has died on step: {self._step}")
        self._bomberman.kill()
        logger.debug(f"bomberman has now {self._bomberman.lives} lives")
        if self._bomberman.lives > 0:
            logger.debug("RESPAWN")
            self._bomberman.respawn()
            for e in self._enemies:
                if distance(self._bomberman.pos, e.pos) < VITAL_SPACE:
                    logger.debug("respawn camper")
                    e.respawn()
            self._bombs = []
        else:
            self.stop()

    def collision(self):
        for e in self._enemies:
            if e.pos == self._bomberman.pos:
                self.kill_bomberman()
                e.respawn()

    def explode_bomb(self):
        for bomb in self._bombs[:]:
            bomb.update()
            if bomb.exploded():
                logger.debug("BOOM")
                if bomb.in_range(
                        self._bomberman) and not self._bomberman.flamepass:
                    self.kill_bomberman()

                for wall in self.map.walls[:]:
                    if bomb.in_range(wall):
                        logger.debug(f"Destroying wall @{wall}")
                        self.map.remove_wall(wall)
                        if self.map.exit_door == wall:
                            self._exit = wall
                        if self.map.powerup == wall:
                            self._powerups.append(
                                (wall, LEVEL_POWERUPS[self.map.level]))

                for enemy in self._enemies[:]:
                    if bomb.in_range(enemy):
                        logger.debug(f"killed enemy @{enemy}")
                        self._score += enemy.points()
                        self._enemies.remove(enemy)

                if bomb in self._bombs:
                    self._bombs.remove(bomb)

    async def next_frame(self):
        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(
                f"[{self._step}] SCORE {self._score} - LIVES {self._bomberman.lives}"
            )

        self.explode_bomb()
        self.update_bomberman()
        self.collision()

        if (self._step % (self._bomberman.powers.count(Powerups.Speed) + 1) ==
                0):  # increase speed of bomberman by moving enemies less often
            for enemy in self._enemies:
                enemy.move(self.map, self._bomberman, self._bombs,
                           self._enemies)
            self.collision()

        #sanity check
        assert all([
            ep not in self.map.walls
            for ep in [e.pos for e in self._enemies if not e._wallpass]
        ])

        self._state = {
            "level":
            self.map.level,
            "step":
            self._step,
            "timeout":
            self._timeout,
            "player":
            self._player_name,
            "score":
            self._score,
            "lives":
            self._bomberman.lives,
            "bomberman":
            self._bomberman.pos,
            "bombs": [(b.pos, b.timeout, b.radius) for b in self._bombs],
            "enemies": [{
                "name": str(e),
                "id": str(e.id),
                "pos": e.pos
            } for e in self._enemies],
            "walls":
            self.map.walls,
            "powerups": [(p, Powerups(n).name) for p, n in self._powerups],
            "bonus":
            self._bonus,
            "exit":
            self._exit,
        }

    @property
    def state(self):
        # logger.debug(self._state)
        return json.dumps(self._state)
Exemplo n.º 4
0
class AI_Agent():
    def __init__(self, game_properties):
        self.logger = logging.getLogger("AI AGENT")
        self.logger.setLevel(logging.WARN)
        self.logger.info("AI Agent created.")

        self.map = Map(size=game_properties["size"],
                       mapa=game_properties["map"])
        self.logger.debug(self.map)

        self.update_wallpass_next = False
        self.cur_pos = None
        self.walls = None
        self.enemies = None
        self.powerups = None
        self.bonus = None
        self.exit = None
        self.have_powerup = None
        self.level = None
        self.lives = 3  #this shouldnt be hardcoded

        # self.enemy_past_pos = {}
        self.pursuing_enemy = None
        # self.eval_enemy = None

        self.depth_limit = 100
        self.loop = 0
        self.search_domain = BombermanSearch(self.map)

        self.decisions_queue = []

        self.rounds_pursuing_limit = 3  # Limit of rounds we can be pursuing the same enemy
        self.wait_time = 200  # time to wait when in loop pursuing enemy

        self.last_enemy_dir = None
        self.perform_last_resort = False

    def dist(self, pos1, pos2):
        return math.hypot(pos2[0] - pos1[0], pos2[1] - pos1[1])

    def update_pursuing_enemy(self, closest_enemy):
        if (self.pursuing_enemy is None
                or self.pursuing_enemy['id'] != closest_enemy['id']):
            self.pursuing_enemy = closest_enemy
            self.pursuing_enemy['last_pos'] = None
            self.pursuing_enemy['rounds_pursuing'] = 0
            self.reset_waiting_limit()
        else:
            last_pos = self.pursuing_enemy[
                'pos']  # keep track of enemy movement
            rounds_pursuing = self.pursuing_enemy['rounds_pursuing']
            self.pursuing_enemy = closest_enemy
            self.pursuing_enemy['last_pos'] = last_pos
            self.pursuing_enemy['rounds_pursuing'] = rounds_pursuing

    def incr_round(self):
        rounds_pursuing = self.pursuing_enemy['rounds_pursuing']
        self.pursuing_enemy['rounds_pursuing'] = rounds_pursuing + 1

    def reset_life(self):
        self.logger.info(
            "I Died, will restart decisionQueue------------------------------------------"
        )
        #self.cur_pos = [1,1]
        self.decisions_queue = []

    def reset_level(self):
        self.logger.info("NEW LEVEL")
        #self.cur_pos = [1,1]
        self.decisions_queue = []
        self.have_powerup = False
        self.pursuing_enemy = None
        self.search_domain.remove_destroyed_walls()

    def reset_waiting_limit(self):
        if self.level > 2:
            self.rounds_pursuing_limit = 4  # Limit of rounds we can be pursuing the same enemy
            self.wait_time = 70  # time to wait when in loop pursuing enemy
        elif self.level > 6:
            self.wait_time = 50
            self.rounds_pursuing = 5

    def update_level(self, level):
        if self.level is None:
            self.level = level
        if self.level != level:
            self.reset_level()
            self.reset_map_walls()
        self.level = level

    def reset_map_walls(self):
        self.logger.debug("Updating walls because NEXT LEVEL")
        self.search_domain.set_walls(self.walls)

    def closest_enemy(self, pos=None):
        if pos is None:
            pos = self.cur_pos
        closest = None
        for enemy in self.enemies:
            d = self.dist(pos, enemy['pos'])
            if closest is None or d < closest[1]:
                closest = (enemy, d)
        return closest[0] if closest != None else None

    #Importante para nao perder tempo a procura de paredes
    def closest_wall(self, i=0):
        closest_list = []
        if not self.walls:
            return None
        for wall in self.walls:
            d = self.dist(self.cur_pos, wall)
            closest_list.append((wall, d))
            # if closest is None or d < closest[1]:
            #     closest = (wall, d)
        closest_list = sorted(closest_list, key=lambda x: x[1])
        return closest_list[i][0]

    def calculate_path(self, origin, goal):
        problem = SearchProblem(self.search_domain, origin, goal)
        tree = SearchTree(problem, strategy='greedy')
        self.logger.debug("Searching path from " + str(origin) + " to " +
                          str(goal))
        # self.logger.debug(self.walls)
        path, moves = tree.search(depth_limit=self.depth_limit)
        self.logger.debug("Path found.")
        return (path, moves)

    def find_direction(self, prev_pos, pos):
        #change this
        if prev_pos is None:
            return ''
        if prev_pos[0] < pos[0]:
            return 'd'
        elif prev_pos[0] > pos[0]:
            return 'a'
        elif prev_pos[1] > pos[1]:
            return 'w'
        elif prev_pos[1] < pos[1]:
            return 's'

    def dir_in_x(self, d):
        if d in ['a', 'd']:
            return True
        return False

    def dir_in_y(self, d):
        if d in ['w', 's']:
            return True
        return False

    def opposite_move(self, move):
        if move == 'w':
            return 's'
        elif move == 's':
            return 'w'
        elif move == 'a':
            return 'd'
        elif move == 'd':
            return 'a'

    def running_away(self, move):
        # self.logger.debug(str(self.pursuing_enemy))
        enemy_dir = self.find_direction(self.pursuing_enemy['last_pos'],
                                        self.pursuing_enemy['pos'])

        cond1 = self.dir_in_x(enemy_dir) and (self.cur_pos[1]
                                              == self.pursuing_enemy['pos'][1])
        cond2 = self.dir_in_y(enemy_dir) and (self.cur_pos[0]
                                              == self.pursuing_enemy['pos'][0])

        if enemy_dir == move and (cond1 or cond2):
            self.logger.debug("Enemy running away from me.")
            return True
        return False

    def running_towards(self, move):
        if (not 'last_pos' in self.pursuing_enemy
                or self.pursuing_enemy['last_pos'] is None
                or self.pursuing_enemy is None):
            return False
        # self.logger.debug(str(self.pursuing_enemy))
        if (self.find_direction(self.pursuing_enemy['last_pos'],
                                self.pursuing_enemy['pos'])
                == self.opposite_move(move)
                and ((self.cur_pos[0] == self.pursuing_enemy['pos'][0]) or
                     (self.cur_pos[1] == self.pursuing_enemy['pos'][1]))):
            self.logger.debug("Enemy running towards me.")
            return True
        return False

    def select_bomb_point(self, target, i=0):
        closest = None
        for pos in [
                self.search_domain.result(target, mov)
                for mov in self.search_domain.actions(target)
        ]:
            if pos in self.walls:
                continue
            d = self.dist(self.cur_pos, pos)
            if closest is None or d < closest[1]:
                closest = (pos, d)

        if closest is None:  # did not find place to put bomb
            self.logger.debug(f'Current wall {target} is blocked')
            target = self.closest_wall(i=i + 1)
            return self.select_bomb_point(target, i + 1)

        self.logger.debug("Closest wall: " + str(target) + ". Going to " +
                          str(closest[0]))

        self.bomb_point = closest[0]

        path, moves = self.calculate_path(self.cur_pos, closest[0])
        return (path, moves)

    def hide(self, path, moves):
        # hide in nearby position
        # but choose the one closest to next wall ? not yet

        quad1 = [['w', 'd'], ['d', 'w'], ['w', 'w', 'd'], ['d', 'd', 'w'],
                 ['w', 'w', 'w', 'd'], ['d', 'd', 'd', 'w']]
        quad2 = [['w', 'a'], ['a', 'w'], ['w', 'w', 'a'], ['a', 'a', 'w'],
                 ['a', 'a', 'a', 'w'], ['w', 'w', 'w', 'a']]
        quad3 = [['s', 'a'], ['a', 's'], ['s', 's', 'a'], ['a', 'a', 's'],
                 ['a', 'a', 'a', 's'], ['s', 's', 's', 'a']]
        quad4 = [['s', 'd'], ['d', 's'], ['s', 's', 'd'], ['d', 'd', 's'],
                 ['s', 's', 's', 'd'], ['d', 'd', 'd', 's']]

        last_pos = path[-1]
        #Instead of none, we have a possibility to stay in place if there is no exit
        best = last_pos, ['']

        quad1n = 0
        quad2n = 0
        quad3n = 0
        quad4n = 0

        for e in self.enemies:
            epos = e['pos']
            if self.search_domain.dist(last_pos, epos) < 8:  #only the closests

                if last_pos[0] > epos[0]:  #esquerda
                    if last_pos[1] < epos[1]:  #baixo
                        quad3n += 2
                    if last_pos[1] > epos[1]:  #cima
                        quad2n += 2
                    else:
                        quad2n += 1
                        quad3n += 1

                if last_pos[0] < epos[0]:  #direita
                    if last_pos[1] < epos[1]:  #baixo
                        quad4n += 2
                    if last_pos[1] > epos[1]:  #cima
                        quad1n += 2
                    else:
                        quad1n += 1
                        quad4n += 1

        possible_moves = []

        if quad1n != 0 and quad2n == 0 and quad3n == 0 and quad4n == 0:
            possible_moves += quad3 + quad4 + quad2 + quad1
        if quad1n == 0 and quad2n != 0 and quad3n == 0 and quad4n == 0:
            possible_moves += quad4 + quad1 + quad3 + quad2
        if quad1n == 0 and quad2n == 0 and quad3n != 0 and quad4n == 0:
            possible_moves += quad1 + quad2 + quad4 + quad3
        if quad1n == 0 and quad2n == 0 and quad3n == 0 and quad4n != 0:
            possible_moves += quad2 + quad3 + quad1 + quad4
        else:
            helper = {
                'quad1': quad1n,
                'quad2': quad2n,
                'quad3': quad3n,
                'quad4': quad4n
            }
            for i in sorted(helper.items(), key=lambda e: (e[1], e[0])):
                if i[0] == 'quad1':
                    possible_moves += quad1
                elif i[0] == 'quad2':
                    possible_moves += quad2
                elif i[0] == 'quad3':
                    possible_moves += quad3
                elif i[0] == 'quad4':
                    possible_moves += quad4

        for possible_move in possible_moves:

            p = [last_pos]
            this_works = True

            hide_path = self.result(possible_move)
            if len(self.enemies) > 0:
                for e in self.enemies:
                    if e['pos'] in hide_path:
                        this_works = False

            if not this_works:
                continue

            this_works = True

            #print(last_pos,possible_move)

            if self.can_i_do_this(last_pos, possible_move):
                for move in possible_move:
                    p.append(self.search_domain.result(p[-1], move))

                best = (p, possible_move)
                break
        #aqui verificar se best == None e se for talvez andar para tras conforme o radius da bomab?

        self.logger.debug("Hiding from bomb in " + str(best[0][-1]) + " (" +
                          str(best[1]) + ")")

        path += best[0]
        moves += best[1]

    def result(self, moves, origin=None):
        if origin is None:
            origin = self.cur_pos
        path = [origin]
        for mov in moves:
            r = self.search_domain.result(path[-1], mov)
            path.append(r)
        return path

    def can_i_do_this(self, pos, possible_move):
        if possible_move == []:
            return True

        enemies_pos = []
        for e in self.enemies:
            enemies_pos.append(e['pos'])

        letter = possible_move[0]
        #print(pos, letter, letter in self.search_domain.actions(pos))

        return ((letter in self.search_domain.actions(pos))
                and self.can_i_do_this(self.search_domain.result(pos, letter),
                                       possible_move[1:]))

    def predict_enemy(self, n_moves, enemy=None):
        if enemy is None:
            enemy = self.pursuing_enemy

        if 'last_pos' in enemy:
            direction = self.find_direction(enemy['last_pos'], enemy['pos'])
            moves = [direction for _ in range(n_moves)]
            result = self.result(moves, enemy['pos'])
            return result
        else:
            return None

    def allBalloms(self):
        for e in self.enemies:
            if e['name'] not in ['Balloom', 'Doll']:
                return False
        return True

    def decide_move(self):
        if self.update_wallpass_next:
            self.logger.info("I can walk through walls now!")
            self.search_domain.wallpass = True
        if self.powerups:  # powerup to pick up
            powerup_popped = self.powerups.pop(0)  # 0 - pos, 1 - type
            powerup = powerup_popped[0]
            powerup_name = powerup_popped[1]  # [[15, 16], 'Flames']
            self.have_powerup = True
            self.logger.debug("Going for powerup: " + str(powerup))
            path, moves = self.calculate_path(self.cur_pos, powerup)
            if len(self.enemies) > 0:
                self.powerups.append(powerup)
                if str(powerup_name) == "Wallpass":
                    self.update_wallpass_next = True
                return [moves[0]]
            return moves

        if self.exit and not self.enemies and self.have_powerup:
            path, moves = self.calculate_path(self.cur_pos, self.exit)
            return moves

        #Does this solve?
        closest_enemy = self.closest_enemy()
        closest_wall = self.closest_wall()

        if (closest_enemy is not None and
            (not self.walls or self.dist(self.cur_pos, closest_enemy['pos']) <=
             self.dist(self.cur_pos, closest_wall)
             #or (self.have_powerup and self.exit)
             ) and not closest_enemy['pos'] in self.walls):

            self.update_pursuing_enemy(closest_enemy)

            #se distancia for maior que o metade do tamanho do mapa, vamos para uma posiçao no meio e tentamos procurar a partir de la
            if False:
                #melhorar esta porcaria xd
                #if self.search_domain.dist(self.cur_pos, closest_enemy['pos']) >= self.map._size[1]/2:
                chosen_pos = [
                    int(self.map._size[0] / 2),
                    int(self.map._size[1] / 2)
                ]
                self.logger.debug("Going for %s" % (chosen_pos))
                while self.map.is_blocked(chosen_pos) or self.map.is_stone(chosen_pos) \
                    or chosen_pos not in self.search_domain.destroyed_walls or chosen_pos!=[self.map._size[0]-1,self.map._size[1]-1]:

                    chosen_pos = [ele + 1 for ele in chosen_pos]
                    self.logger.debug("Going for %s" % (chosen_pos))
                path, moves = self.calculate_path(self.cur_pos, chosen_pos)
                return moves

            else:
                path, moves = self.calculate_path(self.cur_pos,
                                                  closest_enemy['pos'])
                if (len(moves) > 0 and self.search_domain.dist(
                        self.cur_pos, closest_enemy['pos']) <= 2
                        and not self.running_away(moves[-1])):

                    if self.walls == [] and self.allBalloms():
                        self.perform_last_resort = True

                    moves = ['B']
                    self.incr_round()
                    self.hide([self.cur_pos], moves)
                elif self.search_domain.dist(self.cur_pos,
                                             closest_enemy['pos']) <= 1:
                    if self.walls == [] and self.allBalloms():
                        self.perform_last_resort = True

                    moves = ['B']
                    self.incr_round()
                    self.hide([self.cur_pos], moves)
                elif ((self.pursuing_enemy['name'] == 'Balloom'
                       or self.pursuing_enemy['name'] == 'Doll')
                      and self.pursuing_enemy['rounds_pursuing'] >=
                      self.rounds_pursuing_limit):

                    enemy_direction = self.find_direction(
                        self.pursuing_enemy['last_pos'],
                        self.pursuing_enemy['pos'])
                    if enemy_direction is None:
                        enemy_direction = self.last_enemy_dir
                    if enemy_direction == 'w':
                        self.last_enemy_dir = 'w'
                        if not self.map.is_stone(
                            (self.cur_pos[0] - 1, self.cur_pos[1])):
                            return ['a']
                        elif not self.map.is_stone(
                            (self.cur_pos[0], self.cur_pos[1] - 1)):
                            return ['w']
                        else:
                            self.pursuing_enemy['rounds_pursuing'] = 0
                            return [moves[0]]
                    elif enemy_direction == 'a':
                        self.last_enemy_dir = 'a'
                        if not self.map.is_stone(
                            (self.cur_pos[0], self.cur_pos[1] + 1)):
                            return ['s']
                        elif not self.map.is_stone(
                            (self.cur_pos[0] - 1, self.cur_pos[1])):
                            return ['a']
                        else:
                            self.pursuing_enemy['rounds_pursuing'] = 0
                            return [moves[0]]

                    elif enemy_direction == 's':
                        self.last_enemy_dir = 's'
                        if not self.map.is_stone(
                            (self.cur_pos[0] + 1, self.cur_pos[1])):
                            return ['d']
                        elif not self.map.is_stone(
                            (self.cur_pos[0], self.cur_pos[1] + 1)):
                            return ['s']
                        else:
                            self.pursuing_enemy['rounds_pursuing'] = 0
                            return [moves[0]]

                    elif enemy_direction == 'd':
                        self.last_enemy_dir = 'd'
                        if not self.map.is_stone(
                            (self.cur_pos[0], self.cur_pos[1] - 1)):
                            return ['w']
                        elif not self.map.is_stone(
                            (self.cur_pos[0] + 1, self.cur_pos[1])):
                            return ['d']
                        else:
                            self.pursuing_enemy['rounds_pursuing'] = 0
                            return [moves[0]]

                elif self.perform_last_resort:
                    '''pos = [1,1]
                    while self.map.is_blocked(pos) or self.map.is_stone(pos):
                        pos = [x+1 for x in pos] 
                    if self.cur_pos == pos:
                        return ''
                    self.logger.info("Chosen position to camp: %s" % (pos))
                    path, moves = self.calculate_path(self.cur_pos, pos)
                    return [moves[0]]'''
                    if not self.map.is_stone(
                        (self.cur_pos[0], self.cur_pos[1] - 1)):
                        return ['w']
                    elif not self.map.is_stone(
                        (self.cur_pos[0] - 1, self.cur_pos[1])):
                        return ['a']
                    else:
                        return ['']

                elif self.pursuing_enemy['name'] in [
                        'Oneal', 'Minvo', 'Ovapi', 'Kondoria', 'Pass'
                ] and self.pursuing_enemy['rounds_pursuing'] > 10:
                    self.pursuing_enemy['rounds_pursuing'] = 0
                    if self.cur_pos[0] > self.pursuing_enemy['pos'][0]:
                        return ['a', 's', 'a', 's', 'a']
                    else:
                        return ['d', 's', 'd', 's', 'd']
                else:
                    return [moves[0]]

        elif closest_wall != None:
            # self.eval_enemy = False
            path, moves = self.select_bomb_point(closest_wall)
            if not self.enemies or self.search_domain.dist(
                    self.cur_pos, closest_wall) <= 1:
                moves.append('B')  # leave a bomb at the end
                self.hide(path, moves)
            else:
                return [moves[0]]

        if (self.level == 3 and self.have_powerup or self.level > 3):
            moves.append('A')
        return moves

    def next_move(self, state):

        if self.enemies != None and len(self.enemies) != len(
                state['enemies']):  #Enemy killed
            self.perform_last_resort = False

        self.cur_pos = state['bomberman']
        self.enemies = state['enemies']
        self.powerups = state['powerups']
        self.bonus = state['bonus']
        self.exit = state['exit']
        #self.walls = state['walls']

        ##Keep track of enemies past positions to check for loops
        #for enemy in self.enemies:
        #    if enemy['id'] in self.enemy_past_pos:
        #        old_pos = self.enemy_past_pos[enemy['id']]
        #        new_pos = enemy['pos']
        #        old_pos.append(new_pos)
        #        self.enemy_past_pos[enemy['id']] = old_pos
        # else:
        #     self.enemy_past_pos[enemy['id']] = [enemy['pos']]

        # self.logger.info("Past enemies positions: %s" % (self.enemy_past_pos))
        # Workaround to compare previous and current walls
        new_walls = state['walls']
        if self.walls is not None:
            for wall in [w for w in self.walls if w not in new_walls]:
                self.search_domain.set_destroyed_wall(wall)
        self.walls = new_walls

        lost_life = self.lives != state['lives']
        self.lives = state['lives']

        #Clear after death
        if lost_life:
            self.reset_life()

        level = state['level']
        self.update_level(level)

        # if queue is empty and there are no bombs placed
        if ((not self.decisions_queue) and not state['bombs']):
            self.decisions_queue = self.decide_move()

        if not self.decisions_queue:
            return ['']

        self.logger.debug("Path: " + str(self.decisions_queue))
        next_move = self.decisions_queue.pop(0)
        self.logger.debug("Next move: " + str(next_move))

        return next_move
Exemplo n.º 5
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