def test_block_objects_methods(self): self.memory = MCAgentMemory() bo_memid = BlockObjectNode.create(self.memory, [((1, 1, 1), (1, 2)), ((2, 2, 2), (2, 3))]) # Test get_object_by_id assert (self.memory.get_mem_by_id(bo_memid).blocks == self.memory.get_object_by_id(bo_memid).blocks) # Test get_object_info_by_xyz byt cheking with BlockObject's mean xyz assert self.memory.get_object_info_by_xyz( (1, 1, 1), "BlockObjects")[0] == bo_memid # Test get_block_object_ids_by_xyz assert (self.memory.get_block_object_ids_by_xyz( (1, 1, 1))[0] == self.memory.get_object_info_by_xyz( (1, 1, 1), "BlockObjects")[0]) assert self.memory.get_block_object_ids_by_xyz( (1, 1, 1))[0] == bo_memid # Test get_block_object_by_xyz -> same BlockObjectNode object as what we created before assert (self.memory.get_block_object_by_xyz( (1, 1, 1)).blocks == self.memory.get_mem_by_id(bo_memid).blocks) # Test get_block_object_by_id assert (self.memory.get_block_object_by_id(bo_memid).blocks == self.memory.get_mem_by_id(bo_memid).blocks) # Test tag_block_object_from_schematic sch_memid = SchematicNode.create(self.memory, (((0, 0, 1), (1, 0)), ((0, 0, 2), (1, 0)), ((0, 0, 3), (2, 0)))) bo1_memid = BlockObjectNode.create(self.memory, (((0, 0, 2), (1, 0)), ((0, 0, 3), (2, 0)))) self.memory.tag_block_object_from_schematic(bo1_memid, sch_memid) assert len(self.memory.get_triples(pred_text="_from_schematic")) == 1
def test_dance_api(self): self.memory = MCAgentMemory() def x(): return 2 self.memory.add_dance(x, "generate_num_dance", ["generate_2", "dance_with_numbers"]) assert len(self.memory.get_triples(obj_text="generate_2")) == 1 assert len(self.memory.get_triples(obj_text="dance_with_numbers")) == 1
def test_get_entity_by_id(self): self.memory = MCAgentMemory() mob_memid = MobNode.create(self.memory, Mob(10, 65, Pos(1, 2, 3), Look(0, 0))) player_memid = PlayerNode.create( self.memory, Player(20, "xyz", Pos(1, 1, 1), Look(1, 1))) # Test get_entity_by_eid with entityId assert self.memory.get_entity_by_eid(10).pos == (1.0, 2.0, 3.0) assert self.memory.get_entity_by_eid(20).pos == (1.0, 1.0, 1.0)
class MethodsTests(unittest.TestCase): def setUp(self): self.memory = MCAgentMemory(load_minecraft_specs=False) # Add dances to memory dance.add_default_dances(self.memory) def test_peek_empty(self): self.assertEqual(self.memory.task_stack_peek(), None) 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 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 test_voxel_apis(self): self.memory = MCAgentMemory() bo_memid = BlockObjectNode.create(self.memory, [((1, 1, 1), (1, 2)), ((2, 2, 2), (2, 3))]) assert len(self.memory.get_mem_by_id(bo_memid).blocks) == 2 # Test remove_voxel and test total number of blocks self.memory.remove_voxel(1, 1, 1, "BlockObjects") assert len(self.memory.get_mem_by_id(bo_memid).blocks) == 1 # Test upsert_block (add a new block and assert that now there are two self.memory.upsert_block(((3, 3, 3), (1, 1)), bo_memid, "BlockObjects") assert len(self.memory.get_mem_by_id(bo_memid).blocks) == 2
def test_inst_seg_node(self): self.memory = MCAgentMemory() inst_seg_memid = InstSegNode.create(self.memory, [(1, 0, 34), (1, 0, 35), (2, 0, 34), (3, 0, 34)], ["shiny", "bright"]) # Test get_instseg_object_ids_by_xyz assert self.memory.get_instseg_object_ids_by_xyz( (1, 0, 34))[0][0] == inst_seg_memid assert self.memory.get_instseg_object_ids_by_xyz( (2, 0, 34))[0][0] == inst_seg_memid assert self.memory.get_instseg_object_ids_by_xyz( (3, 0, 34))[0][0] == inst_seg_memid
def init_memory(self): T = FakeMCTime(self.world) low_level_data = self.low_level_data.copy() low_level_data["check_inside"] = heuristic_perception.check_inside self.memory = MCAgentMemory( load_minecraft_specs=False, coordinate_transforms=self.coordinate_transforms, agent_time=T, agent_low_level_data=low_level_data, ) # Add dances to memory dance.add_default_dances(self.memory)
def test_items_apis(self): self.memory = MCAgentMemory() low_level_data = {"bid_to_name": {(0, 0): "sand", (10, 4): "grass"}} item_memid = ItemStackNode.create( self.memory, ItemStack(12, Item(0, 0), Pos(0, 0, 0)), low_level_data) # test update_item_stack_eid assert self.memory.update_item_stack_eid(item_memid, 23).eid == 23 # test set_item_stack_position new_item = ItemStack(23, Item(0, 0), Pos(2, 2, 2)) self.memory.set_item_stack_position(new_item) assert self.memory.get_mem_by_id(item_memid).pos == (2.0, 2.0, 2.0) # test get_all_item_stacks item_2_memid = ItemStackNode.create( self.memory, ItemStack(34, Item(10, 4), Pos(2, 3, 3)), low_level_data) assert len(self.memory.get_all_item_stacks()) == 2
def test_schematic_apis(self): self.memory = MCAgentMemory() schematic_memid = SchematicNode.create(self.memory, (((2, 0, 1), (1, 0)), ((2, 0, 2), (1, 0)), ((2, 0, 3), (2, 0)))) # test get_schematic_by_id assert (self.memory.get_schematic_by_id(schematic_memid).blocks == self.memory.get_mem_by_id(schematic_memid).blocks) # test get_schematic_by_name self.memory.add_triple(subj=schematic_memid, pred_text="has_name", obj_text="house") assert (self.memory.get_schematic_by_name("house").blocks == self.memory.get_mem_by_id(schematic_memid).blocks) bo1_memid = BlockObjectNode.create(self.memory, (((0, 0, 2), (1, 0)), ((0, 0, 3), (2, 0)))) # Test convert_block_object_to_schematic assert self.memory.convert_block_object_to_schematic( bo1_memid).NODE_TYPE == "Schematic"
class BasicTest(unittest.TestCase): def test_get_entity_by_id(self): self.memory = MCAgentMemory() mob_memid = MobNode.create(self.memory, Mob(10, 65, Pos(1, 2, 3), Look(0, 0))) player_memid = PlayerNode.create( self.memory, Player(20, "xyz", Pos(1, 1, 1), Look(1, 1))) # Test get_entity_by_eid with entityId assert self.memory.get_entity_by_eid(10).pos == (1.0, 2.0, 3.0) assert self.memory.get_entity_by_eid(20).pos == (1.0, 1.0, 1.0) def test_voxel_apis(self): self.memory = MCAgentMemory() bo_memid = BlockObjectNode.create(self.memory, [((1, 1, 1), (1, 2)), ((2, 2, 2), (2, 3))]) assert len(self.memory.get_mem_by_id(bo_memid).blocks) == 2 # Test remove_voxel and test total number of blocks self.memory.remove_voxel(1, 1, 1, "BlockObjects") assert len(self.memory.get_mem_by_id(bo_memid).blocks) == 1 # Test upsert_block (add a new block and assert that now there are two self.memory.upsert_block(((3, 3, 3), (1, 1)), bo_memid, "BlockObjects") assert len(self.memory.get_mem_by_id(bo_memid).blocks) == 2 def test_block_objects_methods(self): self.memory = MCAgentMemory() bo_memid = BlockObjectNode.create(self.memory, [((1, 1, 1), (1, 2)), ((2, 2, 2), (2, 3))]) # Test get_object_by_id assert (self.memory.get_mem_by_id(bo_memid).blocks == self.memory.get_object_by_id(bo_memid).blocks) # Test get_object_info_by_xyz byt cheking with BlockObject's mean xyz assert self.memory.get_object_info_by_xyz( (1, 1, 1), "BlockObjects")[0] == bo_memid # Test get_block_object_ids_by_xyz assert (self.memory.get_block_object_ids_by_xyz( (1, 1, 1))[0] == self.memory.get_object_info_by_xyz( (1, 1, 1), "BlockObjects")[0]) assert self.memory.get_block_object_ids_by_xyz( (1, 1, 1))[0] == bo_memid # Test get_block_object_by_xyz -> same BlockObjectNode object as what we created before assert (self.memory.get_block_object_by_xyz( (1, 1, 1)).blocks == self.memory.get_mem_by_id(bo_memid).blocks) # Test get_block_object_by_id assert (self.memory.get_block_object_by_id(bo_memid).blocks == self.memory.get_mem_by_id(bo_memid).blocks) # Test tag_block_object_from_schematic sch_memid = SchematicNode.create(self.memory, (((0, 0, 1), (1, 0)), ((0, 0, 2), (1, 0)), ((0, 0, 3), (2, 0)))) bo1_memid = BlockObjectNode.create(self.memory, (((0, 0, 2), (1, 0)), ((0, 0, 3), (2, 0)))) self.memory.tag_block_object_from_schematic(bo1_memid, sch_memid) assert len(self.memory.get_triples(pred_text="_from_schematic")) == 1 def test_inst_seg_node(self): self.memory = MCAgentMemory() inst_seg_memid = InstSegNode.create(self.memory, [(1, 0, 34), (1, 0, 35), (2, 0, 34), (3, 0, 34)], ["shiny", "bright"]) # Test get_instseg_object_ids_by_xyz assert self.memory.get_instseg_object_ids_by_xyz( (1, 0, 34))[0][0] == inst_seg_memid assert self.memory.get_instseg_object_ids_by_xyz( (2, 0, 34))[0][0] == inst_seg_memid assert self.memory.get_instseg_object_ids_by_xyz( (3, 0, 34))[0][0] == inst_seg_memid def test_schematic_apis(self): self.memory = MCAgentMemory() schematic_memid = SchematicNode.create(self.memory, (((2, 0, 1), (1, 0)), ((2, 0, 2), (1, 0)), ((2, 0, 3), (2, 0)))) # test get_schematic_by_id assert (self.memory.get_schematic_by_id(schematic_memid).blocks == self.memory.get_mem_by_id(schematic_memid).blocks) # test get_schematic_by_name self.memory.add_triple(subj=schematic_memid, pred_text="has_name", obj_text="house") assert (self.memory.get_schematic_by_name("house").blocks == self.memory.get_mem_by_id(schematic_memid).blocks) bo1_memid = BlockObjectNode.create(self.memory, (((0, 0, 2), (1, 0)), ((0, 0, 3), (2, 0)))) # Test convert_block_object_to_schematic assert self.memory.convert_block_object_to_schematic( bo1_memid).NODE_TYPE == "Schematic" def test_set_mob_position(self): self.memory = MCAgentMemory() m = Mob(10, 65, Pos(1, 2, 3), Look(0, 0)) assert self.memory.set_mob_position(m).pos == (1.0, 2.0, 3.0) def test_items_apis(self): self.memory = MCAgentMemory() low_level_data = {"bid_to_name": {(0, 0): "sand", (10, 4): "grass"}} item_memid = ItemStackNode.create( self.memory, ItemStack(12, Item(0, 0), Pos(0, 0, 0)), low_level_data) # test update_item_stack_eid assert self.memory.update_item_stack_eid(item_memid, 23).eid == 23 # test set_item_stack_position new_item = ItemStack(23, Item(0, 0), Pos(2, 2, 2)) self.memory.set_item_stack_position(new_item) assert self.memory.get_mem_by_id(item_memid).pos == (2.0, 2.0, 2.0) # test get_all_item_stacks item_2_memid = ItemStackNode.create( self.memory, ItemStack(34, Item(10, 4), Pos(2, 3, 3)), low_level_data) assert len(self.memory.get_all_item_stacks()) == 2 def test_dance_api(self): self.memory = MCAgentMemory() def x(): return 2 self.memory.add_dance(x, "generate_num_dance", ["generate_2", "dance_with_numbers"]) assert len(self.memory.get_triples(obj_text="generate_2")) == 1 assert len(self.memory.get_triples(obj_text="dance_with_numbers")) == 1
def test_set_mob_position(self): self.memory = MCAgentMemory() m = Mob(10, 65, Pos(1, 2, 3), Look(0, 0)) assert self.memory.set_mob_position(m).pos == (1.0, 2.0, 3.0)
def setUp(self): self.memory = MCAgentMemory(load_minecraft_specs=False) # Add dances to memory dance.add_default_dances(self.memory)
class FakeAgent(DroidletAgent): CCW_LOOK_VECS = [(1, 0), (0, 1), (-1, 0), (0, -1)] default_frame = CraftAssistAgent.default_frame coordinate_transforms = CraftAssistAgent.coordinate_transforms def __init__(self, world, opts=None, do_heuristic_perception=False, prebuilt_perception=None): self.mark_airtouching_blocks = do_heuristic_perception self.head_height = HEAD_HEIGHT self.world = world self.chat_count = 0 # use these to not have to re-init models if running many tests: self.prebuilt_perception = prebuilt_perception if not opts: opts = MockOpt() self.e2e_mode = getattr(opts, "e2e_mode", False) self.low_level_data = { "mobs": SPAWN_OBJECTS, "mob_property_data": craftassist_specs.get_mob_property_data(), "schematics": craftassist_specs.get_schematics(), "block_data": craftassist_specs.get_block_data(), "block_property_data": craftassist_specs.get_block_property_data(), "color_data": craftassist_specs.get_colour_data(), "boring_blocks": BORING_BLOCKS, "passable_blocks": PASSABLE_BLOCKS, "fill_idmeta": fill_idmeta, "color_bid_map": COLOR_BID_MAP, } super(FakeAgent, self).__init__(opts) self.do_heuristic_perception = do_heuristic_perception self.no_default_behavior = True self.last_task_memid = None pos = (0, 63, 0) if hasattr(self.world, "agent_data"): pos = self.world.agent_data["pos"] self.pos = np.array(pos, dtype="int") self.logical_form = None self.world_interaction_occurred = False self.world_interaction_occurred = False self._held_item: IDM = (0, 0) self._look_vec = (1, 0, 0) self._changed_blocks: List[Block] = [] self._outgoing_chats: List[str] = [] CraftAssistAgent.add_self_memory_node(self) def set_look_vec(self, x, y, z): l = np.array((x, y, z)) l = l / np.linalg.norm(l) self._look_vec = (l[0], l[1], l[2]) self.look = self.get_look() def init_perception(self): self.perception_modules = {} if self.prebuilt_perception: for k, v in self.prebuilt_perception: self.perception_modules[k] = v # make a method to do this.... self.perception_modules[k].agent = self else: self.perception_modules["language_understanding"] = NSPQuerier(self.opts, self) self.perception_modules["low_level"] = LowLevelMCPerception(self, perceive_freq=1) self.perception_modules["heuristic"] = PerceptionWrapper( self, low_level_data=self.low_level_data, mark_airtouching_blocks=self.mark_airtouching_blocks, ) def init_physical_interfaces(self): init_agent_interfaces(self) def init_memory(self): T = FakeMCTime(self.world) low_level_data = self.low_level_data.copy() low_level_data["check_inside"] = heuristic_perception.check_inside self.memory = MCAgentMemory( load_minecraft_specs=False, coordinate_transforms=self.coordinate_transforms, agent_time=T, agent_low_level_data=low_level_data, ) # Add dances to memory dance.add_default_dances(self.memory) def init_controller(self): dialogue_object_classes = {} dialogue_object_classes["bot_capabilities"] = {"task": MCBotCapabilities, "data": {}} dialogue_object_classes["interpreter"] = MCInterpreter dialogue_object_classes["get_memory"] = MCGetMemoryHandler dialogue_object_classes["put_memory"] = PutMemoryHandler low_level_interpreter_data = { "block_data": craftassist_specs.get_block_data(), "special_shape_functions": SPECIAL_SHAPE_FNS, "color_bid_map": self.low_level_data["color_bid_map"], "get_all_holes_fn": heuristic_perception.get_all_nearby_holes, "get_locs_from_entity": get_locs_from_entity, } self.dialogue_manager = DialogueManager( self.memory, dialogue_object_classes, self.opts, low_level_interpreter_data=low_level_interpreter_data, ) def set_logical_form(self, lf, chatstr, speaker): self.logical_form = {"logical_form": lf, "chatstr": chatstr, "speaker": speaker} def step(self): if hasattr(self.world, "step"): if self.world_interaction_occurred or self.count % WORLD_STEP == 0: self.world.step() self.world_interaction_occurred = False if hasattr(self, "recorder"): self.recorder.record_world() super().step() def perceive(self, force=False): if self.e2e_mode: perception_output = self.perception_modules["low_level"].perceive(force=force) self.areas_to_perceive = self.memory.update(perception_output, self.areas_to_perceive)[ "areas_to_perceive" ] super().perceive(force=force) if "semseg" in self.perception_modules: sem_seg_perception_output = self.perception_modules["semseg"].perceive() self.memory.update(sem_seg_perception_output) return # clear the chat buffer self.get_incoming_chats() if self.logical_form: # use the logical form as given... DroidletAgent.process_language_perception( self, self.logical_form["speaker"], self.logical_form["chatstr"], self.logical_form["chatstr"], self.logical_form["logical_form"], ) force = True perception_output = self.perception_modules["low_level"].perceive(force=force) self.areas_to_perceive = self.memory.update(perception_output, self.areas_to_perceive)[ "areas_to_perceive" ] if self.do_heuristic_perception: if force or not self.agent.memory.task_stack_peek(): # perceive from heuristic perception module heuristic_perception_output = self.perception_modules["heuristic"].perceive() self.memory.update(heuristic_perception_output) def controller_step(self): CraftAssistAgent.controller_step(self) # if the logical form was set explicitly, clear it, so that it won't keep # being perceived and used to respawn new interpreters self.logical_form = None def setup_test(self): self.task_steps_count = 0 def clear_outgoing_chats(self): self._outgoing_chats.clear() def get_last_outgoing_chat(self): try: return self._outgoing_chats[-1] except IndexError: return None def task_step(self): CraftAssistAgent.task_step(self, sleep_time=0) def point_at(*args): pass def get_blocks(self, xa, xb, ya, yb, za, zb): return self.world.get_blocks(xa, xb, ya, yb, za, zb) def get_local_blocks(self, r): x, y, z = self.pos return self.get_blocks(x - r, x + r, y - r, y + r, z - r, z + r) def get_incoming_chats(self): c = self.chat_count self.chat_count = len(self.world.chat_log) return self.world.chat_log[c:].copy() def get_player(self): return Player(1, "fake_agent", Pos(*self.pos), self.get_look(), Item(*self._held_item)) def get_mobs(self): return self.world.get_mobs() def get_item_stacks(self): return self.world.get_item_stacks() def get_other_players(self): return self.world.get_players() def get_other_player_by_name(self): raise NotImplementedError() def get_vision(self): raise NotImplementedError() def get_line_of_sight(self): raise NotImplementedError() def get_look(self): yaw, pitch = yaw_pitch(self._look_vec) return Look(yaw, pitch) 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 + HEAD_HEIGHT, 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 get_changed_blocks(self) -> List[Block]: # need a better solution here r = self._changed_blocks.copy() self._changed_blocks.clear() return r def safe_get_changed_blocks(self) -> List[Block]: return self.get_changed_blocks() ###################################### ## World setup ###################################### def set_blocks(self, xyzbms: List[Block], boring_blocks: Tuple[int], origin: XYZ = (0, 0, 0)): """Change the state of the world, block by block, store in memory""" changes_to_be_updated = CraftAssistPerceptionData(changed_block_attributes={}) for xyz, idm in xyzbms: abs_xyz = tuple(np.array(xyz) + origin) self.perception_modules["low_level"].pending_agent_placed_blocks.add(abs_xyz) # TODO add force option so we don't need to make it as if agent placed interesting, player_placed, agent_placed = self.perception_modules[ "low_level" ].mark_blocks_with_env_change(xyz, idm, boring_blocks) changes_to_be_updated.changed_block_attributes[(abs_xyz, idm)] = [ interesting, player_placed, agent_placed, ] self.world.place_block((abs_xyz, idm)) # TODO: to be named to normal update function self.memory.update(changes_to_be_updated, self.areas_to_perceive) def add_object( self, xyzbms: List[Block], origin: XYZ = (0, 0, 0), relations={} ) -> VoxelObjectNode: """Add an object to memory as if it was placed block by block Args: - xyzbms: a list of relative (xyz, idm) - origin: (x, y, z) of the corner Returns an VoxelObjectNode """ boring_blocks = self.low_level_data["boring_blocks"] self.set_blocks(xyzbms, boring_blocks, origin) abs_xyz = tuple(np.array(xyzbms[0][0]) + origin) memid = self.memory.get_block_object_ids_by_xyz(abs_xyz)[0] for pred, obj in relations.items(): self.memory.add_triple(subj=memid, pred_text=pred, obj_text=obj) # sooooorrry FIXME? when we handle triples better in interpreter_helper if "has_" in pred: self.memory.tag(memid, obj) return self.memory.get_object_by_id(memid) # WARNING!! this does not step the world, but directly fast-forwards # to count. Use only in world setup, once world is running! def add_object_ff_time( self, count, xyzbms: List[Block], origin: XYZ = (0, 0, 0), relations={} ) -> VoxelObjectNode: assert count >= self.world.count self.world.set_count(count) return self.add_object(xyzbms, origin, relations=relations) ###################################### ## visualization ###################################### def draw_slice(self, h=None, r=5, c=None): if not h: h = self.pos[1] if c: c = [c[0], h, c[1]] else: c = [self.pos[0], h, self.pos[2]] C = self.world.to_world_coords(c) A = self.world.to_world_coords(self.pos) shifted_agent_pos = [A[0] - C[0] + r, A[2] - C[2] + r] npy = self.world.get_blocks( c[0] - r, c[0] + r, c[1], c[1], c[2] - r, c[2] + r, transpose=False ) npy = npy[:, 0, :, 0] try: npy[shifted_agent_pos[0], shifted_agent_pos[1]] = 1024 except: pass mobnums = {"rabbit": -1, "cow": -2, "pig": -3, "chicken": -4, "sheep": -5} nummobs = {-1: "rabbit", -2: "cow", -3: "pig", -4: "chicken", -5: "sheep"} for mob in self.world.mobs: # todo only in the plane? p = np.round(np.array(self.world.to_world_coords(mob.pos))) p = p - C try: npy[p[0] + r, p[1] + r] = mobnums[mob.mobname] except: pass mapslice = "" height = npy.shape[0] width = npy.shape[1] def xs(x): return x + int(self.pos[0]) - r def zs(z): return z + int(self.pos[2]) - r mapslice = mapslice + " " * (width + 2) * 3 + "\n" for i in reversed(range(height)): mapslice = mapslice + str(xs(i)).center(3) for j in range(width): if npy[i, j] > 0: if npy[i, j] == 1024: mapslice = mapslice + " A " else: mapslice = mapslice + str(npy[i, j]).center(3) elif npy[i, j] == 0: mapslice = mapslice + " * " else: npy[i, j] = mapslice + " " + nummobs[npy[i, j]][0] + " " mapslice = mapslice + "\n" mapslice = mapslice + " " for j in range(width): mapslice = mapslice + " * " mapslice = mapslice + "\n" mapslice = mapslice + " " for j in range(width): mapslice = mapslice + str(zs(j)).center(3) return mapslice