Esempio n. 1
0
    def step(self, agent):

        mx, My, mz = self.origin
        Mx = mx + (self.width - 1)
        my = My - (self.depth - 1)
        Mz = mz + (self.length - 1)

        blocks = agent.get_blocks(mx, Mx, my, My, mz, Mz)

        # if top row is above ground, make sure you are digging into the ground
        if np.isin(blocks[-1, :, :, 0], PASSABLE_BLOCKS).all():
            my -= 1

        poss = [
            (x, y, z)
            for x in range(mx, Mx + 1)
            for y in range(my, My + 1)
            for z in range(mz, Mz + 1)
        ]
        schematic = util.fill_idmeta(agent, poss)
        self.destroy_task = Destroy(agent, {"schematic": schematic, "dig_message": True})
        agent.memory.task_stack_push(self.destroy_task, parent_memid=self.memid)

        self.finished = True
Esempio n. 2
0
def get_all_nearby_holes(agent, location, radius=15):
    """Return a list of holes. Each hole is tuple(list[xyz], idm)"""
    sx, sy, sz = location
    max_height = sy + 5
    map_size = radius * 2 + 1
    height_map = [[sz] * map_size for i in range(map_size)]
    hid_map = [[-1] * map_size for i in range(map_size)]
    idm_map = [[(0, 0)] * map_size for i in range(map_size)]
    visited = set([])
    global current_connected_comp, current_idm
    current_connected_comp = []
    current_idm = (2, 0)

    # helper functions
    def get_block_info(x, z):  # fudge factor 5
        height = max_height
        while True:
            B = agent.get_blocks(x, x, height, height, z, z)
            if ((B[0, 0, 0, 0] != 0) and (x != sx or z != sz or height != sy)
                    and (x != agent.pos[0] or z != agent.pos[2]
                         or height != agent.pos[1]) and
                (B[0, 0, 0, 0] !=
                 383)):  # if it's not a mobile block (agent, speaker, mobs)
                return height, tuple(B[0, 0, 0])
            height -= 1

    gx = [0, 0, -1, 1]
    gz = [1, -1, 0, 0]

    def dfs(x, y, z):
        """ Traverse current connected component and return minimum
                height of all surrounding blocks """
        build_height = 100000
        if (x, y, z) in visited:
            return build_height
        global current_connected_comp, current_idm
        current_connected_comp.append(
            (x - radius + sx, y, z - radius + sz))  # absolute positions
        visited.add((x, y, z))
        for d in range(4):
            nx = x + gx[d]
            nz = z + gz[d]
            if nx >= 0 and nz >= 0 and nx < map_size and nz < map_size:
                if height_map[x][z] == height_map[nx][nz]:
                    build_height = min(build_height, dfs(nx, y, nz))
                else:
                    build_height = min(build_height, height_map[nx][nz])
                    current_idm = idm_map[nx][nz]
            else:
                # bad ... hole is not within defined radius
                return -100000
        return build_height

    # find all holes
    blocks_queue = []
    for i in range(map_size):
        for j in range(map_size):
            height_map[i][j], idm_map[i][j] = get_block_info(
                i - radius + sx, j - radius + sz)
            heapq.heappush(blocks_queue, (height_map[i][j] + 1,
                                          (i, height_map[i][j] + 1, j)))
    holes = []
    while len(blocks_queue) > 0:
        hxyz = heapq.heappop(blocks_queue)
        h, (x, y, z) = hxyz  # NB: relative positions
        if (x, y, z) in visited or y > max_height:
            continue
        assert h == height_map[x][z] + 1, " h=%d heightmap=%d, x,z=%d,%d" % (
            h,
            height_map[x][z],
            x,
            z,
        )  # sanity check
        current_connected_comp = []
        current_idm = (2, 0)
        build_height = dfs(x, y, z)
        if build_height >= h:
            holes.append((current_connected_comp.copy(), current_idm))
            cur_hid = len(holes) - 1
            for n, xyz in enumerate(current_connected_comp):
                x, y, z = xyz
                rx, ry, rz = x - sx + radius, y + 1, z - sz + radius
                heapq.heappush(blocks_queue, (ry, (rx, ry, rz)))
                height_map[rx][rz] += 1
                if hid_map[rx][rz] != -1:
                    holes[cur_hid][0].extend(holes[hid_map[rx][rz]][0])
                    holes[hid_map[rx][rz]] = ([], (0, 0))
                hid_map[rx][rz] = cur_hid

    # A bug in the algorithm above produces holes that include non-air blocks.
    # Just patch the problem here, since this function will eventually be
    # performed by an ML model
    for i, (xyzs, idm) in enumerate(holes):
        blocks = util.fill_idmeta(agent, xyzs)
        xyzs = [xyz for xyz, (d, _) in blocks
                if d == 0]  # remove non-air blocks
        holes[i] = (xyzs, idm)

    # remove 0-length holes
    holes = [h for h in holes if len(h[0]) > 0]

    return holes
Esempio n. 3
0
    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)