def get_holes(interpreter, speaker, location, limit=1, all_proximity=10) -> List[Tuple[XYZ, Hole]]: holes: List[Hole] = perception.get_all_nearby_holes( interpreter.agent, location) candidates: List[Tuple[XYZ, Hole]] = [ (to_block_pos(np.mean(hole[0], axis=0)), hole) for hole in holes ] if len(candidates) > 0: # NB(demiguo): by default, we fill the hole the player is looking at player = interpreter.memory.get_player_struct_by_name(speaker) centroid_hole = object_looked_at(interpreter.agent, candidates, player, limit=limit) if centroid_hole is None or len(centroid_hole) == 0: # NB(demiguo): if there's no hole in front of the player, we will fill the nearest hole speaker_pos = interpreter.memory.get_player_struct_by_name( speaker).pos speaker_pos = to_block_pos(pos_to_np(speaker_pos)) if limit == "ALL": return list( filter( lambda c: euclid_dist(c[0], speaker_pos) <= all_proximity, candidates)) else: candidates.sort(key=lambda c: euclid_dist(c[0], speaker_pos)) return candidates[:limit] else: return centroid_hole else: return []
def test_action_sequence_order(self): target1 = (3, 63, 2) target2 = (7, 63, 7) d = { "dialogue_type": "HUMAN_GIVE_COMMAND", "action_sequence": [ { "action_type": "MOVE", "location": { "location_type": "COORDINATES", "coordinates": str(target1) }, }, { "action_type": "MOVE", "location": { "location_type": "COORDINATES", "coordinates": str(target2) }, }, ], } self.handle_logical_form(d) self.assertLessEqual(euclid_dist(self.agent.pos, target2), 1)
def test_come_here(self): chat = "come here" self.add_incoming_chat(chat, self.speaker) self.flush() self.assertLessEqual( euclid_dist(self.agent.pos, self.get_speaker_pos()), 1)
def test_stop(self): # start moving target = (20, 63, 20) d = { "dialogue_type": "HUMAN_GIVE_COMMAND", "action": { "action_type": "MOVE", "location": { "location_type": "COORDINATES", "coordinates": str(target) }, }, } self.handle_action_dict(d, max_steps=5) # stop d = { "dialogue_type": "HUMAN_GIVE_COMMAND", "action": { "action_type": "STOP" } } self.handle_action_dict(d) # assert that move did not complete self.assertGreater(euclid_dist(self.agent.pos, target), 1)
def test_come_here(self): chat = "come here" self.add_incoming_chat(chat) self.dialogue_manager.step((self.speaker, chat)) self.flush() self.assertLessEqual( euclid_dist(self.agent.pos, self.get_speaker_pos()), 1)
def test_build_sphere_move_here(self): d = COMBINED_COMMANDS["build a small sphere then move here"] changes = self.handle_logical_form(d) # check that a small object was built self.assertGreater(len(changes), 0) self.assertLess(len(changes), 30) # check that agent moved self.assertLessEqual(euclid_dist(self.agent.pos, self.get_speaker_pos()), 1)
def test_move_here(self): d = { "dialogue_type": "HUMAN_GIVE_COMMAND", "action": self.possible_actions["move_speaker_pos"], } self.handle_logical_form(d) # check that agent moved self.assertLessEqual( euclid_dist(self.agent.pos, self.get_speaker_pos()), 1)
def perceive(self, force=False): # FIXME (low pri) remove these in code, get from sql self.agent.pos = to_block_pos(pos_to_np(self.agent.get_player().pos)) if self.agent.count % self.perceive_freq == 0 or force: for mob in self.agent.get_mobs(): if euclid_dist(self.agent.pos, pos_to_np( mob.pos)) < self.memory.perception_range: self.memory.set_mob_position(mob) for item_stack in self.agent.get_item_stacks(): if (euclid_dist(self.agent.pos, pos_to_np(item_stack.pos)) < self.memory.perception_range): self.memory.set_item_stack_position(item_stack) # note: no "force"; these run on every perceive call. assumed to be fast self.update_self_memory() self.update_other_players(self.agent.get_other_players()) # use safe_get_changed_blocks to deal with pointing for (xyz, idm) in self.agent.safe_get_changed_blocks(): self.on_block_changed(xyz, idm)
def test_stop(self): # start moving target = (20, 63, 20) d = MOVE_COMMANDS["move to 20 63 20"] self.handle_logical_form(d, max_steps=5) # stop d = OTHER_COMMANDS["stop"] self.handle_logical_form(d) # assert that move did not complete self.assertGreater(euclid_dist(self.agent.pos, target), 1)
def _is_placed_block_interesting(self, xyz: XYZ, bid: int) -> Tuple[bool, bool]: """Return two values: - bool: is the placed block interesting? - bool: is it interesting because it was placed by a player? """ if xyz in self.pending_agent_placed_blocks: self.pending_agent_placed_blocks.remove(xyz) return True, True for player in self.other_players.values(): if util.euclid_dist(util.pos_to_np(player.pos), xyz) < 5 and player.mainHand.id == bid: return True, True if bid not in BORING_BLOCKS: return True, False return False, False
def test_move_here(self): d = { "dialogue_type": "HUMAN_GIVE_COMMAND", "action": { "action_type": "MOVE", "location": { "location_type": "SPEAKER_POS" } }, } self.handle_action_dict(d) # check that agent moved self.assertLessEqual( euclid_dist(self.agent.pos, self.get_speaker_pos()), 1)
def test_move_coordinates(self): d = { "dialogue_type": "HUMAN_GIVE_COMMAND", "action": { "action_type": "MOVE", "location": { "location_type": "COORDINATES", "coordinates": "-7 63 -8" }, }, } self.handle_action_dict(d) # check that agent moved self.assertLessEqual(euclid_dist(self.agent.pos, (-7, 63, -8)), 1)
def find_inside(entity): """Return a point inside the entity if it can find one. TODO: heuristic quick check to find that there aren't any, and maybe make this not d^3""" l = util.get_locs_from_entity(entity) if l is None: return None m = np.round(np.mean(l, axis=0)) maxes = np.max(l, axis=0) mins = np.min(l, axis=0) inside = [] for x in range(mins[0], maxes[0] + 1): for y in range(mins[1], maxes[1] + 1): for z in range(mins[2], maxes[2] + 1): if check_inside([(x, y, z), entity]): inside.append((x, y, z)) return sorted(inside, key=lambda x: util.euclid_dist(x, m))
def test_build_sphere_move_here(self): d = { "dialogue_type": "HUMAN_GIVE_COMMAND", "action_sequence": [ self.possible_actions["build_small_sphere"], self.possible_actions["move_speaker_pos"], ], } changes = self.handle_logical_form(d) # check that a small object was built self.assertGreater(len(changes), 0) self.assertLess(len(changes), 30) # check that agent moved self.assertLessEqual( euclid_dist(self.agent.pos, self.get_speaker_pos()), 1)
def _is_placed_block_interesting(self, xyz: XYZ, bid: int) -> Tuple[bool, bool, bool]: """Return three values: - bool: is the placed block interesting? - bool: is it interesting because it was placed by a player? - bool: is it interesting because it was placed by the agent? """ interesting = False player_placed = False agent_placed = False # TODO record *which* player placed it if xyz in self.pending_agent_placed_blocks: self.pending_agent_placed_blocks.remove(xyz) interesting = True agent_placed = True for player in self.other_players.values(): if util.euclid_dist(util.pos_to_np(player.pos), xyz) < 5 and player.mainHand.id == bid: interesting = True player_placed = True if bid not in BORING_BLOCKS: interesting = True return interesting, player_placed, agent_placed
def find_inside(entity): """Return a point inside the entity if it can find one. TODO: heuristic quick check to find that there aren't any, and maybe make this not d^3""" # is this a negative object? if yes, just return its mean: if hasattr(entity, "blocks"): if all(b == (0, 0) for b in entity.blocks.values()): m = np.mean(list(entity.blocks.keys()), axis=0) return [util.to_block_pos(m)] l = util.get_locs_from_entity(entity) if l is None: return None m = np.round(np.mean(l, axis=0)) maxes = np.max(l, axis=0) mins = np.min(l, axis=0) inside = [] for x in range(mins[0], maxes[0] + 1): for y in range(mins[1], maxes[1] + 1): for z in range(mins[2], maxes[2] + 1): if check_inside([(x, y, z), entity]): inside.append((x, y, z)) return sorted(inside, key=lambda x: util.euclid_dist(x, m))
def check_between(entities, fat_scale=0.2): """ Heuristic check if entities[0] is between entities[1] and entities[2] by checking if the locs of enitity[0] are in the convex hull of union of the max cardinal points of entity[1] and entity[2]""" locs = [] means = [] for e in entities: l = util.get_locs_from_entity(e) if l is not None: locs.append(l) means.append(np.mean(l, axis=0)) else: # this is not a thing we know how to assign 'between' to return False mean_separation = util.euclid_dist(means[1], means[2]) fat = fat_scale * mean_separation bounding_locs = [] for l in locs: if len(l) > 1: bl = [] idx = np.argmax(l, axis=0) for i in range(3): f = np.zeros(3) f[i] = fat bl.append(np.array(l[idx[i]]) + fat) idx = np.argmin(l, axis=0) for i in range(3): f = np.zeros(3) f[i] = fat bl.append(np.array(l[idx[i]]) - fat) bounding_locs.append(np.concatenate(bl)) else: bounding_locs.append(np.array(l)) x = np.mean(bounding_locs[0], axis=0) points = np.concatenate(bounding_locs[1], bounding_locs[2]) return in_hull(points, x)
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 test_move_here(self): d = MOVE_COMMANDS["move here"] self.handle_logical_form(d) # check that agent moved self.assertLessEqual(euclid_dist(self.agent.pos, self.get_speaker_pos()), 1)
def test_move_coordinates(self): d = MOVE_COMMANDS["move to -7 63 -8"] self.handle_logical_form(d) # check that agent moved self.assertLessEqual(euclid_dist(self.agent.pos, (-7, 63, -8)), 1)
def test_action_sequence_order(self): d = COMBINED_COMMANDS["move to 3 63 2 then 7 63 7"] self.handle_logical_form(d) self.assertLessEqual(euclid_dist(self.agent.pos, (7, 63, 7)), 1)