def __init__(self, config, client): self.client = client ### Pygame related ### pygame.init() self.config = config self.tile_size = config['tile_size'] self.borders_width = config['borders_width'] # Based on the tile size (px), ensure that the final resolution makes for an even # number of tiles if config['target_resolution']: tmp_screen_size = config['target_resolution'] else: tmp_screen_size = pygame.display.Info() tmp_screen_size = Pos(x=tmp_screen_size.current_w, y=tmp_screen_size.current_h) self.tiles_nbr = tmp_screen_size // self.tile_size if self.tiles_nbr.x % 2: self.tiles_nbr.x -= 1 if self.tiles_nbr.y % 2: self.tiles_nbr.y -= 1 # Set variables self.screen_size = self.tiles_nbr * self.tile_size self.hud_tiles_nbr = (config['hud_width_px'] // self.tile_size) + 1 self.map_tiles_nbr = Pos(x=self.tiles_nbr.x - 3 - self.hud_tiles_nbr, y=self.tiles_nbr.y - 2) # Launch display self.display = pygame.display.set_mode( (self.screen_size.x, self.screen_size.y)) pygame.display.set_caption('Clippy') ### ASSETS ### self.font = pygame.font.Font('./assets/fonts/Everson_Mono.ttf', 24) self.sprites = {} self.load_available_sprites(self.tile_size) self.last_print_time = time.time()
def parse_map(s, map_raw): s.width = len(map_raw[0]) s.height = len(map_raw) if s.width < 2 or s.height < 2: raise MapError( f"{s.file_path}: Map too small ({s.width} x {s.height})") s.map = [EMPTY for x in range(s.width * s.height)] for y_idx, l in enumerate(map_raw): if len(l) != s.width: raise MapError( f"{s.file_path}: Invalid length {len(l)} for line {y_idx}") for x_idx, c in enumerate(l): if c == 'X': # Wall s.set_square(WALL, y_idx, x_idx) elif c == 'P': # Player if s.player_pos: print( "Multiple player locations detected, the last one found will be used" ) s.player_pos = Pos(y_idx, x_idx) s.set_square(PLAYER, y_idx, x_idx) elif c == 'E': # Enemy s.enemies_pos.append(Pos(y_idx, x_idx)) s.set_square(ENEMY, y_idx, x_idx) elif c != "-": # Empty raise MapError( f"{s.file_path}: Invalid character ({c}) on line {y_idx}" ) if not s.enemies_pos or not s.player_pos: raise MapError( f"{s.file_path}: Map is missing a player and / or an enemy")
def generate_terrain_chunk(self, anchor_pos=Pos(0, 0)): ''' returns a double array of tuples: (bloc_type, region, noise_value) noise value is needed by clients to vary the display of similar bloc types''' chunk = [] regions_blocs_positions = {} for region in self.config['regions']: regions_blocs_positions[region] = [] for y in range(self.config['chunk_size']): tmp_line = [] for x in range(self.config['chunk_size']): tile_pos = Pos(x=anchor_pos.x + x, y=anchor_pos.y + y) noise_value = self.get_simplex_value(tile_pos) region = self.get_pos_region(tile_pos) bloc_type = self.get_bloc_type(noise_value, region) tmp_line.append((bloc_type, region, noise_value)) regions_blocs_positions[region].append((y, x)) chunk.append(tmp_line) # Now randomly swap blocs swaps_nbr = 0 for region in self.config['regions']: bloc_nbr_to_swap = len(regions_blocs_positions[region]) * self.config['regions'][region]['random_bloc_swaps_frequency'] // 100 print(f"Swapping {bloc_nbr_to_swap} blocs in {region}") while swaps_nbr < bloc_nbr_to_swap: # choose two blocs bloc_1_pos = random.choice(regions_blocs_positions[region]) bloc_2_pos = random.choice(regions_blocs_positions[region]) if bloc_1_pos == bloc_2_pos: continue swaps_nbr += 2 tmp = chunk[bloc_1_pos[0]][bloc_1_pos[1]] chunk[bloc_1_pos[0]][bloc_1_pos[1]] = chunk[bloc_2_pos[0]][bloc_2_pos[1]] chunk[bloc_2_pos[0]][bloc_2_pos[1]] = tmp return chunk
def get_info(self): info = MobInfo() info.entityId = self.entityId info.pos = Pos(*self.pos) info.look = Look(*self.look) info.mobType = self.mobType info.color = self.color info.mobname = self.mobname return info
def test_add_mob(self): # add mob chicken = {v: k for k, v in MOBS_BY_ID.items()}["chicken"] mob_id, mob_type, pos, look = 42, chicken, Pos(3, 4, 5), Look(0.0, 0.0) self.memory.set_mob_position(Mob(mob_id, mob_type, pos, look)) # get mob self.assertIsNotNone(self.memory.get_entity_by_eid(mob_id)) # update mob pos = Pos(6, 7, 8) look = Look(120.0, 50.0) self.memory.set_mob_position(Mob(mob_id, mob_type, pos, look)) # get mob mob_node = self.memory.get_entity_by_eid(mob_id) self.assertIsNotNone(mob_node) self.assertEqual(mob_node.pos, (6, 7, 8), (120.0, 50.0))
def get_player_line_of_sight(self, player_struct): if hasattr(self.world, "get_line_of_sight"): pos = (player_struct.pos.x, player_struct.pos.y, player_struct.pos.z) pitch = player_struct.look.pitch yaw = player_struct.look.yaw xsect = self.world.get_line_of_sight(pos, yaw, pitch) if xsect is not None: return Pos(*xsect) else: raise NotImplementedError()
def generate_capitals_positions(self, capitals_nbr): ''' Choose {capitals_nbr} coordinates to place the capitals Regions will be determined based on those coordinates''' for c in range(capitals_nbr): point_x = random.randint(0, self.config['map_size']) while point_x < 0 or point_x >= self.config['map_size']: point_x = random.randint(0, self.config['map_size']) point_y = random.randint(0, self.config['map_size']) while point_y < 0 or point_y >= self.config['map_size']: point_y = random.randint(0, self.config['map_size']) self.capitals_positions.append(Pos(x=point_x, y=point_y))
def display_entity(self, bloc_type, region, noise_value, pos): """ From the entity type, position and noise value assigned to this position (computed server side) draw a sprite""" sprite_name = f'{region}_{bloc_type}' if sprite_name not in self.sprites.keys(): sprite_name = bloc_type if sprite_name not in self.sprites.keys(): sprite_name = 'default' sprite_idx = int(noise_value * len(self.sprites[sprite_name])) self.display.blit(self.sprites[sprite_name][sprite_idx], ((pos + Pos(1, 1)) * self.tile_size).get_xy())
def draw_map(self): ''' Display the static map, received at the start of the connection ''' x_idx = 0 y_idx = 0 while y_idx < self.map_tiles_nbr.y and y_idx < len(self.client.map): while x_idx < self.map_tiles_nbr.x and x_idx < len( self.client.map[0]): pos = Pos(y=y_idx, x=x_idx) # print(pos) tile_data = self.client.map[y_idx][x_idx] self.display_entity(*tile_data, pos) x_idx += 1 x_idx = 0 y_idx += 1
def setUp(self, agent_opts=None): spec = { "players": [Player(42, "SPEAKER", Pos(5, 63, 5), Look(270, 0), Item(0, 0))], "mobs": [], "item_stacks": [], "ground_generator": flat_ground_generator, "agent": {"pos": (0, 63, 0)}, "coord_shift": (-16, 54, -16), } world_opts = Opt() world_opts.sl = 32 self.world = World(world_opts, spec) self.agent = FakeAgent(self.world, opts=agent_opts) self.set_looking_at((0, 63, 0)) self.speaker = self.agent.get_other_players()[0].name self.agent.perceive()
def __init__(self): # Replica of test environment spec = { "players": [Player(42, "SPEAKER", Pos(5, 63, 5), Look(270, 0), Item(0, 0))], "mobs": [], "ground_generator": flat_ground_generator, "agent": { "pos": (0, 63, 0) }, "coord_shift": (-16, 54, -16), } world_opts = Opt() world_opts.sl = 32 self.world = World(world_opts, spec) self.agent = FakeAgent(self.world, opts=None) self.speaker = "cat"
def test_add_guardian_mob(self): guardian = {v: k for k, v in MOBS_BY_ID.items()}["guardian"] mob_id, mob_type, pos, look = 42, guardian, Pos(3, 4, 5), Look(0.0, 0.0) self.memory.set_mob_position(Mob(mob_id, mob_type, pos, look))
def legal(self, pos, walls: np.ndarray, action) -> bool: direction_vec = Pos.get_pos_vector(action.value) candidate = pos.clone().add(direction_vec) if walls[candidate.x][candidate.y] == 0: return True return False
def set_looking_at(self, xyz: XYZ): """Set the return value for C++ call to get_player_line_of_sight""" self.agent.get_player_line_of_sight = Mock(return_value=Pos(*xyz))
log_formatter = logging.Formatter( "%(asctime)s [%(filename)s:%(lineno)s - %(funcName)s() %(levelname)s]: %(message)s" ) logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().handlers.clear() # set up stdout logging sh = logging.StreamHandler() sh.setLevel(logging.DEBUG) sh.setFormatter(log_formatter) logging.getLogger().addHandler(sh) opts = Opt() opts.sl = 32 spec = { "players": [Player(42, "SPEAKER", Pos(0, 68, 0), Look(270, 80), Item(0, 0))], "mobs": [SimpleMob(make_mob_opts("cow")), SimpleMob(make_mob_opts("chicken"))], "agent": { "pos": (1, 68, 1) }, "coord_shift": (-opts.sl // 2, 63 - opts.sl // 2, -opts.sl // 2), } world = World(opts, spec) agent = FakeAgent(world, opts=None) speaker_name = agent.get_other_players()[0].name move_speaker_pos = { "action_type": "MOVE", "location": { "location_type": "SPEAKER_POS"
def get_player(self): return Player(1, "fake_agent", Pos(*self.pos), Look(0, 0), Item(0, 0))
def setUp(self, agent_opts=None): spec = { "players": [Player(42, "SPEAKER", Pos(5, 63, 5), Look(270, 0), Item(0, 0))], "mobs": [], "ground_generator": flat_ground_generator, "agent": { "pos": (0, 63, 0) }, "coord_shift": (-16, 54, -16), } world_opts = Opt() world_opts.sl = 32 self.world = World(world_opts, spec) self.agent = FakeAgent(self.world, opts=agent_opts) # More helpful error message to encourage test writers to use self.set_looking_at() self.agent.get_player_line_of_sight = Mock( side_effect=NotImplementedError( "Cannot call into C++ function in this unit test. " + "Call self.set_looking_at() to set the return value")) self.speaker = self.agent.get_other_players()[0].name self.agent.perceive() # Combinable actions to be used in test cases self.possible_actions = { "destroy_speaker_look": { "action_type": "DESTROY", "reference_object": { "location": { "location_type": "SPEAKER_LOOK" } }, }, "copy_speaker_look_to_agent_pos": { "action_type": "BUILD", "reference_object": { "location": { "location_type": "SPEAKER_LOOK" } }, "location": { "location_type": "AGENT_POS" }, }, "build_small_sphere": { "action_type": "BUILD", "schematic": { "has_name": "sphere", "has_size": "small" }, }, "build_1x1x1_cube": { "action_type": "BUILD", "schematic": { "has_name": "cube", "has_size": "1 x 1 x 1" }, }, "move_speaker_pos": { "action_type": "MOVE", "location": { "location_type": "SPEAKER_POS" }, }, "build_diamond": { "action_type": "BUILD", "schematic": { "has_name": "diamond" } }, "build_gold_cube": { "action_type": "BUILD", "schematic": { "has_block_type": "gold", "has_name": "cube" }, }, "fill_all_holes_speaker_look": { "action_type": "FILL", "location": { "location_type": "SPEAKER_LOOK" }, "repeat": { "repeat_key": "ALL" }, }, "go_to_tree": { "action_type": "MOVE", "location": { "location_type": "REFERENCE_OBJECT", "reference_object": { "has_name": "tree" }, }, }, "build_square_height_1": { "action_type": "BUILD", "schematic": { "has_name": "square", "has_height": "1" }, }, "stop": { "action_type": "STOP" }, "fill_speaker_look": { "action_type": "FILL", "location": { "location_type": "SPEAKER_LOOK" }, }, "fill_speaker_look_gold": { "action_type": "FILL", "has_block_type": "gold", "location": { "location_type": "SPEAKER_LOOK" }, }, }
def get_pos(s, idx): return Pos(idx // s.width, idx % s.width)
def get_player(self): return Player(1, "fake_agent", Pos(*self.pos), self.get_look(), Item(*self._held_item))
def get_other_players(self): return [Player(42, "SPEAKER", Pos(5, 63, 5), Look(270, 0), Item(0, 0))]
from utils import Pos TRANSPARENT = (0, 0, 0, 0) BLACK = (0, 0, 0) WHITE = (255, 255, 255) GREY = (100, 100, 100) GREEN = (76, 231, 60) RED = (231, 76, 60) DARK_BLUE = (44, 62, 80) LIGHT_BLUE = (52, 152, 219) MEDIUM_BLUE = (41, 128, 185) DISPLAY_CONFIG = { 'target_resolution': Pos(x=1600, y=700), 'hud_width_px': 200, 'tile_size': 16, 'borders_width': 2, } # BORDERS_WIDTH = 2