def interpret_named_schematic(
        interpreter, speaker,
        d) -> Tuple[List[Block], Optional[str], List[Tuple[str, str]]]:
    """Return a tuple of 3 values:
    - the schematic blocks, list[(xyz, idm)]
    - a SchematicNode memid, or None
    - a list of (pred, val) tags
    """
    if "has_name" not in d:
        raise ErrorWithResponse("I don't know what you want me to build.")
    name = d["has_name"]
    stemmed_name = stemmer.stemWord(name)
    shapename = SPECIAL_SHAPES_CANONICALIZE.get(
        name) or SPECIAL_SHAPES_CANONICALIZE.get(stemmed_name)
    if shapename:
        blocks, tags = interpret_shape_schematic(speaker,
                                                 d,
                                                 shapename=shapename)
        return blocks, None, tags

    schematic = interpreter.memory.get_schematic_by_name(name)
    if schematic is None:
        schematic = interpreter.memory.get_schematic_by_name(stemmed_name)
        if schematic is None:
            raise ErrorWithResponse("I don't know what you want me to build.")
    tags = [(p, v)
            for (_, p,
                 v) in interpreter.memory.get_triples(subj=schematic.memid)]
    return list(schematic.blocks.items()), schematic.memid, tags
Example #2
0
def maybe_get_location_memory(interpreter, speaker, d):
    location_type = d.get("location_type", "SPEAKER_LOOK")
    if location_type == "REFERENCE_OBJECT" or d.get(
            "reference_object") is not None:
        if d.get("relative_direction") == "BETWEEN":
            if d.get("reference_object_1"):
                mem1 = interpret_reference_object(interpreter,
                                                  speaker,
                                                  d["reference_object_1"],
                                                  loose_speakerlook=True)[0]
                mem2 = interpret_reference_object(interpreter,
                                                  speaker,
                                                  d["reference_object_2"],
                                                  loose_speakerlook=True)[0]
                mems = [mem1, mem2]
            else:
                mems = interpret_reference_object(interpreter,
                                                  speaker,
                                                  d["reference_object"],
                                                  limit=2,
                                                  loose_speakerlook=True)
                if len(mems) < 2:
                    mem1 = None
                else:
                    mem1, mem2 = mems
            if not mem1:
                # TODO specify the ref object in the error message
                raise ErrorWithResponse(
                    "I don't know what you're referring to")
            loc = (np.add(mem1.get_pos(), mem2.get_pos())) / 2
            loc = (loc[0], loc[1], loc[2])
        else:
            mems = interpret_reference_object(interpreter, speaker,
                                              d["reference_object"])
            if len(mems) == 0:
                tags = set(tags_from_dict(d["reference_object"]))
                cands = interpreter.memory.get_recent_entities("Mobs")
                mems = [
                    c for c in cands
                    if any(set.intersection(set(c.get_tags()), tags))
                ]
                if len(mems) == 0:
                    cands = interpreter.memory.get_recent_entities(
                        "BlockObjects")
                    mems = [
                        c for c in cands
                        if any(set.intersection(set(c.get_tags()), tags))
                    ]
                    if len(mems) == 0:
                        raise ErrorWithResponse(
                            "I don't know what you're referring to")
            assert len(mems) == 1, mems
            interpreter.memory.update_recent_entities(mems)
            mem = mems[0]
            loc = mem.get_pos()
            mems = [mem]
        return loc, mems
    return None, None
Example #3
0
    def __init__(
        self,
        agent,
        ref_object=None,
        relative_direction="CLOCKWISE",  # this is the memory of the object
    ):
        self.agent = agent
        self.tick = 0
        blocks = [(bpos, bid) for bpos, bid in ref_object.blocks.items()]
        bounds = shapes.get_bounds(blocks)
        center = np.mean([b[0] for b in blocks], axis=0)

        d = max(bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4])
        if relative_direction == "CLOCKWISE":
            offsets = shapes.arrange(
                "circle", schematic=None, shapeparams={"encircled_object_radius": d}
            )
        elif relative_direction == "ANTICLOCKWISE":
            offsets = shapes.arrange(
                "circle", schematic=None, shapeparams={"encircled_object_radius": d}
            )
            offsets = offsets[::-1]
        else:
            raise NotImplementedError("TODO other kinds of paths")
        self.path = [np.round(center + o) for o in offsets]
        self.path.append(self.path[0])

        # check each offset to find a nearby reachable point, see if a path
        # is possible now, and error otherwise

        for i in range(len(self.path) - 1):
            path = search.astar(agent, self.path[i + 1], approx=2, pos=self.path[i])
            if path is None:
                raise ErrorWithResponse("I cannot find an appropriate path.")
