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) item_stack_set = set() for item_stack in self.agent.get_item_stacks(): item_stack_set.add(item_stack.entityId) if (euclid_dist(self.agent.pos, pos_to_np(item_stack.pos)) < self.memory.perception_range): self.memory.set_item_stack_position(item_stack) old_item_stacks = self.memory.get_all_item_stacks() if old_item_stacks: for old_item_stack in old_item_stacks: memid = old_item_stack[0] eid = old_item_stack[1] if eid not in item_stack_set: self.memory.untag(memid, "_on_ground") else: self.memory.tag(memid, "_on_ground") # 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_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_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_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, 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: interesting = True agent_placed = True for player_struct in self.agent.get_other_players(): if (euclid_dist(pos_to_np(player_struct.pos), xyz) < 5 and player_struct.mainHand.id == bid): interesting = True if not agent_placed: 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 [to_block_pos(m)] l = get_locs_from_entity(entity) if l is None: return [] 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: 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 = 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 = 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.vstack(bl)) else: bounding_locs.append(np.array(l)) x = np.mean(bounding_locs[0], axis=0) points = np.vstack([bounding_locs[1], bounding_locs[2]]) return in_hull(points, x)
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)
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 filter_by_sublocation( interpreter, speaker, candidates: List[Tuple[XYZ, T]], d: 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 """ F = d.get("filters") assert F is not None, "no filters".format(d) location = F.get("location", SPEAKERLOOK) if limit == 1: limit = get_repeat_num(d) # handle SPEAKER_LOOK separately due to slightly different semantics # (proximity to ray instead of point) if location.get("location_type") == "SPEAKER_LOOK": player_struct = interpreter.agent.perception_modules[ "low_level"].get_player_struct_by_name(speaker) return object_looked_at(interpreter.agent, candidates, player_struct, 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_reference_location... ref_mems = interpret_reference_object( interpreter, speaker, location["reference_object"]) for l, candidate_mem in candidates: if heuristic_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 elif reldir == "BETWEEN": mems = interpret_reference_location(interpreter, speaker, location) steps, reldir = interpret_relative_direction(interpreter, d) ref_loc, _ = compute_locations(interpreter, speaker, mems, steps, reldir) candidates.sort(key=lambda c: euclid_dist(c[0], ref_loc)) return candidates[:limit] else: # reference object location, i.e. the "X" in "left of X" mems = interpret_reference_location(interpreter, speaker, location) ref_loc = mems[0].get_pos() # 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.agent.perception_modules["low_level"]. 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: # is it even possible to end up in this branch? FIXME? # no reference direction: choose the closest mems = interpret_reference_location(interpreter, speaker, location) steps, reldir = interpret_relative_direction(interpreter, d) ref_loc, _ = compute_locations(interpreter, speaker, mems, steps, reldir) 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?