class Game: def __init__(self, mapfile, n_ghosts=GHOSTS, lives=LIVES, timeout=TIMEOUT): logger.info("Game({}, {}, {})".format(mapfile, n_ghosts, lives)) self._running = False self._timeout = timeout self._state = {} self._n_ghosts = n_ghosts self._initial_lives = lives self.map = Map(mapfile) self._highscores = [] if os.path.isfile(mapfile + ".score"): with open(mapfile + ".score", 'r') as infile: self._highscores = json.load(infile) def info(self): return json.dumps({ "map": self.map.filename, "ghosts": self._n_ghosts, "fps": GAME_SPEED, "timeout": TIMEOUT, "lives": LIVES, "points": { "energy": POINT_ENERGY, "boost": POINT_BOOST, "ghost": POINT_GHOST, "time_bonus": POINT_TIME_BONUS }, "boost_timeout": BOOST_TIMEOUT, "highscores": self.highscores, }) def consume(self, pos): """Update map at position.""" if pos in self._energy: self._energy.remove(pos) return Tiles.ENERGY if pos in self._boost: self._boost.remove(pos) return Tiles.BOOST @property def running(self): return self._running @property def score(self): return self._score @property def highscores(self): return self._highscores def start(self, player_name): logger.debug("Reset world") self._player_name = player_name self._running = True self.map = Map(self.map.filename) self._step = 0 self._ghosts = [Ghost(self.map) for g in range(0, self._n_ghosts)] self._pacman = self.map.pacman_spawn self._energy = self.map.energy self._boost = self.map.boost self._lastkeypress = "d" self._score = INITIAL_SCORE self._lives = self._initial_lives def stop(self): logger.info("GAME OVER") self.save_highscores() self._running = False def quit(self): logger.debug("Quit") self._running = False def save_highscores(self): #update highscores logger.debug("Save highscores") self._highscores.append((self._player_name, self.score)) self._highscores = sorted(self._highscores, key=lambda s: -1 * s[1])[:MAX_HIGHSCORES] with open(self.map._filename + ".score", 'w') as outfile: json.dump(self._highscores, outfile) def keypress(self, key): self._lastkeypress = key def update_pacman(self): self._pacman = self.map.calc_pos(self._pacman, self._lastkeypress) c = self.consume(self._pacman) if c == Tiles.ENERGY: self._score += POINT_ENERGY elif c == Tiles.BOOST: self._score += POINT_BOOST for g in self._ghosts: g.make_zombie(BOOST_TIMEOUT) if len(self._energy) + len(self._boost) == 0: logger.info("Level completed") self._score += ((self._timeout - self._step) // TIME_BONUS_STEPS) * POINT_TIME_BONUS self.stop() def collision(self): for g in self._ghosts: if g.pos == self._pacman and self._running: if g.zombie: self._score += POINT_GHOST g.respawn() else: logger.info("PACMAN has died") if self._lives: self._lives -= 1 self._pacman = self.map.pacman_spawn g.respawn() if not self._lives: self.stop() return async def next_frame(self): await asyncio.sleep(1. / 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("[{}] SCORE {} - LIVES {}".format( self._step, self._score, self._lives)) self.update_pacman() self.collision() for ghost in self._ghosts: ghost.update(self._state) self.collision() self._state = { "step": self._step, "player": self._player_name, "score": self._score, "lives": self._lives, "pacman": self._pacman, "ghosts": [(g.pos, g.zombie, g.zombie_timeout) for g in self._ghosts], "energy": self._energy, "boost": self._boost, } @property def state(self): return json.dumps(self._state)
async def agent_loop(server_address="localhost:8000", agent_name="student"): async with websockets.connect( "ws://{}/player".format(server_address)) as websocket: # Receive information about static game properties await websocket.send(json.dumps({"cmd": "join", "name": agent_name})) msg = await websocket.recv() game_properties = json.loads(msg) _map = Map(game_properties['map']) area = _map.size[0] * _map.size[1] GHOST_PENALIZATION = area BOOST_PENALIZATION = max(_map.size) GHOST_CHASE_DISTANCE = 3 N_GOALS = 4 ZOMBIE_SPEED = .5 #initialize agent properties key = None cur_x, cur_y = None, None ptarget = None while True: r = await websocket.recv() state = json.loads(r) ppos = tuple(state['pacman']) lenergy = [tuple(x) for x in state['energy']] lghosts = [tuple(x[0]) for x in state['ghosts'] if not x[1]] lzombies = [(tuple(x[0]), x[2]) for x in state['ghosts'] if x[1]] lboosts = [tuple(x) for x in state['boost']] if ptarget is None: ptarget = ppos if not state['lives'] or (len(lenergy) == 0 and len(lboosts) == 0): logger.info("GAME OVER") return logger.debug("Lives = %d", state['lives']) # Create new search problem targets = select_target(ppos, lenergy, lghosts, lzombies, lboosts, ptarget, tuple(_map.ghost_spawn), GHOST_CHASE_DISTANCE, ZOMBIE_SPEED) # Create multiple goals goals = [] for i in range(min(len(targets), N_GOALS)): goals += [(targets[i], None)] # Search all goals at the same time if len(lghosts) == 0 and len(lzombies) == 0: st = SearchTree( SearchProblem(PacPath(_map, lghosts, lboosts), (ppos, None), goals)) else: st = SearchTree( SearchProblem( PacPath(_map, lghosts, lboosts, GHOST_PENALIZATION, BOOST_PENALIZATION), (ppos, None), goals)) path = st.search() # When trapped by ghosts take a valid step if path is None: logger.debug("No Path found...") if direction is None: for d in ['w', 's', 'a', 'd']: npos = _map.calc_pos(ppos, d) if npos != ppos: direction = d target = npos cost = 1 else: target = path[1][-1][0] direction = path[1][1][1] cost = path[0] logger.debug("Target = %s Direction = %s (Cost = %f)", target, direction, cost) #send new key await websocket.send(json.dumps({"cmd": "key", "key": direction})) ptarget = target
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._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 def start(self, player_name): logger.debug("Reset world") self._player_name = player_name self._running = True self._score = INITIAL_SCORE self._bomberman = Bomberman(self.map.bomberman_spawn, self._initial_lives) self.next_level(self.initial_level) def stop(self): logger.info("GAME OVER") 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._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(self._enemies) 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): 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() 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) 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() 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)
async def agent_loop(server_address="localhost:8000", agent_name="69892-73761"): async with websockets.connect( "ws://{}/player".format(server_address)) as websocket: # Receive information about static game properties await websocket.send(json.dumps({"cmd": "join", "name": agent_name})) msg = await websocket.recv() game_properties = json.loads(msg) mapa = Map(game_properties['map']) # init agent properties key = 'a' agent = Agent(None, None, None) pac_buf = [] while True: r = await websocket.recv() state = json.loads(r) # receive game state # Points energy = state['energy'] boosts = state['boost'] points = energy + boosts if not state['lives'] or points == []: print("GAME OVER") return # Pacman Position pac_pos = state['pacman'] pac_buf.append(state['pacman']) agent.set_buff_pac_pos(pac_buf) # Ghost(s) Position(s) ghosts_pos = [g[0] for g in state['ghosts']] # Ghost(s) State ghosts_state = [g[1] for g in state['ghosts']] # Ghost(s) timeout ghosts_time = [g[2] for g in state['ghosts']] # Closest Point cp = agent.closest_point(pac_pos, points) dirs = [] #print() #print('Lives: ',state['lives']) #print('Ghosts Pos: ',ghosts_pos) #print('Pacman Pos: ',pac_pos) #print('Cp: ',cp) if len(state['ghosts']) == 0: dirs = agent.dir_to_point(pac_pos, cp) elif len(state['ghosts']) >= 1: if len(pac_buf) == 4 and agent.check_pac_buff(pac_buf): dirs = agent.dir_pac pac_buf = [] continue elif len(pac_buf) > 1 and agent.check_pos_buff(pac_pos): dirs = agent.dir_pac continue elif len(pac_buf) == 4 and not agent.check_pac_buff(pac_buf): pac_buf = [] if any(ghosts_state) == True: g_pos = [ ghosts_pos[i] for i, g in enumerate(ghosts_state) if g == True ] for pos in g_pos: dst_ghost = agent.distance_from_ghost(pac_pos, pos) if dst_ghost > 1: dirs = agent.dir_to_point(pac_pos, pos) agent.set_dirs(dirs) break else: dirs = agent.dir_to_point(pac_pos, cp) agent.set_dirs(dirs) #break if any(ghosts_state) == False: g_pos = [ ghosts_pos[i] for i, g in enumerate(ghosts_state) if g == False ] for pos in g_pos: dst_ghost = agent.distance_from_ghost(pac_pos, pos) if dst_ghost < 3: dirs = agent.reverse_dir_to_point(pac_pos, pos) if agent.distance_to_point( pac_pos, cp ) > 1: #and agent.distance_to_point(pac_pos, pos) < 5: dirs = agent.dir_to_point(pac_pos, pos) agent.set_dirs(dirs) #dirs = agent.change_dir_escaping(pac_pos, pos, cp) #print('inside if dist cp == 1: ',dirs) break else: dirs = agent.dir_to_point(pac_pos, cp) agent.set_dirs(dirs) #break for d in dirs: npos = mapa.calc_pos(pac_pos, d) if npos != pac_pos: key = d break # send new key await websocket.send(json.dumps({"cmd": "key", "key": key}))