Example #4
0
def interpret_facing(interpreter, speaker, d):
    current_pitch = interpreter.agent.get_player().look.pitch
    current_yaw = interpreter.agent.get_player().look.yaw
    if d.get("yaw_pitch"):
        span = d["yaw_pitch"]
        # for now assumed in (yaw, pitch) or yaw, pitch or yaw pitch formats
        yp = span.replace("(", "").replace(")", "").split()
        return {"head_yaw_pitch": (int(yp[0]), int(yp[1]))}
    elif d.get("yaw"):
        # for now assumed span is yaw as word or number
        w = d["yaw"].strip(" degrees").strip(" degree")
        return {"head_yaw_pitch": (word_to_num(w), current_pitch)}
    elif d.get("pitch"):
        # for now assumed span is pitch as word or number
        w = d["pitch"].strip(" degrees").strip(" degree")
        return {"head_yaw_pitch": (current_yaw, word_to_num(w))}
    elif d.get("relative_yaw"):
        # TODO in the task use turn angle
        if d["relative_yaw"].get("angle"):
            return {"relative_yaw": int(d["relative_yaw"]["angle"])}
        else:
            pass
    elif d.get("relative_pitch"):
        if d["relative_pitch"].get("angle"):
            # TODO in the task make this relative!
            return {"relative_pitch": int(d["relative_pitch"]["angle"])}
        else:
            pass
    elif d.get("location"):
        loc, _ = interpret_location(interpreter, speaker, d["location"])
        return {"head_xyz": loc}
    else:
        raise ErrorWithResponse("I am not sure where you want me to turn")
Example #5
0
def interpret_point_target(interpreter, speaker, d):
    if d.get("location") is None:
        # TODO other facings
        raise ErrorWithResponse("I am not sure where you want me to point")
    loc, mem = interpret_location(interpreter, speaker, d["location"])
    if mem is not None:
        return mem.get_point_at_target()
    else:
        return (loc[0], loc[1] + 1, loc[2], loc[0], loc[1] + 1, loc[2])
Example #6
0
def interpret_named_schematic(
        interpreter, speaker,
        d) -> Tuple[List[Block], Optional[str], List[Tuple[str, str]]]:
    """Return a tuple of 3 values:
    - the schematic blocks, list[(xyz, idm)]
    - a SchematicNode memid, or None
    - a list of (pred, val) tags
    """
    if "has_name" not in d:
        raise ErrorWithResponse("I don't know what you want me to build.")
    name = d["has_name"]
    stemmed_name = name
    shapename = SPECIAL_SHAPES_CANONICALIZE.get(
        name) or SPECIAL_SHAPES_CANONICALIZE.get(stemmed_name)
    if shapename:
        shape_blocks, tags = interpret_shape_schematic(interpreter,
                                                       speaker,
                                                       d,
                                                       shapename=shapename)
        return shape_blocks, None, tags

    schematic = interpreter.memory.get_schematic_by_name(name)
    if schematic is None:
        schematic = interpreter.memory.get_schematic_by_name(stemmed_name)
        if schematic is None:
            raise ErrorWithResponse("I don't know what you want me to build.")
    tags = [(p, v)
            for (_, p,
                 v) in interpreter.memory.get_triples(subj=schematic.memid)]
    blocks = schematic.blocks
    # TODO generalize to more general block properties
    # Longer term: remove and put a call to the modify model here
    if d.get("has_colour"):
        old_idm = most_common_idm(blocks.values())
        c = block_data.COLOR_BID_MAP.get(d["has_colour"])
        if c is not None:
            new_idm = random.choice(c)
            for l in blocks:
                if blocks[l] == old_idm:
                    blocks[l] = new_idm
    return list(blocks.items()), schematic.memid, tags
