def do(self, world: World, elapsed: float, location: Location, position: Position, ant: ModularAnt): try: # Get a list of ALL possible directions to move in. # Compile list into a list of pairs, where the first entry is a direction, and the second a weight. directions: Directions = Directions(world, position) # Bias the direction in terms of the origin position. if self.brood_position is not None: directions.bias(brood_direction_func(self.hold_chance, self.brood_position)) # Bias the direction weights, by pheromone level (with respect to the bias amount). directions.bias(pheromone_bias_func(age_bias(self.pheromone_bias, self.age))) # Bias by brood pheromones, encouraging ants to adopt a nurse role. directions.bias(brood_pheromone_bias_func(self.age)) # Make a random, weighted selection of these direction. self.facing = directions.get_random_direction() # Apply a wobble at random. d: Direction = self.facing.get_wobble(random.choice(array([0, -1, 1]), p=array([1 - self.wobble_chance, self.wobble_chance/2, self.wobble_chance/2]))) to: Location = world.get_location(position.x + d.get()[0], position.y + d.get()[1]) world.move(location, to) except IndexError: # The ant can't move... pass
def __populate_with_objects(world: World, objects_file: str) -> World: if objects_file is None or objects_file == "": return world with open(objects_file) as file: lines: [str] = file.readlines() for line in lines: name, x, y = line.split()[0], int(line.split()[1]), int( line.split()[2]) world.add_object(factory.make_object(name), x, y) return world
def get_baked(world: World, position: Position) -> [(Direction, float)]: if world not in Directions.compiled_positions: Directions.compiled_positions[world] = {} for i in range(world.get_dimensions()[0]): for j in range(world.get_dimensions()[1]): Directions.compiled_positions[world][( i, j)] = Directions.get_weighted_directions(world, i, j) return Directions.compiled_positions[world][(position.x, position.y)].copy()
def __generate_with_grounds(grounds_file: str) -> World: with open(grounds_file) as file: lines: [str] = file.readlines() width, height = int(lines[0].split()[0]), int(lines[0].split()[1]) world = World(width, height) for y in range(height): strings = lines[y + 1].split() for x in range(width): world.set_location(Location(factory.make_ground(strings[x])), x, y) return world
def draw_world(world: World, *object_kinds: [Kind]): """\ Draws a World in its current state. """ colours: [[Colour]] = world.get_printable(*object_kinds) colour_list_of_list = [] cmap = [] colour_dict = {} for i in range(len(colours[0])): colour_list_of_list.append([]) for j in range(len(colours)): if colours[j][i].get_rgba() not in colour_dict: colour_dict[colours[j][i].get_rgba()] = len(colour_dict) cmap.append(colours[j][i].get_rgba()) colour_list_of_list[i].append( colour_dict[colours[j][i].get_rgba()]) cmap = mpl.colors.ListedColormap(cmap) plt.figure(figsize=(len(colour_list_of_list[0]), len(colour_list_of_list))) plt.axis("off") plt.pcolor(colour_list_of_list[::-1], cmap=cmap) plt.get_current_fig_manager().window.state("zoomed") plt.show()
def change_dir_random(world: World, elapsed: float, location: Location, pos: Position): possible_free_locations = [] # scan 1 range saving taking note of all available spots for i in range(-1, 2): for j in range(-1, 2): if i != 0 and j != 0: if world.get_location(pos.get_coordinates()[0] + i, pos.get_coordinates()[1] + j).is_free(): possible_free_locations.append( [pos.get_coordinates()[0] + i, pos.get_coordinates()[1] + j, i, j]) # if at least 1 available spot in 1 range if len(possible_free_locations) > 0: # pick a random one and move there random_available_space = random.choice(possible_free_locations) WanderBehaviour.move(ant, world.get_location(pos.x, pos.y), world.get_location(random_available_space[0], random_available_space[1])) ant.current_dir = [random_available_space[2], random_available_space[3]]
def tick(self, world: World, elapsed: float, location: Location, position: Position): self.age += elapsed * ModularAnt.AGE_SCALE self.current_wander_behaviour.set_age(self.age) self.current_wander_behaviour.set_seeking_food(not self.carrying_food) if random.random() > self.hold_position_chance * (1 + self.interacting_ticks): self.current_wander_behaviour.do(world, elapsed, location, position, self) if self.age < ModularAnt.FORAGING_AGE or random.random() < ModularAnt.\ FORAGING_PHEROMONE_OVERRIDE_CHANCE: location.add_pheromones(ModularAnt.PHEROMONES_PER_TICK) else: if location.get_brood_pheromone_count() == 0 and location.get_pheromone_count() == 0: location.add_foraging_pheromones(ModularAnt.FORAGING_PHEROMONES_PER_TICK) # If this ant is in the foraging area, it will pick up food readily. if not self.carrying_food: if location.get_ground().get_id() == ForageGrounds.get_id() or \ random.random() < ModularAnt.ALT_PICKUP_CHANCE: foods: [Object] = location.get_objects(Kind.FOOD) if len(foods) > 0: self.carrying_food = True location.remove_object(foods[-1]) elif location.get_ground().get_id() == Nest.get_id(): if random.random() < ModularAnt.FOOD_DROP_CHANCE or sum(map(lambda l: len(l.get_objects(Kind.FOOD)), world.get_adjacent_locations(position.x, position.y, 1, False))) > 0: location.add_object(Food()) self.carrying_food = False # Measure interactions: others: [int] = [] for loc in world.get_adjacent_locations(position.x, position.y, ModularAnt.INTERACTION_RADIUS, False): if loc.get_actor() is None: continue if isinstance(loc.get_actor(), ModularAnt): other: ModularAnt = loc.get_actor() if ModularAnt.can_interact(self, other): others.append(other.get_ant_id()) if self.interacting_with_id in others: self.interacting_ticks += 1 if self.interacting_ticks > ModularAnt.INTERACTING_TICKS_THRESHOLD: self.interact(self.interacting_with_id) elif len(others) > 0: self.interacting_with_id = others[0] self.interacting_ticks = 0
def pheromone_bias(world: World, position: Position, direction: Direction, weight: float) -> float: try: loc: Location = world.get_location(position.x + direction.get()[0], position.y + direction.get()[1]) return weight + (bias * loc.get_pheromone_count() if loc.is_free() and weight != 0 else 0) except IndexError: return 0
def tick(self, world: World, elapsed: float, location: Location, position: Position): if self.brood_position is None: self.brood_position = (position.x, position.y) self.current_wander_behaviour.set_brood_position(self.brood_position) # location.add_brood_pheromones(ModularQueen.BROOD_PHEROMONES_PER_TICK) for loc in world.get_adjacent_locations(position.x, position.y, 1, False): loc.add_brood_pheromones(ModularQueen.BROOD_PHEROMONES_PER_TICK) self.current_wander_behaviour.set_age(ModularQueen.QUEEN_LOGICAL_AGE) super().tick(world, elapsed, location, position)
def __populate_with_actors(world: World, actors_file: str) -> World: if actors_file is None or actors_file == "": return world with open(actors_file) as file: lines: [str] = file.readlines() for line in lines: name, x, y = line.split()[0], int(line.split()[1]), int( line.split()[2]) attributes: Union[Attributes, None] if len(line.split()) > 3: attributes = Attributes( ast.literal_eval(''.join(line.split()[3:]))) else: attributes = None world.add_actor(factory.make_actor(name, attributes), x, y) return world
def exploration_bias(world: World, position: Position, direction: Direction, weight: float) -> float: try: loc: Location = world.get_location(position.x + direction.get()[0], position.y + direction.get()[1]) return weight * (tanh((age - 130) / 10) + 2 # weight * 5 * (tanh((age - 130)/(10)) + 1.2 # ) if (loc.get_pheromone_count() <= FORAGING_PHEROMONE_THRESHOLD and loc.get_brood_pheromone_count() <= FORAGING_PHEROMONE_THRESHOLD) or \ loc.get_foraging_pheromone_count() > FORAGING_PHEROMONE_THRESHOLD else weight except IndexError: return 0
def tick(self, world: World, elapsed: float, location: Location, position: Position): try: # COMPLETELY RANDOM CHOICES FOR WALKS (no preserving of direction). to: Location = Random().choice( world.get_adjacent_locations(position.x, position.y, 1, True)) self.move(location, to) self.food_interaction(to, world, position) except IndexError: # The ant can't find anywhere to move... location.add_object(TestObject())
def get_weighted_directions(world: World, x: int, y: int) -> [(Direction, float)]: directions: [(Directions, float)] = [] for i in range(-1, 2): for j in range(-1, 2): if i == j: continue weight: float try: loc: Location = world.get_location(x + i, y + j) weight = 1 if loc.is_free() else 0 except (IndexError, InvalidLocationException): weight = 0 directions.append((Direction(i, j), weight)) return directions
def food_interaction(self, loc: Location, world: World, position: Position): if self.carrying is not None and Random().random( ) < StigmergyAnt.DROP_CHANCE: for l in world.get_adjacent_locations(position.x, position.y, 1, False): if len(l.get_objects(Kind.FOOD)) > 0: loc.add_object(self.carrying) self.carrying = None break else: food_items: [Object] = loc.get_objects(Kind.FOOD) if len(food_items) > 0: self.carrying = food_items[0] loc.remove_object(self.carrying)
def change_dir_random_chance(self, world: World, elapsed: float, location: Location, pos: Position, chance: float): if chance < random.random(): #90% of the time #if spot in same direction is free and passes a 33% chance, go there if world.get_location(pos.get_coordinates()[0] + self.current_dir[0], pos.get_coordinates()[1] + self.current_dir[1]).is_free() and random.random() < 1/3: self.move_ant(world, elapsed, pos, [pos.get_coordinates()[0] + self.current_dir[0], pos.get_coordinates()[1] + self.current_dir[1]]) else: #with a 50% choose the adjacent spot e.g. if going north, pick north-east 50% of the time , north-west 50% of the time next_dirr = self.give_dir_next_to_current( self.current_dir[0], self.current_dir[1]) #if the first picked adjacent spot is free go there if world.get_location(pos.get_coordinates()[0] + next_dirr[0][0],pos.get_coordinates()[1] + next_dirr[0][1]).is_free(): self.move_ant(world, elapsed, pos, [pos.get_coordinates()[0] + next_dirr[0][0], pos.get_coordinates()[1] + next_dirr[0][1]]) # else if the second picked adjacent spot is free go there elif world.get_location(pos.get_coordinates()[0] + next_dirr[1][0],pos.get_coordinates()[1] + next_dirr[1][1]).is_free(): self.move_ant(world, elapsed, pos, [pos.get_coordinates()[0] + next_dirr[1][0], pos.get_coordinates()[1] + next_dirr[1][1]]) #else go random available spot ... and if no spot available change_dir_random will make the ant stay still else: self.change_dir_random(world,elapsed,location,pos) else: #10% of the time go random available spot ... and if no spot available change_dir_random will make the ant stay still self.change_dir_random(world,elapsed,location,pos)
def food_lure(world: World, position: Position, direction: Direction, weight: float) -> float: locations: [(Location, int, int) ] = world.get_adjacent_locations_with_positions( position.x, position.y, FOOD_LURE_RADIUS, True) return weight + sum( map( lambda lxy: FOOD_BIAS_FACTOR * len(lxy[0].get_objects(Kind.FOOD )), filter( lambda lxy: (sign(lxy[1] - position.x) == sign(direction.x) or direction.x == 0) and (sign(lxy[2] - position.y) == sign( direction.y) or direction.y == 0), filter( lambda lxy: lxy[0].get_ground().get_id() == ForageGrounds.get_id(), locations))))
def move_ant(self,world: World, elapsed: float, pos: Position, target:list): world.get_location(pos.get_coordinates()[0], pos.get_coordinates()[1]).remove_actor() world.get_location(target[0], target[1]).set_actor(self) world.get_location(target[0], target[1]).add_object(TestObject())
def save(world: World): world.write_out() ModularAnt.save_interactions()
def tick(self, world: World, elapsed: float, location: Location, position: Position): if random.random() < FoodGenerator.CHANCE: for loc in world.get_adjacent_locations(position.x, position.y, FoodGenerator.RADIUS, False): if len(loc.get_objects(Kind.FOOD)) == 0: loc.add_object(Food())
def change_dir_random_chance(self, world: World, elapsed: float, location: Location, pos: Position, variation_chance: float, wobble_chance: float, ant: BehaviourAnt): def give_dir_next_to_current(i, j): if i == -1 and j == 1: if random.random() > 0.5: return ([[-1, 0], [0, 1]]) return ([[0, 1], [-1, 0]]) if i == -1 and j == 0: if random.random() > 0.5: return ([[-1, 1], [-1, -1]]) return ([[-1, -1], [-1, 1]]) if i == -1 and j == -1: if random.random() > 0.5: return ([[-1, 0], [0, -1]]) return ([[0, -1], [-1, 0]]) if i == 0 and j == 1: if random.random() > 0.5: return ([[-1, 1], [1, 1]]) return ([[1, 1], [-1, 1]]) if i == 0 and j == -1: if random.random() > 0.5: return ([[-1, -1], [1, -1]]) return ([[1, -1], [-1, -1]]) if i == 1 and j == 1: if random.random() > 0.5: return ([[0, 1], [1, 0]]) return ([[1, 0], [0, 1]]) if i == 1 and j == 0: if random.random() > 0.5: return ([[1, 1], [1, -1]]) return ([[1, -1], [1, 1]]) if i == 1 and j == -1: if random.random() > 0.5: return ([[1, 0], [0, -1]]) return ([[0, -1], [1, 0]]) def change_dir_random(world: World, elapsed: float, location: Location, pos: Position): possible_free_locations = [] # scan 1 range saving taking note of all available spots for i in range(-1, 2): for j in range(-1, 2): if i != 0 and j != 0: if world.get_location(pos.get_coordinates()[0] + i, pos.get_coordinates()[1] + j).is_free(): possible_free_locations.append( [pos.get_coordinates()[0] + i, pos.get_coordinates()[1] + j, i, j]) # if at least 1 available spot in 1 range if len(possible_free_locations) > 0: # pick a random one and move there random_available_space = random.choice(possible_free_locations) WanderBehaviour.move(ant, world.get_location(pos.x, pos.y), world.get_location(random_available_space[0], random_available_space[1])) ant.current_dir = [random_available_space[2], random_available_space[3]] if variation_chance < random.random(): # 90% of the time # if spot in same direction is free and passes a 33% chance, go there if world.get_location(pos.get_coordinates()[0] + ant.current_dir[0], pos.get_coordinates()[1] + ant.current_dir[ 1]).is_free() and random.random() < wobble_chance: WanderBehaviour.move(ant, world.get_location(pos.x, pos.y), world.get_location(pos.get_coordinates()[0] + ant.current_dir[0], pos.get_coordinates()[1] + ant.current_dir[1])) else: # with a 50% choose the adjacent spot e.g. if going north, pick north-east 50% of the time , north-west 50% of the time next_dirr = give_dir_next_to_current(ant.current_dir[0], ant.current_dir[1]) # if the first picked adjacent spot is free go there if world.get_location(pos.get_coordinates()[0] + next_dirr[0][0], pos.get_coordinates()[1] + next_dirr[0][1]).is_free(): WanderBehaviour.move(ant, world.get_location(pos.x, pos.y), world.get_location(pos.get_coordinates()[0] + next_dirr[0][0], pos.get_coordinates()[1] + next_dirr[0][1])) # else if the second picked adjacent spot is free go there elif world.get_location(pos.get_coordinates()[0] + next_dirr[1][0], pos.get_coordinates()[1] + next_dirr[1][1]).is_free(): WanderBehaviour.move(ant, world.get_location(pos.x, pos.y), world.get_location(pos.get_coordinates()[0] + next_dirr[1][0], pos.get_coordinates()[1] + next_dirr[1][1])) # else go random available spot ... and if no spot available change_dir_random will make the ant stay still else: change_dir_random(world, elapsed, location, pos) else: # 10% of the time go random available spot ... and if no spot available change_dir_random will make the ant stay still change_dir_random(world, elapsed, location, pos)