Exemple #1
0
def require_proximity(exact=None, at_least=None, at_most=None, fn=WRAPPED, f_args=F_ARGS, f_kwargs=F_KWARGS):
    self, action = f_kwargs['self'], f_kwargs['action']
    sub_loc = self.simulation_manager.grid_model.get_loc_of_obj(action.actor)

    if action.target_id is None:
        action.status = ActionStatus.FAILED
        action.reason = "No entity is targeted for proximity"
        return
    else:
        tar_loc = self.simulation_manager.grid_model.get_loc_of_obj(action.target_id)
        dist = cubic_manhattan(sub_loc, tar_loc)

        if exact is not None and dist != exact:
            action.status = ActionStatus.FAILED
            action.reason = "Target proximity was not exactly equal to %d, value: %d" % (exact, dist)
            return

        if at_least is not None and dist <= at_least:
            action.status = ActionStatus.FAILED
            action.reason = "Target proximity was not at least equal to %d, value: %d" % (at_least, dist)
            return

        if at_most is not None and dist >= at_most:
            action.status = ActionStatus.FAILED
            action.reason = "Target proximity was not at most equal to %d, value: %d" % (at_most, dist)
            return

        return fn(*f_args, **f_kwargs)
Exemple #2
0
    def move(self, entity_id, vec: np.ndarray):
        """
        Shorthand for remove-insert with a vector.
        """
        # Skip empty moves.
        if (vec == np.array([0, 0, 0])).all():
            return

        curr_chunk = self._obj_chunk.get(entity_id)
        dest_chunk_id = curr_chunk.chunk_id
        new_offset = curr_chunk.offset_vec + vec

        # Determine the destination chunk
        if self.chunk_radius < cubic_manhattan(curr_chunk.chunk_vec,
                                               new_offset):
            # Compute offset and chunk from absolute.
            absolute = curr_chunk.chunk_vec + new_offset
            dest_chunk_id = self.get_chunk_of_loc(
                absolute, hint_chunk=curr_chunk.chunk_vec)
            dest_chunk_vec = self._buf2vec(dest_chunk_id)
            new_offset = absolute - dest_chunk_vec

        # Remove the entity from the original location.
        self.remove(entity_id)

        # Add it to the new location.
        self.insert_chunked(dest_chunk_id, new_offset, entity_id)
Exemple #3
0
    def _find_chunk_of_location(self,
                                absolute_position,
                                starting_chunk: np.ndarray = None):
        # Default start at the origin (inefficient).
        neighbor_count = 6
        starting_chunk = np.array(
            [0, 0, 0]) if starting_chunk is None else starting_chunk
        starting_distance = cubic_manhattan(starting_chunk, absolute_position)
        destination_repeat = np.repeat([absolute_position],
                                       neighbor_count,
                                       axis=0)

        cur_min = [(starting_chunk, starting_distance)]
        destination_chunk = None
        # TODO: move chunk searching to chunk class? or split since this may be needed for finding unloaded chunks
        # Also maybe use dot product to determine direction

        while len(cur_min) != 0:
            chunk_center, remaining_dist = cur_min.pop(0)
            neighbors_matrix = self.neighbor_chunk_vec + np.repeat(
                [chunk_center], neighbor_count, axis=0)
            neighbor_distances = list(
                cubic_manhattan(neighbors_matrix, destination_repeat, axis=1))
            neighbors_and_dist = list(
                zip(list(neighbors_matrix), neighbor_distances))
            closer_neighbors = list(
                filter(lambda nd: nd[1] < remaining_dist, neighbors_and_dist))

            # If no neighbors increase the distance, then the current chunk is the closest.
            if len(closer_neighbors) == 0:
                destination_chunk = self._vec2buf(chunk_center)
                break

            min_dist = min(closer_neighbors, key=lambda x: x[1])
            cur_min.append(min_dist)

        return destination_chunk
Exemple #4
0
    def get_entities_in_radius_chunked(self, chunk_id, offset, radius):
        # TODO: make this more performant, and not calculate entities clearly too far away (using chunk sizes)
        # Determine radius-to-chunk-radius ratio, and grab surrounding chunks of relevance.
        # Use the chunk list to filter the _chunk dictionary, then calculate distances.
        center_chunk = self._buf2vec(chunk_id)
        entity_chunks = list(
            map(lambda e_cid: (e_cid[0], e_cid[1].absolute),
                self._obj_chunk.items()))
        entities, chunk_locs = map(np.array, zip(*entity_chunks))
        center_repeated = np.repeat([center_chunk],
                                    repeats=len(chunk_locs),
                                    axis=0)
        entity_distances = list(
            cubic_manhattan(chunk_locs, center_repeated, axis=1))

        # Ignore chunks over a certain distance (no possibility of any parts being in the radius).
        entity_distances = list(zip(entities, entity_distances, chunk_locs))
        return list(
            filter(lambda ed: ed[1] + self.chunk_radius <= radius,
                   entity_distances))