Example #7
0
    def __init__(
            self,
            agent,
            ref_object=None,
            relative_direction="CLOCKWISE",  # this is the memory of the object
    ):
        self.agent = agent
        self.tick = 0
        if ref_object is None or ref_object == "AGENT_POS":
            x, y, z = agent.pos
            bounds = (x, x, y, y, z, z)
            center = (x, y, z)
        else:
            bounds = ref_object.get_bounds()
            center = ref_object.get_pos()
        d = max(bounds[1] - bounds[0], bounds[3] - bounds[2],
                bounds[5] - bounds[4])
        if relative_direction == "CLOCKWISE":
            offsets = shapes.arrange(
                "circle",
                schematic=None,
                shapeparams={"encircled_object_radius": d})
        elif relative_direction == "ANTICLOCKWISE":
            offsets = shapes.arrange(
                "circle",
                schematic=None,
                shapeparams={"encircled_object_radius": d})
            offsets = offsets[::-1]
        else:
            raise NotImplementedError("TODO other kinds of paths")
        self.path = [np.round(np.add(center, o)) for o in offsets]
        self.path.append(self.path[0])

        # check each offset to find a nearby reachable point, see if a path
        # is possible now, and error otherwise

        for i in range(len(self.path) - 1):
            path = search.astar(agent,
                                self.path[i + 1],
                                approx=2,
                                pos=self.path[i])
            if path is None:
                raise ErrorWithResponse("I cannot find an appropriate path.")
def interpret_reference_object(
        interpreter,
        speaker,
        d,
        ignore_mobs=False,
        limit=1,
        loose_speakerlook=False) -> List[ReferenceObjectNode]:

    if d.get("coref_resolve", "NULL") != "NULL":
        mem = d["coref_resolve"]
        if isinstance(mem, ReferenceObjectNode):
            return [mem]
        else:
            logging.error("bad coref_resolve -> {}".format(mem))

    if len(interpreter.progeny_data) == 0:
        tags = [
            stemmer.stemWord(tag.lstrip("the ")) for key, tag in d.items()
            if key.startswith("has_") and isinstance(tag, str)
        ]
        candidates = (get_reference_objects(interpreter, *tags)
                      if not ignore_mobs else get_objects(interpreter, *tags))
        if len(candidates) > 0:
            location_d = d.get("location", {"location_type": "SPEAKER_LOOK"})
            if limit == 1:
                # override with input value
                limit = get_repeat_num(d)
            r = filter_by_sublocation(interpreter,
                                      speaker,
                                      candidates,
                                      location_d,
                                      limit=limit,
                                      loose=loose_speakerlook)
            return [mem for _, mem in r]
        else:
            # no candidates found; ask Clarification
            # TODO: move ttad call to dialogue manager and remove this logic
            interpreter.action_dict_frozen = True

            player = interpreter.memory.get_player_struct_by_name(speaker)
            confirm_candidates = get_objects(interpreter)  # no tags
            objects = object_looked_at(interpreter.agent,
                                       confirm_candidates,
                                       player,
                                       limit=1)
            if len(objects) == 0:
                raise ErrorWithResponse(
                    "I don't know what you're referring to")
            _, mem = objects[0]
            blocks = list(mem.blocks.keys())
            interpreter.provisional["object_mem"] = mem
            interpreter.provisional["object"] = blocks
            interpreter.provisional["d"] = d
            interpreter.dialogue_stack.append_new(ConfirmReferenceObject,
                                                  blocks)
            raise NextDialogueStep()

    else:
        # clarification answered
        r = interpreter.progeny_data[-1].get("response")
        if r == "yes":
            # TODO: learn from the tag!  put it in memory!
            return [interpreter.provisional.get("object_mem")] * limit
        else:
            # TODO: error handling here ?
            return []
