def _load_schematics(self, load_minecraft_specs=True): if load_minecraft_specs: for premem in minecraft_specs.get_schematics(): npy = premem["schematic"] memid = SchematicNode.create(self, npy_to_blocks_list(npy)) if premem.get("name"): for n in premem["name"]: self.add_triple(subj=memid, pred_text="has_name", obj_text=n) self.add_triple(subj=memid, pred_text="has_tag", obj_text=n) if premem.get("tags"): for t in premem["tags"]: self.add_triple(subj=memid, pred_text="has_tag", obj_text=t) # load single blocks as schematics bid_to_name = minecraft_specs.get_block_data()["bid_to_name"] for (d, m), name in bid_to_name.items(): if d >= 256: continue memid = SchematicNode.create(self, [((0, 0, 0), (d, m))]) self.add_triple(subj=memid, pred_text="has_name", obj_text=name) if "block" in name: self.add_triple(subj=memid, pred_text="has_name", obj_text=name.strip("block").strip()) # tag single blocks with 'block' self.add_triple(subj=memid, pred_text="has_name", obj_text="block")
def handle_no_path(self, agent): delta = self.target - agent.pos for vec, step_fn_name in self.STEP_FNS.items(): if np.dot(delta, vec) > 0: newpos = agent.pos + vec x, y, z = newpos newpos_blocks = agent.get_blocks(x, x, y, y + 1, z, z) # dig if necessary for (bp, idm) in npy_to_blocks_list(newpos_blocks, newpos): self.replace.add((bp, idm)) agent.dig(*bp) # move step_fn = getattr(agent, step_fn_name) step_fn() break
def finish(self, agent): xyz, _ = npy_to_blocks_list(self.schematic, self.origin)[0] memid = agent.memory.get_block_object_ids_by_xyz(xyz)[0] if self.schematic_memid: logging.info( "tag_block_object_from_schematic {} {}".format(memid, self.schematic_memid) ) agent.memory.tag_block_object_from_schematic(memid, self.schematic_memid) if self.schematic_tags: for pred, obj in self.schematic_tags: logging.info("add block_object triple {} {} {}".format(memid, pred, obj)) agent.memory.add_triple(memid, pred, obj) if pred == "has_name": agent.memory.tag(memid, obj) if self.verbose: agent.send_chat("I finished building this") if self.fill_message: agent.send_chat("I finished filling this") self.finished = True
def _load_schematics(self, load_minecraft_specs=True): """Load all Minecraft schematics into agent memory""" if load_minecraft_specs: for premem in minecraft_specs.get_schematics(): npy = premem["schematic"] # lazy loading, only store memid in db, ((0, 0, 0), (0, 0)) as a placeholder memid = SchematicNode.create(self, [((0, 0, 0), (0, 0))]) self.schematics[memid] = npy_to_blocks_list(npy) if premem.get("name"): for n in premem["name"]: self.add_triple(subj=memid, pred_text="has_name", obj_text=n) self.add_triple(subj=memid, pred_text="has_tag", obj_text=n) if premem.get("tags"): for t in premem["tags"]: self.add_triple(subj=memid, pred_text="has_tag", obj_text=t) # load single blocks as schematics bid_to_name = minecraft_specs.get_block_data()["bid_to_name"] for (d, m), name in bid_to_name.items(): if d >= 256: continue memid = SchematicNode.create(self, [((0, 0, 0), (d, m))]) self.add_triple(subj=memid, pred_text="has_name", obj_text=name) if "block" in name: self.add_triple(subj=memid, pred_text="has_name", obj_text=name.strip("block").strip()) # tag single blocks with 'block' self.add_triple(subj=memid, pred_text="has_name", obj_text="block")
def step(self, agent): self.interrupted = False # get blocks occupying build area ox, oy, oz = self.origin sy, sz, sx, _ = self.schematic.shape current = agent.get_blocks(ox, ox + sx - 1, oy, oy + sy - 1, oz, oz + sz - 1) # save state for undo() if self.old_blocks_list is None: self.old_blocks_list = npy_to_blocks_list(current, self.origin) if len(self.old_blocks_list) > 0: self.old_origin = np.min(util.strip_idmeta(self.old_blocks_list), axis=0) # are we done? # TODO: diff ignores block meta right now because placing stairs and # chests in the appropriate orientation is non-trivial diff = ( (current[:, :, :, 0] != self.schematic[:, :, :, 0]) & (self.attempts > 0) & np.isin(current[:, :, :, 0], BUILD_IGNORE_BLOCKS, invert=True) ) if self.embed: diff &= self.schematic[:, :, :, 0] != 0 # don't delete blocks for pair in BUILD_INTERCHANGEABLE_PAIRS: diff &= np.isin(current[:, :, :, 0], pair, invert=True) | np.isin( self.schematic[:, :, :, 0], pair, invert=True ) if not np.any(diff): self.finish(agent) return # blocks that would need to be removed remove_mask = diff & (current[:, :, :, 0] != 0) # destroy any blocks in the way first rel_yzxs = np.argwhere(remove_mask) xyzs = [ (x + self.origin[0], y + self.origin[1], z + self.origin[2]) for (y, z, x) in rel_yzxs ] if xyzs: logging.info("Excavating {} blocks first".format(len(xyzs))) agent.memory.task_stack_push( Destroy(agent, {"schematic": util.fill_idmeta(agent, xyzs)}), parent_memid=self.memid, ) return # get next block to place yzx = self.get_next_target(agent, current, diff) idm = self.schematic[tuple(yzx)] current_idm = current[tuple(yzx)] # try placing block target = yzx[[2, 0, 1]] + self.origin logging.debug("trying to place {} @ {}".format(idm, target)) if tuple(target) in (tuple(agent.pos), tuple(agent.pos + [0, 1, 0])): # can't place block where you're standing, so step out of the way self.step_any_dir(agent) return if util.manhat_dist(agent.pos, target) <= self.PLACE_REACH: # block is within reach assert current_idm[0] != idm[0], "current={} idm={}".format(current_idm, idm) if current_idm[0] != 0: logging.debug( "removing block {} @ {} from {}".format(current_idm, target, agent.pos) ) agent.dig(*target) if idm[0] != 0: agent.set_held_item(idm) logging.debug("placing block {} @ {} from {}".format(idm, target, agent.pos)) x, y, z = target if agent.place_block(x, y, z): B = agent.get_blocks(x, x, y, y, z, z) if B[0, 0, 0, 0] == idm[0]: agent.memory.pending_agent_placed_blocks.add((x, y, z)) else: logging.error( "failed to place block {} @ {}, but place_block returned True. \ Got {} instead.".format( idm, target, B[0, 0, 0, :] ) ) self.new_blocks.append(((x, y, z), idm)) else: logging.warn("failed to place block {} from {}".format(target, agent.pos)) self.attempts[tuple(yzx)] -= 1 if self.attempts[tuple(yzx)] == 0 and not self.giving_up_message_sent: agent.send_chat( "I'm skipping a block because I can't place it. Maybe something is in the way." ) self.giving_up_message_sent = True else: # too far to place; move first task = Move(agent, {"target": target, "approx": self.PLACE_REACH}, self.featurizer) agent.memory.task_stack_push(task, parent_memid=self.memid)
def __init__(self, agent, task_data): super(Build, self).__init__() self.task_data = task_data self.embed = task_data.get("embed", False) self.schematic, _ = blocks_list_to_npy(task_data["blocks_list"]) self.origin = task_data["origin"] self.verbose = task_data.get("verbose", True) self.relations = task_data.get("relations", []) self.default_behavior = task_data.get("default_behavior") self.force = task_data.get("force", False) self.attempts = 3 * np.ones(self.schematic.shape[:3], dtype=np.uint8) self.fill_message = task_data.get("fill_message", False) self.schematic_memid = task_data.get("schematic_memid", None) self.schematic_tags = task_data.get("schematic_tags", []) self.giving_up_message_sent = False self.wait = False self.old_blocks_list = None self.old_origin = None self.PLACE_REACH = task_data.get("PLACE_REACH", 3) # negative schematic related self.is_destroy_schm = task_data.get("is_destroy_schm", False) self.dig_message = task_data.get("dig_message", False) self.blockobj_memid = None self.DIG_REACH = task_data.get("DIG_REACH", 3) self.last_stepped_time = agent.memory.get_time() if self.is_destroy_schm: # is it destroying a whole block object? if so, save its tags self.destroyed_block_object_triples = [] xyzs = set(strip_idmeta(task_data["blocks_list"])) mem = agent.memory.get_block_object_by_xyz(next(iter(xyzs))) # TODO what if there are several objects being destroyed? if mem and all(xyz in xyzs for xyz in mem.blocks.keys()): for pred in ["has_tag", "has_name", "has_colour"]: self.destroyed_block_object_triples.extend( agent.memory.get_triples(subj=mem.memid, pred_text=pred)) logging.info("Destroying block object {} tags={}".format( mem.memid, self.destroyed_block_object_triples)) # modify the schematic to avoid placing certain blocks for bad, good in BUILD_BLOCK_REPLACE_MAP.items(): self.schematic[self.schematic[:, :, :, 0] == bad] = good self.new_blocks = [] # a list of (xyz, idm) of newly placed blocks # snap origin to ground if bottom level has dirt blocks # NOTE(kavyasrinet): except for when we are rebuilding the old dirt blocks, we # don't want to change the origin then, hence the self.force check. if not self.force and not self.embed and np.isin( self.schematic[:, :, :, 0], (2, 3)).any(): h = ground_height(agent, self.origin, 0) self.origin[1] = h[0, 0] # get blocks occupying build area and save state for undo() ox, oy, oz = self.origin sy, sz, sx, _ = self.schematic.shape current = agent.get_blocks(ox, ox + sx - 1, oy, oy + sy - 1, oz, oz + sz - 1) self.old_blocks_list = npy_to_blocks_list(current, self.origin) if len(self.old_blocks_list) > 0: self.old_origin = np.min(strip_idmeta(self.old_blocks_list), axis=0)