import argparse import re from enum import Enum from string import Template from typing import NamedTuple from jinja2 import Environment, PackageLoader, select_autoescape import items from destinations import DESTINATIONS, SYSTEMS, Destination, DestinationType # global variables env = Environment(loader=PackageLoader("swjg"), autoescape=select_autoescape) parser = argparse.ArgumentParser() # helper definitions def get_identifiers_from_template(template): # Template.get_identifiers() doesn't appear until Python 3.11 # Don't substitute anything; get the raw string template_str = Template.safe_substitute() matches = re.findall("\$[a-z0-9_\.]+", template_str) matches = [x[1:] for x in matches] return matches def get_destination_type_from_enum(destination): dtype = destination.type dtype_name = dtype._name_ dtype_name_lower = dtype_name.lower() dtype_name_nou = dtype_name_lower.replace("_", " ") if dtype_name_nou[0] in ["a", "e", "i", "o", "u"]: # it's only "asteroid" and "ocean" now but it might expand dtype_name_final = "an " + dtype_name_nou else: dtype_name_final = "a " + dtype_name_nou return dtype_name_final # Class definitions class Mission: output_destination_template = Template( "to $world $destination_type in the $system system" ) output_sector_template = Template(", part of the $sector,") value_template = Template("for $value credits") error_template = Template( "You haven't given this mission some of the values it needs. Please check: $potential_values" ) def __init__(self, destination: Destination, value: int, *args, **kwargs): # destination: Destination (see above) self.world = destination.name if destination.name is not None else None self.system = ( destination.system if destination.system is not None else destination.name ) self.sector = destination.sector if destination.sector is not None else None self.destination_type = get_destination_type_from_enum(destination) self.value = value def assemble_templates(self) -> list: # Override this in children template_list = [] if self.world != None: template_list.append(self.output_destination_template) if self.sector != None: template_list.append(self.output_sector_template) template_list.append(self.value_template) return template_list def get_current_values(self) -> dict: return { "world": self.world + "," if self.world != "" else "", "system": self.system, "sector": self.sector, "destination_type": self.destination_type, "value": self.value, } def missing_values(self) -> list: object_vars = vars(self) missing = [] for key, val in object_vars.items(): if val is None and key != "time": # time is optional missing.append(key) return missing def assemble_text(self) -> str: missing_vals = self.missing_values() templates = self.assemble_templates() if ( len(templates) == 0 or len(missing_vals) != 0 ): # either both of these should be true or neither should raise ValueError( self.error_template.substitute(potential_values=missing_vals) ) current_values = self.get_current_values() output = [] for template in templates: output.append(template.substitute(**current_values)) out_text = " ".join(output) out_text = out_text.replace(" ,", ",") out_text += "." return out_text class PassengerMission(Mission): output_initial_template = Template("Bring $number passengers") def __init__(self, number: int, *args, **kwargs): super(PassengerMission, self).__init__(*args, **kwargs) self.number = number def get_current_values(self) -> dict: base_dict = super(PassengerMission, self).get_current_values() new_dict = {"number": self.number} base_dict.update(new_dict) return base_dict def assemble_templates(self): template_list = [self.output_initial_template] template_list.extend(super(PassengerMission, self).assemble_templates()) return template_list def assemble_text(self) -> str: return super(PassengerMission, self).assemble_text() class CargoMission(Mission): output_initial_template = Template("Deliver $tons tons of $item") output_time_timeplate = Template("in the next $time days") def __init__(self, tons: int, item: str, time: int | None, *args, **kwargs): # tons: integer # item: string (this will not be pluralized in the text) # time: integer (number of days the crew have to complete delivery) # value: integer (value in credits) super(CargoMission, self).__init__(*args, **kwargs) self.tons = tons self.item = item self.time = time def get_current_values(self) -> dict: base_dict = super(CargoMission, self).get_current_values() new_dict = {"tons": self.tons, "item": self.item, "time": self.time} base_dict.update(new_dict) return base_dict def assemble_templates(self): template_list = [self.output_initial_template] if self.time is not None: template_list.append(self.output_time_timeplate) template_list.extend(super(CargoMission, self).assemble_templates()) return template_list def assemble_text(self) -> str: return super(CargoMission, self).assemble_text() class GroupMission(Mission): output_initial_template = Template("Bring $number members of a $group") def __init__(self, number: int, group: str, *args, **kwargs): super(GroupMission, self).__init__(*args, **kwargs) self.number = number self.group = group def get_current_values(self) -> dict: base_dict = super(GroupMission, self).get_current_values() new_dict = {"number": self.number, "group": self.group} base_dict.update(new_dict) return base_dict def assemble_templates(self): template_list = [self.output_initial_template] template_list.extend(super(GroupMission, self).assemble_templates()) return template_list def assemble_text(self) -> str: return super(GroupMission, self).assemble_text() # the function that does the thing def main(): import random mission_types = [PassengerMission, CargoMission, GroupMission] missions = [] for _ in range(5): current_mission = random.choice(mission_types) dest_type = random.randint(1, 5) if dest_type < 3: # world, 40% chance destination = random.choice(DESTINATIONS) else: system = random.choice(SYSTEMS) destination = Destination( name="", system=system[0], sector=system[1], type=( DestinationType.STATION if random.randint(0, 1) == 0 else DestinationType.ASTEROID_BASE ), ) item, time, tons, number, value, group = [None] * 6 if current_mission == CargoMission: current_item = random.choice(items.ITEMS) tons = random.randint( current_item.potential_tons[0], current_item.potential_tons[1] ) value = random.randint(*current_item.potential_values) * 1000 item = current_item.name if current_item.potential_times is not None: if current_item.chance_for_time is not None: is_there_a_time = random.random() * 100 # print(current_item.chance_for_time, is_there_a_time, is_there_a_time < current_item.chance_for_time) if is_there_a_time < current_item.chance_for_time: time = random.randint( current_item.potential_times[0], current_item.potential_times[1], ) else: number = random.randint(1, 8) time = random.randint(7, 31) value = random.randint(20, 120) * 1000 if current_mission == GroupMission: group = random.choice(["family", "performing troupe", "acrobatic troupe"]) missions.append( current_mission( destination=destination, number=number, tons=tons, item=item, time=time, value=value, group=group, ) ) for mission in missions: # print(mission, mission.__dict__) print(mission.assemble_text()) # Don't do anything if the module is loaded wholesale into something else if __name__ == "__main__": main()