def __init__(self): possible_areas = [area for area in Map().areas if not len(area.players)] if START_AREA in possible_areas: area = Map().get_area(START_AREA) else: area = choice(possible_areas) Event.__init__(self, 'drop', [area])
def pillage(self, stuff): if len([p for p in Context().alive_players if p.is_alive]) == 1: return if Map().players_count(self) > 1: return looted = [] for item in stuff: if item not in Map().loot(self.current_area): continue if isinstance(item, Weapon): if item.damage_mult > self.weapon.damage_mult: looted.append(item) Map().remove_loot(item, self.current_area) else: looted.append(item) Map().remove_loot(item, self.current_area) if not len(looted): return Narrator().add( [self.name, 'loots', format_list([e.long_name for e in looted])]) for item in looted: if isinstance(item, Weapon): self.get_weapon(item) else: self.get_item(item)
def loot_bag(self): items: List[Item] = [] for _ in self.acting_players: b = Map().pick_bag(self) if b is None: b = Map().pick_item(self) if b is not None: items.append(b) items.sort(key=lambda x: (isinstance(x, Bag)) * 10 + random()) self.acting_players.sort( key=lambda x: (x.bag is not None) * 10 + random()) empty_handed: List[Player] = [] for player in self.acting_players: if len(items): item = items.pop(0) Narrator().add([ player.name, 'picks up', item.long_name, self.current_area.at ]) player.get_item(item) else: empty_handed.append(player) if len(empty_handed): verb = 'try' if len(empty_handed) > 1 else 'tries' Narrator().add([ player_names(empty_handed), verb, 'to find loot', self.current_area.at, 'but can\'t find anything good' ])
def go_to(self, area: Union[str, Area, Entity]) -> Optional[Area]: area = Map().get_area(area) if area != self.current_area: self.end_ambush() for p in self.acting_players: p.go_to(area) return Map().move_player(self, area) return None
def go_to(self, area: Union[str, Area, Entity]) -> Optional[Area]: area = Map().get_area(area) if area != self.current_area: self.reveal() self._energy -= self.move_cost self.busy = True return Map().move_player(self, area) return None
def loot_start(self): w = self.estimate(Map().weapons(self)) b = self.weapon.damage_mult * Map().has_bags(self) * (self.bag is None) if w > b and w > 0: self.loot_weapon() elif b > 0: self.loot_bag(take_a_break=False) else: self.hide(panic=True)
def hide(self, panic=False, stock=False): if panic: Narrator().add([self.name, 'hides', self.current_area.at], stock=stock) return if self.sleep < 0.1 \ or (Context().time == NIGHT and Map().players_count == 1 and len(self.wounds) == 0) \ or (Map().players_count == 1 and self.sleep < 0.2 and len(self.wounds) == 0) \ or (Context().time == NIGHT and self.sleep < 0.3 and len(self.wounds) == 0): self.go_to_sleep(stock=stock) return return self.rest(stock=stock)
def check_for_ambush_and_traps(self): traps = Map().traps(self) for t in traps: if t.check(self): t.apply(self) return True ambushers = Map().ambushers(self) if not len(ambushers): return False ambusher = choice(ambushers) ambusher.trigger_ambush(self) return True
def available_areas(cls) -> List[Area]: areas = copy(Map().areas) if cls.water == -1: areas = [a for a in areas if not a.has_water] if cls.water == 1: areas = [a for a in areas if a.has_water] return areas
def loot_weapon(self, weapon: Optional[Weapon] = None, retry=True): if weapon is None: weapon = Map().pick_weapon(self.current_area) if weapon is None or (weapon.damage_mult <= self.weapon.damage_mult and (not weapon.small or Context().time == STARTER or self.bag is None)): if retry: return self.loot(take_a_break=False) else: Narrator().add([ self.name, 'tries to find a weapon', self.current_area.at, 'but can\'t find anything good' ]) return if weapon.name == self.weapon.name: self.weapon.long_name.replace('\'s', '\'s old') Narrator().add([ self.name, 'picks up', f'a better {weapon.name}', self.current_area.at ]) else: Narrator().add([ self.name, 'picks up', weapon.long_name, self.current_area.at ]) self.get_weapon(weapon)
def fill_bottles(self): self.water_upkeep() if Map().has_water(self): total_water = self.water + sum(b.fill for b in self.bottles) self._water = max(1.5, self.water) for b in self.bottles: b.fill = 1 new_total_water = self.water + sum(b.fill for b in self.bottles) if new_total_water > total_water + 1: if len(self.bottles): Narrator().add([ self.name, 'fills', self.his, 'bottles' if len(self.bottles) > 1 else 'bottle', self.current_area.at ]) else: Narrator().add([self.name, 'drinks', self.current_area.at]) else: water = random() amount = min(self.thirst, water) self._water += amount water -= amount for b in self.bottles: amount = min(1 - b.fill, water) b.fill += amount water -= amount
def flee(self, area=None, panic=False, drop_verb='drops', stock=False, filtered_areas=None): if area is None: if filtered_areas is None: filtered_areas = [] filtered_areas = [*Context().forbidden_areas, *filtered_areas] self.status.append(FLEEING) if panic and random() > self.courage + 0.5: self.drop_weapon(verbose=True, drop_verb=drop_verb) available_areas = [ area for area in Map().areas if area not in filtered_areas ] available_areas.sort(key=lambda x: self._flee_value(x)) if available_areas: area = available_areas[-1] out = None self.reveal() if area is not None: out = self.go_to(area) if out is None: self.hide(panic=panic, stock=stock) else: Narrator().add([self.name, f'flees {out.to}'], stock=stock) self.check_for_ambush_and_traps()
def set_up_ambush(self): self.stealth += (random() / 2 + 0.5) * (1 - self.stealth) self.end_ambush() Map().add_ambusher(self, self) self._ambush = True Narrator().add( [self.name, 'sets up', 'an ambush', self.current_area.at])
def loot_bag(self, take_a_break=True): item = Map().pick_bag(self.current_area) if item is None: return self.loot(take_a_break=take_a_break) Narrator().add( [self.name, 'picks up', item.long_name, self.current_area.at]) self.get_item(item)
def get_drop_strats(): return [ Strategy(f'go get loot {area.at}', lambda x: x.should_go_get_drop(area), lambda x: x.go_get_drop(area)) for area in Map().areas if area not in Context().forbidden_areas ]
def __init__(self, owner, stealth): Entity.__init__(self, self._name, 'it') self.long_name = f'{owner.name}\'s {self.name}' self.owner = owner self.knowing = owner.players self.stealth = stealth Map().add_trap(self, owner)
def can_flee(self): filtered_areas = Context().forbidden_areas accessible_areas = [a for a in Map().areas if a not in filtered_areas] if not len(accessible_areas): return False if TRAPPED in self.status: return False return self.energy + self.health > self.move_cost or not self.current_area.is_start
def estimate_of_danger(self, area) -> float: neighbors = Map().potential_players(area) if not len(neighbors): return 0 seen_neighbors = [ p for p in neighbors if self.can_see(p) and p != self ] return sum([p.dangerosity for p in seen_neighbors])
def drop_weapon(self, verbose=True, drop_verb='drops'): if self.weapon != HANDS: if verbose: Narrator().add([ self.name, drop_verb, f'{self.his} {self.weapon.name}', self.current_area.at ]) Map().add_loot(self.weapon, self.current_area) self._weapon = HANDS
def go_get_drop(self, area: Area): self.end_ambush() out = self.go_to(area) if out is not None: Narrator().add([self.name, f'goes {out.to} to get loot']) else: Narrator().cut() if self.check_for_ambush_and_traps(): return seen_neighbors = [ p for p in Map().potential_players(self) if self.can_see(p) and p != self ] free_neighbors = [ p for p in seen_neighbors if p.current_area == self.current_area and not p.busy ] potential_danger = sum([p.dangerosity for p in seen_neighbors]) actual_danger = sum([p.dangerosity for p in free_neighbors]) if potential_danger > self.dangerosity and potential_danger > self.courage: Narrator().add([ self.name, 'sees', format_list([p.name for p in seen_neighbors]) ]) self.flee(filtered_areas=[area]) elif actual_danger > self.dangerosity and actual_danger > self.courage: Narrator().add([ self.name, 'sees', format_list([p.name for p in free_neighbors]) ]) self.flee(filtered_areas=[area]) elif actual_danger > 0: # enemy present -> fight them Narrator().cut() self.attack_at_random() elif potential_danger > 0 and actual_danger == 0: # enemy busy but incoming if self.dangerosity > potential_danger: # attack before the other(s) arrive Narrator().cut() self.attack_at_random() else: # loot and go/get your load and hit the road Narrator().add([ self.name, 'avoids', format_list([p.name for p in seen_neighbors]) ]) self.loot(take_a_break=False) self.flee(filtered_areas=[area]) else: # servez-vous self.loot() if len(Narrator().current_sentence) == 0: Narrator().add([ self.name, f'potential_danger={potential_danger}', f'actual_danger={actual_danger}', f'dangerosity={self.dangerosity}', f'courage={self.courage}', ])
def loot(self, take_a_break=True): if take_a_break: self.take_a_break() if Map().players_count(self) == 1: item = Map().pick_best_item(self) else: item = Map().pick_item(self.current_area) if item is None or (isinstance(item, Weapon) and item.damage_mult <= self.weapon.damage_mult): Narrator().add([ self.name, 'tries to loot', self.current_area.at, 'but can\'t find anything useful' ]) return if isinstance(item, Weapon): self.loot_weapon(item, retry=False) else: Narrator().add( [self.name, 'picks up', item.long_name, self.current_area.at]) self.get_item(item)
def apply(self, players: Union[PlayingEntity, List[PlayingEntity]]): if not isinstance(players, list): players = [players] if not self.group_trap: players = [choice(players)] for player in players: name = self.long_name if player is self.owner: name = f'{player.his} own {self.name}' Map().remove_trap(self) player.reveal() self._apply(name, player)
def __init__(self, players: Optional[List[Player]]=None): if players is None: raise ValueError('No players in the arena') Context().game = self self.players = players self.map = Map(len(players)) self._event_gauge = 0 self._players_at_last_event = 0 self._time_since_last_event = 0 for p in self.players: self.map.add_player(p) for p2 in self.players: if p != p2: if p.district == p2.district: p.relationship(p2).add_friendship(random() * 0.5 + 0.5) p.relationship(p2).add_trust(random() * 0.5) else: p.relationship(p2).add_friendship(random()) p.relationship(p2).add_trust(random() * 0.25) self.event_classes = [WildFire, DropEvent, Flood, AcidGas, Wasps, Beasts] self.day = 0 self.time = STARTER
def judge_strats(self) -> dict: if self.sleep < 0: if Map().players_count(self) > 1 and self.energy > self.move_cost: strats = flee_strats() else: return {hide_strat: 1} else: if Context().time == NIGHT: strats = night_strategies() elif Context().time == STARTER: strats = start_strategies() else: strats = morning_strategies() # strats.sort(key=lambda x: -x.pref(self) + random() * (1 - self.wisdom)) return {s: s.pref(self) + random() * (1 - self.wisdom) for s in strats}
def trigger(self): Context().forbidden_areas.extend(self.areas) saw_it_coming = [] warned = [] # Narrator().add(['should trigger for', [p.name for a in self.areas for p in a.players]]) for area in self.areas: for p_e in Context().playing_entities_at(area): for p in p_e.players: if p.wisdom * random() > self.stealth: saw_it_coming.append(p.name) warned.extend(p_e.players) break if len(saw_it_coming): Narrator().new([ format_list(saw_it_coming), 'see' if len(saw_it_coming) > 1 else 'sees', self.it, 'coming' ]) for area in self.areas: area_players = copy(area.players) for p in area_players: Narrator().cut() if p.can_flee(): if p in warned: p.flee() if p.current_area in self.areas: Narrator().new([ 'error:', 'forbidden:', Context().forbidden_areas, p.name, p.current_area.at]) Map().test = 'SHIT' elif p.be_damaged(self.base_damage, weapon=self.weapon_name): Narrator().new([p.name, 'fails', 'to escape', self.it, 'and', self.dies, area.at]) else: Narrator().apply_stock() p.flee(panic=True, drop_verb='loses') continue # successful escape Narrator().new([p.name, 'is', 'trapped', area.at]) if self.trapped_means_dead: p.be_damaged(1) Narrator().add([p.name, 'is', 'swiped', 'by', self.it, area.at, 'and', self.dies]) elif p.be_damaged(random() * self.extra_damage + self.base_damage, weapon=self.weapon_name): Narrator().add(['and', self.dies]) else: Narrator().apply_stock() if not len(Narrator().current_sentence): # error Narrator().add([p.name, 'escaped', self.it, 'and is', p.current_area.at]) Narrator().clear_stock() if self.remove_loot: area.loot.clear()
def loot_weapon(self, weapon: Optional[Union[Weapon, List[Weapon]]] = None): weapons: List[Weapon] = [] if isinstance(weapon, Weapon): weapons.append(weapon) if isinstance(weapon, list): weapons = weapon if weapon is None: for _ in self.acting_players: weapons.append(Map().pick_weapon(self)) weapons = [w for w in weapons if w is not None] weapons.sort(key=lambda x: -x.damage_mult) self.acting_players.sort(key=lambda x: -x.weapon.damage_mult) empty_handed: List[Player] = [] for player in self.acting_players: picked = False for weapon in weapons: if weapon.damage_mult > player.weapon.damage_mult: if weapon.name == player.weapon.name: player.weapon.long_name.replace('\'s', '\'s old') Narrator().add([ player.name, 'picks up', f'a better {weapon.name}', self.current_area.at ]) else: Narrator().add([ player.name, 'picks up', weapon.long_name, self.current_area.at ]) if player.weapon != HANDS: weapons.append(player.weapon) player.get_weapon(weapon) weapons.remove(weapon) picked = True break if not picked: empty_handed.append(player) if len(empty_handed): verb = 'try' if len(empty_handed) > 1 else 'tries' Narrator().add([ player_names(empty_handed), verb, 'to find weapons', self.current_area.at, 'but can\'t find anything good' ])
def set_up_ambush(self): self.stealth += (random() / 2 + 0.5) * (1 - self.stealth) if AMBUSH not in self.status: self.status.append(AMBUSH) Map().add_ambusher(self, self) Narrator().add( [self.name, 'sets up', 'an ambush', self.current_area.at]) else: self._waiting += 1 if self._waiting < 2: Narrator().add( [self.name, 'keeps', 'hiding', self.current_area.at]) else: Narrator().add([ self.name, 'gets', 'tired of hiding', self.current_area.at ]) self.end_ambush() self.pursue()
def pursue(self): available_areas = [ a for a in Map().areas if a not in Context().forbidden_areas ] available_areas.sort( key=lambda x: sum(p._pursue_value(x) for p in self.acting_players)) out = None if not available_areas: out = self.go_to(available_areas[-1]) if out is None: self.hide() Narrator().replace('hides and rests', 'rests') else: targets = [ p.name for p in Context().alive_players if p not in self.players ] players = 'players' if len(targets) > 2 else format_list(targets) Narrator().add([self.name, 'searches for', players, out.at]) self.check_for_ambush_and_traps()
def consider_betrayal(self): allies = self.allies() # when there are no enemies left if len([ p for p in Context().alive_players if p != self and not self.is_allied_to(p) ]) == 0: if len(allies): allies.sort(key=lambda x: x.dangerosity) self.betray(allies[-1]) # betray the most dangerous one return True allies_by_value = { x.dangerosity + self.relationship(x).trust + self.relationship(x).friendship: x for x in allies } if allies and min(allies_by_value) < 0: Map().test = f'{self.name} betrays' self.betray(allies_by_value[min( allies_by_value)]) # get rid of useless/untrustworthy return False
def pursue(self): available_areas = [ a for a in Map().areas if a not in Context().forbidden_areas ] available_areas.sort(key=lambda x: -self._pursue_value(x)) out = self.go_to(available_areas[0]) if out is None: self.hide() Narrator().replace('hides and rests', 'rests') else: self.end_ambush() targets = [ p.name for p in Context().alive_players if p != self and not self.relationship(p).allied ] if len(targets) == 0: Narrator().add( [self.name, 'doesn\'t know', 'who to look for', out.at]) else: players = 'players' if len(targets) > 1 else targets[0] Narrator().add([self.name, 'searches for', players, out.at]) self.check_for_ambush_and_traps()