def filter_by_sublocation(
    interpreter,
    speaker,
    candidates: List[Tuple[XYZ, T]],
    location: Dict,
    limit=1,
    all_proximity=10,
    loose=False,
) -> List[Tuple[XYZ, T]]:
    """Select from a list of candidate (xyz, object) tuples given a sublocation

    If limit == 'ALL', return all matching candidates

    Returns a list of (xyz, mem) tuples
    """

    # handle SPEAKER_LOOK separately due to slightly different semantics
    # (proximity to ray instead of point)
    if location.get("location_type") == "SPEAKER_LOOK":
        player = interpreter.memory.get_player_struct_by_name(speaker)
        return object_looked_at(interpreter.agent,
                                candidates,
                                player,
                                limit=limit,
                                loose=loose)

    reldir = location.get("relative_direction")
    if reldir:
        if reldir == "INSIDE":
            if location.get("reference_object"):
                # this is ugly, should probably return from interpret_location...
                ref_mems = interpret_reference_object(
                    interpreter, speaker, location["reference_object"])
                for l, candidate_mem in candidates:
                    if perception.check_inside([candidate_mem, ref_mems[0]]):
                        return [(l, candidate_mem)]
            raise ErrorWithResponse("I can't find something inside that")
        elif reldir == "AWAY":
            raise ErrorWithResponse("I don't know which object you mean")
        elif reldir == "NEAR":
            pass  # fall back to no reference direction
        else:
            # reference object location, i.e. the "X" in "left of X"
            ref_loc = interpret_location(interpreter,
                                         speaker,
                                         location,
                                         ignore_reldir=True)
            # relative direction, i.e. the "LEFT" in "left of X"
            reldir_vec = rotation.DIRECTIONS[reldir]

            # transform each object into the speaker look coordinate system,
            # and project onto the reldir vector
            look = interpreter.memory.get_player_struct_by_name(speaker).look
            proj = [
                rotation.transform(np.array(l) - ref_loc, look.yaw, 0)
                @ reldir_vec for (l, _) in candidates
            ]

            # filter by relative dir, e.g. "left of Y"
            proj_cands = [(p, c) for (p, c) in zip(proj, candidates) if p > 0]

            # "the X left of Y" = the right-most X that is left of Y
            if limit == "ALL":
                limit = len(proj_cands)
            return [c for (_, c) in sorted(proj_cands, key=lambda p: p[0])
                    ][:limit]
    else:
        # no reference direction: choose the closest
        if limit == "ALL":
            return list(
                filter(lambda c: euclid_dist(c[0], ref_loc) <= all_proximity,
                       candidates))
        else:
            candidates.sort(key=lambda c: euclid_dist(c[0], ref_loc))
            return candidates[:limit]
    return []  # this fixes flake but seems awful?
def interpret_location(interpreter, speaker, d, ignore_reldir=False) -> XYZ:
    """Location dict -> coordinates
    Side effect:  adds mems to agent_memory.recent_entities
    if a reference object is interpreted;
    and loc to memory
    """
    location_type = d.get("location_type", "SPEAKER_LOOK")
    if location_type == "REFERENCE_OBJECT":
        mems = interpret_reference_object(interpreter, speaker,
                                          d["reference_object"])
        if len(mems) == 0:
            raise ErrorWithResponse("I don't know what you're referring to")
        assert len(mems) == 1, mems
        interpreter.memory.update_recent_entities(mems)
        mem = mems[0]
        loc = mem.get_pos()

    elif location_type == "SPEAKER_LOOK":
        player = interpreter.memory.get_player_struct_by_name(speaker)
        loc = capped_line_of_sight(interpreter.agent, player)

    elif location_type == "SPEAKER_POS":
        loc = pos_to_np(
            interpreter.memory.get_player_struct_by_name(speaker).pos)

    elif location_type == "AGENT_POS":
        loc = pos_to_np(interpreter.agent.get_player().pos)

    elif location_type == "COORDINATES":
        loc = cast(
            XYZ,
            tuple(
                int(float(w))
                for w in re.findall("[-0-9.]+", d["coordinates"])))
        if len(loc) != 3:
            logging.error("Bad coordinates: {}".format(d["coordinates"]))
            raise ErrorWithResponse(
                "I don't understand what location you're referring to")

    else:
        raise ValueError(
            "Can't handle Location type: {}".format(location_type))

    # handle relative direction
    reldir = d.get("relative_direction")
    if reldir is not None and not ignore_reldir:
        if reldir == "INSIDE":
            if location_type == "REFERENCE_OBJECT":
                locs = perception.find_inside(mem)
                if len(locs) == 0:
                    raise ErrorWithResponse(
                        "I don't know how to go inside there")
                else:
                    loc = locs[0]
        elif reldir == "AWAY":
            apos = pos_to_np(interpreter.agent.get_player().pos)
            dir_vec = (apos - loc) / np.linalg.norm(apos - loc)
            num_steps = word_to_num(d.get("steps", "5"))
            loc = num_steps * np.array(dir_vec) + to_block_center(loc)
        elif reldir == "NEAR":
            pass
        else:  # LEFT, RIGHT, etc...
            reldir_vec = rotation.DIRECTIONS[reldir]
            look = interpreter.memory.get_player_struct_by_name(speaker).look
            # this should be an inverse transform so we set inverted=True
            dir_vec = rotation.transform(reldir_vec,
                                         look.yaw,
                                         0,
                                         inverted=True)
            num_steps = word_to_num(d.get("steps", "5"))
            loc = num_steps * np.array(dir_vec) + to_block_center(loc)

    # if steps without relative direction
    elif "steps" in d:
        num_steps = word_to_num(d.get("steps", "5"))
        loc = to_block_center(loc) + [0, 0, num_steps]
    return to_block_pos(loc)