Exemple #5
0
    def get_locations_of_path(self,
                              path: np.ndarray,
                              starting_chunk: np.ndarray = None):
        if starting_chunk is None:
            # determine starting chunk. Worst case performance
            pass

        locations = []
        chunk_id = self._vec2buf(starting_chunk)
        current_chunk = self.chunks.get(chunk_id)
        direction_list = [j - i for i, j in zip(path[:-1], path[1:])]
        offset_vec_neighbors = current_chunk.offset_vec_neighbors
        center, offset = starting_chunk, path[0] - starting_chunk
        locations.append(self.at_chunked(chunk_id, offset))

        # TODO: not as performant as possible. Maybe numba-fy this function
        for direction in direction_list:
            # Calculate new offset
            offset = offset + direction
            dist = cubic_manhattan(np.array([0, 0, 0]), offset)
            if dist > self.chunk_radius:
                # Determine which chunk comes from moving in a particular direction.
                offset_key = self._vec2buf(offset)
                chunk_vec, new_offset, neighbor_idx = Chunk.neighbor_by_offset.get(
                    offset_key)
                # Translate the new center and set the new offset.
                center = center + chunk_vec
                offset = new_offset
                chunk_id = self._vec2buf(center)
                current_chunk = current_chunk.neighbors[neighbor_idx]
                if current_chunk is None:
                    current_chunk = self.load_chunk_v(chunk_id)

            locations.append(self.at_chunked(chunk_id, offset))

        return locations
Exemple #6
0
    def resolve(self, action: ObservationAction):
        subject_id = action.actor
        target_id = action.target_id
        existing_obsvs = self.simulation_manager.observation_manager.get_observations(
            subject_id)
        s_center, s_offset = self.simulation_manager.grid_model.get_chunk_offset(
            subject_id)
        s_chunk = s_center.tobytes()

        # Determine which type of observation is being done, Passive or Direct.
        if target_id is not None:
            # TODO: incomplete
            # Direct observation, improve LocationObservation that already exists.
            center = self.simulation_manager.grid_model.get_loc_of_obj(
                subject_id)
            target_loc = self.simulation_manager.grid_model.get_loc_of_obj(
                target_id)
            distance = cubic_manhattan(center, target_loc)

            existing_loc_obsv = existing_obsvs.get(target_id,
                                                   LocationObservation)[0]
            existing_noise = existing_loc_obsv.noise if existing_loc_obsv is not None else None

            noise, result = self._get_noise_for(subject_id, target_id,
                                                distance, 0.5)
            if noise is None:
                action.status = ActionStatus.RESOLVED
                return

            # If there was a critical failure, require an update if any observations exist.
            require_overwrite = False
            if result == RollResult.Critical_Failure:
                require_overwrite = True

            if existing_noise is not None and (require_overwrite
                                               or noise < existing_noise):
                # If an existing observation exists, and there is a reason to overwrite it.
                existing_obsvs.remove_all(target_id, LocationObservation)
            elif existing_noise is not None and noise >= existing_noise:
                # If there was an existing entry, but no reason to update it, return.
                action.status = ActionStatus.RESOLVED
                return

            loc_obsv = LocationObservation(center=center,
                                           noise=noise,
                                           subject_id=subject_id,
                                           target_id=target_id)
            self.simulation_manager.observation_manager.add_observation(
                loc_obsv)
        else:
            entity_distances = self.simulation_manager.grid_model.get_entities_in_radius_chunked(
                s_center, s_offset, 10)
            center = self.simulation_manager.grid_model.get_location(
                subject_id)

            for tid, d, a_loc in entity_distances:
                if tid == subject_id:
                    continue

                # Roll for perception and get the noisiness of the observation.
                noise, roll_result = self._get_noise_for(subject_id, tid, d)
                if noise is None:
                    continue

                t_center, t_offset = self.simulation_manager.grid_model.get_chunk_offset(
                    tid)
                t_absolute = t_center + t_offset
                t_chunk = t_center.tobytes()

                # Check the cache for a path
                key1, key2 = center.tobytes() + t_absolute.tobytes(
                ), t_absolute.tobytes() + center.tobytes()
                if key1 in self.path_cache:
                    path_to_t = self.path_cache[key1]
                elif key2 in self.path_cache:
                    path_to_t = self.path_cache[key2]
                else:
                    # Cast a ray and find the MAX height and associated index along the path
                    path_to_t = cast_hex_ray(center, t_absolute)
                    self.path_cache[key1] = path_to_t
                    self.path_cache[key2] = np.flip(path_to_t, axis=0)

                # Get all the locations
                locations = self.simulation_manager.grid_model.get_locations_of_path(
                    path_to_t, s_center)

                # Ignore ends because they wont affect visibility.
                inter_path = locations[1:-1]

                if len(inter_path) > 0:
                    # TODO: When SM is implemented
                    sm_s, sm_t = 1, 1
                    starting_elev = locations[0].get_elevation() + sm_s * 2
                    ending_elev = locations[-1].get_elevation() + sm_t * 2
                    slope = (ending_elev - starting_elev) / d

                    index, location = max(enumerate(inter_path),
                                          key=lambda n: n[1].get_elevation())

                    if location.get_elevation(
                    ) >= starting_elev + (index + 1) * slope:
                        continue

                    # TODO: if desired, have the percentage of the target visible determine noise instead of distance.

                # If an observation already exists, update it.
                if tid in existing_obsvs.keys():
                    existing_obsvs.remove_all(tid, LocationObservation)
                loc_obsv = LocationObservation(center=a_loc,
                                               noise=noise,
                                               subject_id=subject_id,
                                               target_id=tid)
                self.simulation_manager.observation_manager.add_observation(
                    loc_obsv)

        # TODO: manage movement challenges (terrain difficulty, walls, etc)
        # TODO: in order for this to work, the move resolver must know how much speed it remaining for a given turn.
        #   this also go for other actions, they must know the game state so they can determine whether or not to modify
        #   the game state.

        action.status = ActionStatus.RESOLVED