def __init__(self, names, baby_generator, person_developer, couple_creator, couple_developer, statistics, foster_care_system): self.households = [] self.neighbors = [] self.neighbor_couples = [] # Essential classes self.names = names self.person_developer = person_developer self.couple_creator = couple_creator self.couple_developer = couple_developer # Handler classes self.randomizer = Randomizer() self.death_handler = DeathHandler(self.person_developer) self.marriage_handler = MarriageHandler() self.divorce_handler = DivorceHandler() self.personal_handler = PersonalHandler(self.person_developer) self.lgbta_handler = LgbtaHandler(self.names, self.person_developer) self.addiction_handler = AddictionHandler(self.person_developer) self.pregnancy_handler = PregnancyHandler(baby_generator, statistics, foster_care_system) self.career_handler = CareerHandler(statistics) self.conditions_handler = ConditionsHandler() # Automatically create given number of apartments/households self.create_households()
def __init__(self): self.available_degree = self.SCHOOL self.acquired_degree = [self.UNEDUCATED] self.in_study = False self.current_year = 0 self.years_to_complete_degree = 0 self.total_fail = 0 self.randomizer = Randomizer()
def __init__(self, level=0, salary=0, employment=Traits.UNEMPLOYED): self.level = level self.salary = salary # per year self.employment = employment self.unemployed_year = 0 self.title = None self.current_performance = 0 self.randomizer = Randomizer() self.setup = Setup()
class Names: """Helper class to get names from lists initialized in Setup.""" def __init__(self, setup): self.setup = setup self.randomizer = Randomizer() def get_name(self, person): """Returns a name from provided list that is unique among person's siblings and cousins.""" name = unique = False while not unique: name = self.randomizer.get_random_item( self.setup.MALE_NAMES ) if person.is_male else self.randomizer.get_random_item( self.setup.FEMALE_NAMES) unique = name not in (person.get_siblings_names, person.get_cousins_names) self.validate_name(name) return name def get_surname(self, unavailable_surnames=None): """Returns a surname from provided list that is unique among the population.""" if unavailable_surnames is None: surname = self.randomizer.get_random_item(self.setup.SURNAMES) self.validate_surname(surname) return surname surname = unique = False while not unique: surname = self.randomizer.get_random_item(self.setup.SURNAMES) unique = surname not in unavailable_surnames self.validate_surname(surname, unavailable_surnames) return surname def validate_name(self, name): if name is None or name is False: raise Exception("Name is null.") if name not in self.setup.MALE_NAMES and name not in self.setup.FEMALE_NAMES: raise Exception("Name is wrong.") def validate_surname(self, surname, unavailable_surnames=None): if surname is None: raise Exception("Surname is null.") if unavailable_surnames is not None and surname in unavailable_surnames: raise Exception("Surname is not unique.") if surname not in self.setup.SURNAMES: raise Exception("Surname is wrong.")
class CityMarriageHandler: """Handles marriage.""" def __init__(self): self.randomizer = Randomizer() def get_married(self, couple): """Marriage.""" if any(p.is_married_or_remarried for p in couple.persons): # Skip if poly person is already married return self.set_married_status(couple) self.set_as_spouses(couple) self.set_shared_surname(couple) self.marriage_validation(couple) @classmethod def set_married_status(cls, couple): for person in couple.persons: if len(person.ex_spouses) > 0: person.relationship_status = Traits.REMARRIED else: person.relationship_status = Traits.MARRIED @classmethod def set_as_spouses(cls, couple): """Set each other as spouses.""" for person in couple.persons: spouses = [p for p in couple.persons if p != person] for spouse in spouses: person.spouses.append(spouse) def set_shared_surname(self, couple): """If person is female and is married to a male, take male's surname. Else, 50/50 chance.""" if couple.is_straight: couple.woman.surname = couple.man.surname else: surnames = [] for p in couple.persons: surnames.append(p.surname) chosen = self.randomizer.get_random_item(surnames) for person in couple.persons: person.surname = chosen @classmethod def marriage_validation(cls, couple): if any([p.is_married_or_remarried is False for p in couple.persons]): raise Exception("Married couple is not set as married.") if any([len(p.spouses) == 0 for p in couple.persons]): raise Exception("Married couple has no assigned spouse.") if couple.marriage_date > couple.oldest.age: raise Exception( "Married couple has marriage date set in the future.") if not all( [couple.persons[0].surname == p.surname for p in couple.persons]): raise Exception("Married couple does not have the same surname.")
class Statistics: def __init__(self, stages): self.stages = stages self.randomizer = Randomizer() def get_gender(self): options = { Traits.MALE: 50, Traits.FEMALE: 50 } selected = self.randomizer.get_random_dict_key(options) if selected not in Traits.GENDERS: raise Exception("Wrong gender.") return selected def get_gender_identity(self): options = { Traits.CISGENDER: 98, Traits.TRANSGENDER: 2 } selected = self.randomizer.get_random_dict_key(options) if selected not in Traits.GENDER_IDENTITIES: raise Exception("Wrong gender identity.") return selected def get_race(self): dbmgr = sql_connect.DatabaseManager("testdb.db") race_data = dbmgr.demo_data("default", "race") dbmgr.__del__() options = { Traits.WHITE: race_data[0]['white'], Traits.BLACK: race_data[0]['black'], Traits.LATINO: race_data[0]['latino'], Traits.ASIAN: race_data[0]['asian'] } selected = self.randomizer.get_random_dict_key(options) if selected not in Traits.RACES: raise Exception("Wrong race.") return selected def get_social_class(self): # The database connection, and the city variable probably need doing somewhere once rather than for # every function, every time # Start the db connection dbmgr = sql_connect.DatabaseManager("testdb.db") # Get the data, passing the city social_class_data = dbmgr.demo_data("default", "social_class") # Close the connection dbmgr.__del__() options = { Traits.LOWERCLASS: social_class_data[0]['lower_class'], Traits.MIDDLECLASS: social_class_data[0]['middle_class'], Traits.UPPERCLASS: social_class_data[0]['upper_class'] } selected = self.randomizer.get_random_dict_key(options) if selected not in Traits.SOCIAL_CLASSES: raise Exception("Wrong social class.") return selected def get_employment_chance(self): dbmgr = sql_connect.DatabaseManager("testdb.db") employment_data = dbmgr.demo_data("default", "employment") dbmgr.__del__() options = { Traits.EMPLOYED: employment_data[0]['employed'], Traits.UNEMPLOYED: employment_data[0]['unemployed'] } selected = self.randomizer.get_random_dict_key(options) if selected not in Traits.EMPLOYMENT: raise Exception("Wrong employment attribute.") return selected def get_death_cause(self, person): dbmgr = sql_connect.DatabaseManager("testdb.db") death_cause_data = dbmgr.demo_data("default", "death_cause") dbmgr.__del__() if person.death_date is False: return Traits.OLD_AGE if person.death_date < self.stages.TEEN.start: return Traits.ILLNESS options_teen = { Traits.ILLNESS: death_cause_data[0]['teen_illness'], Traits.SUICIDE: death_cause_data[0]['teen_suicide'] } options_young_adult = { Traits.ILLNESS: death_cause_data[0]['young_adult_illness'], Traits.SUICIDE: death_cause_data[0]['young_adult_suicide'], Traits.ACCIDENT: death_cause_data[0]['young_adult_accident'] } options_adult = { Traits.ILLNESS: death_cause_data[0]['adult_illness'], Traits.SUICIDE: death_cause_data[0]['adult_suicide'], Traits.ACCIDENT: death_cause_data[0]['adult_accident'] } options_senior = { Traits.ILLNESS: death_cause_data[0]['senior_illness'], Traits.SUICIDE: death_cause_data[0]['senior_suicide'], Traits.ACCIDENT: death_cause_data[0]['senior_accident'] } if person.death_date in self.stages.TEEN.span: selected = self.randomizer.get_random_dict_key(options_teen) elif person.death_date in self.stages.YOUNGADULT.span: selected = self.randomizer.get_random_dict_key(options_young_adult) elif person.death_date in self.stages.ADULT.span: selected = self.randomizer.get_random_dict_key(options_adult) elif person.death_date in self.stages.SENIOR.span: selected = self.randomizer.get_random_dict_key(options_senior) else: raise Exception("Wrong death date.") if selected not in Traits.DEATH_CAUSES: raise Exception("Wrong death cause.") return selected def get_death_date(self): options_general = { "before_old_age": 50, "old_age": 50 } options_before_old_age = { self.stages.BABY: 1, self.stages.CHILD: 2, self.stages.TEEN: 3, self.stages.YOUNGADULT: 4, self.stages.ADULT: 10, self.stages.SENIOR: 80 } selected = self.randomizer.get_random_dict_key(options_general) if selected == "old_age": return False random_life_stage = self.randomizer.get_random_dict_key( options_before_old_age) death_date = self.randomizer.get_random_item(random_life_stage.span) if death_date not in self.stages.LIFESPAN: raise Exception("Wrong death date.") return death_date def get_fertility(self): options = { True: 10, False: 90 } return self.randomizer.get_random_dict_key(options) def get_domestic_partnership_desire(self): options = { True: 80, False: 20 } return self.randomizer.get_random_dict_key(options) def get_children_desire(self): options = { True: 60, False: 40 } return self.randomizer.get_random_dict_key(options) def get_sexual_orientation(self): main = { "het": 90, "h**o/bi": 8, "ace": 2 } homo_bi = { "h**o": 40, "bi": 60 } romantic__aromantic = { "aromantic": 50, "romantic": 50 } romantic_orientations = { "het": 90, "h**o/bi": 10 } orientation = self.randomizer.get_random_dict_key(main) # Returns "heterosexual" if orientation == "het": return Traits.SEXUAL_ORIENTATIONS_DICT[orientation]["allosexual"] # Returns either "homosexual" or "bisexual" if orientation == "h**o/bi": orientation = self.randomizer.get_random_dict_key(homo_bi) return Traits.SEXUAL_ORIENTATIONS_DICT[orientation]["allosexual"] if orientation == "ace": orientation = self.randomizer.get_random_dict_key( romantic__aromantic) # Returns "aromantic asexual" if orientation == "aromantic": return Traits.SEXUAL_ORIENTATIONS_DICT[orientation] orientation = self.randomizer.get_random_dict_key( romantic_orientations) # Returns "heteroromantic asexual" if orientation == "het": return Traits.SEXUAL_ORIENTATIONS_DICT[orientation]["asexual"] # Returns either "homoromantic asexual" or "biromantic asexual" if orientation == "h**o/bi": orientation = self.randomizer.get_random_dict_key(homo_bi) return Traits.SEXUAL_ORIENTATIONS_DICT[orientation]["asexual"] def get_relationship_orientation(self): options = { Traits.MONOAMOROUS: 90, Traits.POLYAMOROUS: 10 } return self.randomizer.get_random_dict_key(options) def get_marriage_desire(self): options = { True: 60, False: 40 } return self.randomizer.get_random_dict_key(options) def get_liberalism(self): rates = { True: 60, False: 40 } return self.randomizer.get_random_dict_key(rates) def get_desired_num_of_children(self): dbmgr = sql_connect.DatabaseManager("testdb.db") children_data = dbmgr.demo_data("default", "desired_num_of_children") dbmgr.__del__() options = { Traits.ONE_CHILD: children_data[0]['one_child'], Traits.TWO_CHILDREN: children_data[0]['two_children'], Traits.THREE_CHILDREN: children_data[0]['three_children'], Traits.FOUR_CHILDREN: children_data[0]['four_children'] } return self.randomizer.get_random_dict_key(options) def get_pregnancy_num_of_children(self): """Random number of children for pregnancy: singleton/twins/triplets""" dbmgr = sql_connect.DatabaseManager("testdb.db") pregnancy_data = dbmgr.demo_data("default", "pregnancy") dbmgr.__del__() options = { Traits.SINGLETON: pregnancy_data[0]['singlton'], Traits.TWINS: pregnancy_data[0]['twins'], Traits.TRIPLETS: pregnancy_data[0]['triplets'] } return self.randomizer.get_random_dict_key(options) def get_adoption_num_of_children(self): options = { Traits.ONE_CHILD: 70, Traits.SIBLING_SET: 30 } return self.randomizer.get_random_dict_key(options) def get_age_of_adoptive_children(self): options = { "five_or_younger": 46.4, "between_six_and_ten": 27.4, "between_eleven_and_fifteen": 26.1 } selected = self.randomizer.get_random_dict_key(options) if selected == "five_or_younger": return [0, 5] elif selected == "between_six_and_ten": return [6, 10] elif selected == "between_eleven_and_fifteen": return [11, 15] else: raise Exception("Wrong age.") return selected def get_breakup_chance(self): dbmgr = sql_connect.DatabaseManager("testdb.db") breakup_data = dbmgr.demo_data("default", "breakup") dbmgr.__del__() options = { True: breakup_data[0]['breakup_true'], False: breakup_data[0]['breakup_false'] } return self.randomizer.get_random_dict_key(options) def get_intergenerational_chance(self): """Returns true if intergenerational relationship. False otherwise.""" options = { True: 10, False: 90 } return self.randomizer.get_random_dict_key(options) def get_family_love_chance(self): """Returns true if person will fall in love with family member. False otherwise.""" options = { True: 10, False: 90 } return self.randomizer.get_random_dict_key(options) def get_triad_chance(self): options = { True: 30, False: 70 } return self.randomizer.get_random_dict_key(options) def get_drug_addiction_chance(self): dbmgr = sql_connect.DatabaseManager("testdb.db") drug_addiction_data = dbmgr.demo_data("default", "drug_addiction") dbmgr.__del__() options = { True: drug_addiction_data[0]['addict_true'], False: drug_addiction_data[0]['addict_false'] } return self.randomizer.get_random_dict_key(options) def get_alcohol_addiction_chance(self): dbmgr = sql_connect.DatabaseManager("testdb.db") alcohol_addiction_data = dbmgr.demo_data("default", "alcohol_addiction") dbmgr.__del__() options = { True: alcohol_addiction_data[0]['addict_true'], False: alcohol_addiction_data[0]['addict_false'] } return self.randomizer.get_random_dict_key(options) def get_rehabilitation_chance(self): dbmgr = sql_connect.DatabaseManager("testdb.db") rehabilitation_data = dbmgr.demo_data("default", "rehabilitation") dbmgr.__del__() options = { True: rehabilitation_data[0]['rehabilitation_true'], False: rehabilitation_data[0]['rehabilitation_false'] } return self.randomizer.get_random_dict_key(options) def get_overdose_chance(self): dbmgr = sql_connect.DatabaseManager("testdb.db") overdose_data = dbmgr.demo_data("default", "overdose") dbmgr.__del__() options = { True: overdose_data[0]['overdose_true'], False: overdose_data[0]['overdose_false'] } return self.randomizer.get_random_dict_key(options) def get_relapse_chance(self): dbmgr = sql_connect.DatabaseManager("testdb.db") relapse_data = dbmgr.demo_data("default", "relapse") dbmgr.__del__() options = { True: relapse_data[0]['relapse_true'], False: relapse_data[0]['relapse_false'] } return self.randomizer.get_random_dict_key(options) # EARLY / MID / LATE WITHIN RANGE def get_oldest_breakup_date(self, couple): """Returns breakup date for oldest person.""" return self.get_chance_for_early_mid_late(couple.oldest.span_left_till_old_age, 50, 30, 20) def get_oldest_pregnancy_date(self, couple): """Returns pregnancy date for oldest person.""" return self.get_chance_for_early_mid_late(couple.pregnancy_timespan, 70, 20, 10) def get_chance_for_early_mid_late(self, lst, early_num, mid_num, late_num): if len(lst) in range(1, 4): return self.randomizer.get_random_item(lst) lst = list(self.split_list_in_three(lst, 3)) early = lst[0] mid = lst[1] late = lst[2] options = { 1: early_num, 2: mid_num, 3: late_num } selected = self.randomizer.get_random_dict_key(options) if selected == 1: return self.randomizer.get_random_item(early) if selected == 2: return self.randomizer.get_random_item(mid) return self.randomizer.get_random_item(late) @staticmethod def split_list_in_three(lst, n): k, m = divmod(len(lst), n) return (lst[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n))
class CoupleDeveloper: def __init__(self, statistics): self.statistics = statistics self.randomizer = Randomizer() def set_new_couple_traits(self, couple): """Set new couple's goals.""" if len(couple.oldest.span_left_till_old_age) <= 1: # No time left for marriage/breakup if old age. return couple # If couple wants to get married, set random (within the next X-Y years) marriage date. if couple.will_get_married: self.set_marriage_date(couple) # Statistical break up chance. couple.will_breakup = self.statistics.get_breakup_chance() # If couple will break up, set break-up date to each person if couple.will_breakup: self.set_breakup_date(couple) # If couple will have children, set number of desired children and first child pregnancy/adoption date if couple.will_have_children: # Statistical chance of desired number of children couple.desired_num_of_children = self.statistics.get_desired_num_of_children() couple.desired_children_left = couple.desired_num_of_children couple = self.set_new_pregnancy_or_adoption_process_date(couple) self.unique_dates_validation(couple) return couple def set_marriage_date(self, couple): marriable_range = range(1, 8) date = couple.oldest.age + \ self.randomizer.get_random_item(marriable_range) if date not in couple.oldest.span_left_till_old_age: date = self.randomizer.get_random_item( couple.oldest.span_left_till_old_age) couple.marriage_date = date def set_breakup_date(self, couple): """Sets couple's break up date.""" date = couple.marriage_date while date <= couple.marriage_date or date in range(couple.pregnancy_date, couple.birth_date + 2) or date in range( couple.adoption_process_date, couple.adoption_date + 2): date = self.statistics.get_oldest_breakup_date(couple) couple.breakup_date = date def set_new_pregnancy_or_adoption_process_date(self, couple): """Sets pregnancy or adoption date and birth date.""" date = couple.breakup_date while date in range(couple.breakup_date - 1, couple.breakup_date + 2): date = self.statistics.get_oldest_pregnancy_date(couple) if couple.will_get_pregnant: couple.pregnancy_date = date couple.birth_date = date + 1 elif couple.will_adopt: couple.adoption_process_date = date couple.adoption_date = date + 2 else: raise Exception( "Couple is set on having a child but won't get pregnant nor adopt.") self.unique_dates_validation(couple) return couple @classmethod def unique_dates_validation(cls, couple): if couple.will_get_married and couple.marriage_date > 0: if couple.marriage_date not in couple.oldest.span_left_till_old_age: raise Exception( "Marriage date cannot be set outside oldest person's lifetime.") if couple.will_breakup: if couple.breakup_date not in couple.oldest.span_left_till_old_age: raise Exception( "Breakup date cannot be set outside oldest person's lifetime.") if couple.pregnancy_date > 0: if couple.pregnancy_date not in couple.oldest.span_left_till_old_age or \ couple.birth_date not in couple.oldest.span_left_till_old_age: raise Exception( "Pregnancy/birth date cannot be set outside oldest person's lifetime.") if couple.oldest.is_young_adult is False: raise Exception( "Pregnancy/birth date cannot be set if couple is older than young adult.") if couple.adoption_process_date > 0: if couple.adoption_process_date not in couple.oldest.span_left_till_old_age or \ couple.adoption_date not in couple.oldest.span_left_till_old_age: raise Exception( "Adoption process/adoption date cannot be set outside oldest person's lifetime.") if couple.will_breakup and couple.will_get_married: if couple.breakup_date == couple.marriage_date: raise Exception( "Breakup date and marriage date cannot be set at the same time.") if couple.breakup_date > 0 and couple.pregnancy_date > 0: if couple.breakup_date == couple.pregnancy_date or couple.breakup_date == couple.birth_date: raise Exception( "Breakup date and pregnancy/birth date cannot be set at the same time.") if couple.breakup_date > 0 and couple.adoption_process_date > 0: if couple.breakup_date == couple.adoption_process_date or couple.breakup_date == couple.adoption_date: raise Exception( "Breakup date and adoption process/adoption date cannot be set at the same time.")
class Neighborhood: NEIGHBORHOOD_APARTMENTS = 10 def __init__(self, names, baby_generator, person_developer, couple_creator, couple_developer, statistics, foster_care_system): self.households = [] self.neighbors = [] self.neighbor_couples = [] # Essential classes self.names = names self.person_developer = person_developer self.couple_creator = couple_creator self.couple_developer = couple_developer # Handler classes self.randomizer = Randomizer() self.death_handler = DeathHandler(self.person_developer) self.marriage_handler = MarriageHandler() self.divorce_handler = DivorceHandler() self.personal_handler = PersonalHandler(names, person_developer) self.addiction_handler = AddictionHandler(person_developer) self.pregnancy_handler = PregnancyHandler(baby_generator, statistics, foster_care_system) # Automatically create given number of apartments/households self.create_households() @property def all_households_members_lists(self): """Returns all members from all households.""" return [members for h in self.households for members in h.members_list] def create_households(self): """Creates X number of households and adds them to list of households.""" for i, _ in enumerate(range(self.NEIGHBORHOOD_APARTMENTS), 1): household = Household(i) self.households.append(household) def populate_neighborhood(self, city_population, city_couples): """Populate the neighborhood with X number of city inhabitants. Each person and their family are added to each household and to neighbors list.""" self.choose_first_neighbors(city_population, city_couples) self.add_neighbors_families() self.set_neighbor_status() self.neighborhood_validation() def choose_first_neighbors(self, city_population, city_couples): """Add first new random neighbors to each available apartment.""" for i in range(len(self.households)): h = self.households[i] done = False while not done: # Choose a random living person chosen_person = self.randomizer.get_random_item( city_population) # Check that the person isn't already a neighbor, and that they are of age if chosen_person in self.neighbors or not chosen_person.is_of_age: continue # Check that the person isn't a relative from another neighbor if any(chosen_person in n.partners or chosen_person in n. living_family or chosen_person in n.living_inlaws_family for n in self.neighbors): continue # If not, add it to neighbors list and to household's members list self.add_to_neighbors_and_household(h, chosen_person) done = True # Add as couple if applicable for couple in city_couples: if chosen_person in couple.persons: self.neighbor_couples.append(couple) # ADD EACH NEIGHBOR'S FAMILIES def add_neighbors_families(self): """Add each neighbor's families to the household if any.""" for household in self.households: p = household.members[0] self.add_partners(p, household) self.add_children(p, household) if p.is_single_and_unemployed_adult: self.add_parents(p, household) self.add_siblings(p, household) def add_children(self, p, household): """Add underage or unemployed single children.""" for child in p.children: self.add_child(p, child, household) for child in p.adoptive_children: self.add_child(p, child, household) for child in p.step_children: self.add_child(p, child, household) def add_child(self, p, child, household): """Helper method to add person's bio or adoptive children.""" if child.is_alive and child not in household.members: if child.is_single_and_unemployed_adult or not child.is_of_age: self.add_to_neighbors_and_household(household, child) def add_partners(self, p, household): """Add spouse or 1 partner if unmarried""" for spouse in p.spouses: self.add_to_neighbors_and_household(household, spouse) if len(p.spouses) == 0 and len(p.partners) > 0: self.add_to_neighbors_and_household(household, p.partners[0]) def add_parents(self, p, household): """Add parent 1 and their partner(s).""" if len(p.adoptive_parents) > 0 and p.adoptive_parents[0].is_alive: self.add_parent_to_household(p.adoptive_parents[0], household) elif len(p.parents) > 0 and p.parents[0].is_alive: self.add_parent_to_household(p.parents[0], household) def add_parent_to_household(self, parent1, household): """Helper method to add parents to household.""" self.add_to_neighbors_and_household(household, parent1) for parent in parent1.partners: self.add_to_neighbors_and_household(household, parent) def add_siblings(self, p, household): """Add single and unemployed siblings or underage siblings (included adoptive, half and step-siblings).""" for sibling in p.siblings: if sibling not in household.members and ( sibling.is_single_and_unemployed_adult or not sibling.is_of_age): self.add_to_neighbors_and_household(household, sibling) def add_to_neighbors_and_household(self, household, person): """Helper method to add each neighbor to the household and neighbors list.""" person.apartment_id = household.apartment_id self.neighbors.append(person) household.add_member(person) # NEIGHBOR STATUS def set_neighbor_status(self): """Set neighbor status to True for each neighbor.""" for neighbor in self.neighbors: neighbor.is_neighbor = True # DISPLAY HOUSEHOLDS def display_households(self): """Display each household's basic info of its members.""" for household in self.households: household.display() # TIME JUMP def time_jump_neighborhood(self, romanceable_outsiders): """Main function: age up neighborhood.""" self.do_person_action(romanceable_outsiders) # Remove dead couples self.remove_dead_and_brokenup_couples() for couple in self.neighbor_couples: self.do_couple_action(couple) # Set neighbor status for newborns self.set_neighbor_status() # Remove broken-up couples self.remove_dead_and_brokenup_couples() def do_person_action(self, romanceable_outsiders): """Personal actions for each person.""" for person in self.neighbors: # Age up neighborhood self.personal_handler.age_up(person) if person.is_death_date: self.death_handler.die(person) continue # Come out if applicable if person.is_come_out_date: self.personal_handler.come_out(person) # Become an addict if applicable if person.is_addiction_date: self.addiction_handler.become_an_addict(person) # Recover from addiction if applicable if person.is_rehabilitation_date: self.addiction_handler.get_sober(person) # Relapse if applicable if person.is_relapse_date: self.addiction_handler.relapse(person) # Get into a committed relationship if applicable if person.is_romanceable: # Create new couple if successful match couple = self.couple_creator.create_couple( person, romanceable_outsiders) if couple is not False: self.couple_creator.display_new_relationship_message( person, couple) # Set couple traits self.couple_developer.set_new_couple_traits(couple) # Set new love date for polys self.person_developer.set_new_love_date_for_polys(couple) # Add couple to couples list self.neighbor_couples.append(couple) def do_couple_action(self, couple): """Couple actions for each couple.""" # Pregnancy handler first so that baby can be correctly linked to family. if couple.is_birth_date and couple.is_pregnant and couple.expecting_num_of_children >= 1: new_babies = self.pregnancy_handler.give_birth(couple) household = next(h for h in self.households if new_babies[0].apartment_id == h.apartment_id) for baby in new_babies: self.add_to_neighbors_and_household(household, baby) self.pregnancy_handler.reset_pregnancy(couple) if couple.will_have_children: self.couple_developer.set_new_pregnancy_or_adoption_process_date( couple) if couple.is_adoption_date: children = self.pregnancy_handler.adopt(couple) household = next([ h for h in self.households if children[0].apartment_id == h.apartment_id ]) for child in children: self.add_to_neighbors_and_household(household, child) couple = self.pregnancy_handler.reset_adoption(couple) if couple.will_have_children: self.couple_developer.set_new_pregnancy_or_adoption_process_date( couple) if couple.is_marriage_date and couple.will_get_married: self.marriage_handler.get_married(couple) if couple.is_pregnancy_date and couple.will_get_pregnant: self.pregnancy_handler.get_pregnant(couple) if couple.is_adoption_process_date and couple.will_adopt: self.pregnancy_handler.start_adoption_process(couple) if couple.is_breakup_date and couple.will_breakup: self.divorce_handler.get_divorced( couple ) if couple.is_married else self.divorce_handler.get_separated( couple) for person in couple.persons: self.person_developer.set_new_love_date(person) def remove_dead_and_brokenup_couples(self): if len(self.neighbor_couples) > 0: self.neighbor_couples = [ c for c in self.neighbor_couples if all(p.is_alive and p.is_partnered for p in c.persons) ] # VALIDATION def neighborhood_validation(self): # Individual household validation for household in self.households: household.household_validation() # Neighborhood validation if len(self.neighbors) == 0: raise Exception("There are no neighbors.") if len(set(self.neighbor_couples)) != len(self.neighbor_couples): raise Exception("Neighbor couples list contains duplicates.") if len(set(self.neighbors)) != len(self.neighbors): raise Exception("Neighbor list contains duplicates.") if sum(len(h.members_list) for h in self.households) != len(self.neighbors): raise Exception( "Number of neighbors is not the same as the sum of members of all households." ) if any(n.apartment_id not in range(1, self.NEIGHBORHOOD_APARTMENTS + 1) for n in self.neighbors): raise Exception( "Not all neighbors are assigned to an apartment ID.") if any(n not in self.all_households_members_lists for n in self.neighbors): raise Exception("Not all neighbors are members of a household.") if any(n.apartment_id == h.apartment_id and n not in h.members_list for h in self.households for n in self.neighbors): raise Exception( "Neighbor has an apartment ID assigned to them but is not a member of its household." ) if any(n.is_neighbor is False for n in self.neighbors): raise Exception("Not all neighbors have neighbor status.")
class CityCoupleCreator: """Creates new relationships.""" def __init__(self): self.randomizer = Randomizer() self.compatibility = Compatibility() def create_couple(self, person, romanceable_outsiders): """Creates couple if found a match.""" romanceables = [r for r in romanceable_outsiders if r != person] candidates = self.get_candidates_list(person, romanceables) if not self.there_are_candidates(candidates): return False return self.get_new_relationship(person, candidates) def get_candidates_list(self, person, romanceables): """Returns list of compatible individuals or pairs.""" if person.in_love_as_throuple is False: return [candidate for candidate in romanceables if self.compatibility.are_compatible(person, candidate)] pairs = list(combinations(romanceables, 2)) pair_candidates = [] for romanceable in pairs: if self.compatibility.are_compatible(person, romanceable[0], romanceable[1]): pair_candidates.extend([[romanceable[0], romanceable[1]]]) return pair_candidates def get_new_relationship(self, person, candidates): """Returns new relationship.""" found_person2 = None if person.in_love_as_throuple: found_persons = self.get_random_candidate(candidates) found_person1 = found_persons[0] found_person2 = found_persons[1] else: found_person1 = self.get_random_candidate(candidates) self.configure_relationship(person, found_person1, found_person2) return self.create_new_relationship(person, found_person1, found_person2) def configure_relationship(self, person, found_person1, found_person2): """Assign partners, update relationship status and social class.""" lst = [person, found_person1] if found_person2 is None else [person, found_person1, found_person2] self.set_as_partner(lst) self.update_relationship_status_to_committed(lst) @classmethod def there_are_candidates(cls, candidates): """Checks if there is at least one candidate.""" return candidates is not None and len(candidates) >= 1 def get_random_candidate(self, candidates): """Returns one random item from list of candidates.""" return self.randomizer.get_random_item(candidates) @classmethod def set_as_partner(cls, lst): """Add each person to their partners list.""" for person in lst: person.partners += [partner for partner in lst if partner != person] @classmethod def update_relationship_status_to_committed(cls, lst): """Change status from single to committed.""" for person in lst: if person is not None and not person.is_married_or_remarried: person.relationship_status = Traits.COMMITTED @classmethod def create_new_relationship(cls, person1, person2, person3=None): """Create new relationship.""" if person3 is not None: return Throuple(person1, person2, person3) if person1 in person2.living_bio_family: return ConsangCouple(person1, person2) if person1.gender != person2.gender: return StraightCouple(person1, person2) return GayCouple(person1, person2)
def __init__(self): self.randomizer = Randomizer() self.compatibility = Compatibility()
def __init__(self, statistics): self.statistics = statistics self.randomizer = Randomizer()
def __init__(self, setup, life_stages, statistics): self.setup = setup self.stages = life_stages self.statistics = statistics self.randomizer = Randomizer()
class FosterCareSystem: """Foster care system base class.""" def __init__(self, statistics): self.statistics = statistics self.randomizer = Randomizer() self.children = [] @property def children_up_for_adoption(self): """List containing the children in foster care who are within adoptive age range.""" return [ child for child in self.children if child.age <= Traits.MAX_AGE_FOR_ADOPTION ] @property def only_childs(self): return [ child for child in self.children_up_for_adoption if len(child.siblings) == 0 ] @property def sibling_sets(self): return [ child for child in self.children_up_for_adoption if len(child.siblings) > 0 ] def check_foster_care_system(self, living_outsiders): """Removes newly adult children from the system and adds new children to it if requirements are met.""" adults = [child for child in self.children if child.is_of_age] siblings_of_adults = [ sibling for adult in adults for sibling in self.children if sibling in adult.siblings ] # Remove adults and their siblings self.remove_from_system(adults) self.remove_from_system(siblings_of_adults) # Remove dead children dead_children = [ child for child in self.children if child.is_alive is False ] self.remove_from_system(dead_children) # Add new kids new_children = [ child for person in living_outsiders for child in person.children if person.must_place_children_up_for_adoption and child.is_alive and child not in self.children ] if len(new_children) > 0: self.add_to_system(new_children) # Validation self.foster_validation() def add_to_system(self, children): """Adds given children to the list of child up for adoption.""" self.set_was_in_foster_care_status(children) for child in children: if child not in self.children: self.children.append(child) def remove_from_system(self, children): """Removes adopted children or newly-adult children from the system.""" self.children = [ child for child in self.children if child not in children ] @classmethod def set_was_in_foster_care_status(cls, persons): """Set person's was_in_foster_care status to True.""" for p in persons: p.was_in_foster_care = True @classmethod def set_is_adopted_status(cls, persons): """Set person's is_adopted status to True.""" for p in persons: p.is_adopted = True def adopt_child(self, couple): """Returns only child with statistically random age.""" self.check_children_in_foster_care() if len(self.only_childs) == 0: return self.adopt_sibling_set(couple) children_within_range = self.get_children_within_statistical_range( self.only_childs) child = self.randomizer.get_random_item(children_within_range) self.link_adoptive_family(couple, [child]) self.remove_from_system([child]) self.set_is_adopted_status([child]) return [child] def adopt_child_as_single_parent(self, parent): """Returns only child with statistically random age.""" self.check_children_in_foster_care() if len(self.only_childs) == 0: return self.adopt_sibling_set_as_single_parent(parent) children_within_range = self.get_children_within_statistical_range( self.only_childs) child = self.randomizer.get_random_item(children_within_range) self.link_adoptive_single_family(parent, [child]) self.remove_from_system([child]) self.set_is_adopted_status([child]) return [child] def adopt_sibling_set(self, couple): """Returns a set of siblings with statistically random age.""" self.check_children_in_foster_care() if len(self.sibling_sets) == 0: couple.expecting_num_of_children = 1 return self.adopt_child(couple) children_within_range = self.get_children_within_statistical_range( self.sibling_sets) child = self.randomizer.get_random_item(children_within_range) children = [child] + list(child.siblings) # Set expecting num of children for couple now that they know number of siblings couple.expecting_num_of_children = len(children) self.link_adoptive_family(couple, children) self.remove_from_system(children) self.set_is_adopted_status(children) return children def adopt_sibling_set_as_single_parent(self, parent): """Returns a set of siblings with statistically random age, for single parents.""" self.check_children_in_foster_care() if len(self.sibling_sets) == 0: parent.expecting_num_of_children = 1 return self.adopt_child_as_single_parent(parent) children_within_range = self.get_children_within_statistical_range( self.sibling_sets) child = self.randomizer.get_random_item(children_within_range) children = [child] + list(child.siblings) # Set expecting num of children for couple now that they know number of siblings parent.expecting_num_of_children = len(children) self.link_adoptive_single_family(parent, children) self.remove_from_system(children) self.set_is_adopted_status(children) return children def get_children_within_statistical_range(self, lst): """Helper method to get children within statistical age range.""" children_within_range = [] while len(children_within_range) == 0: age_range = self.statistics.get_age_of_adoptive_children() children_within_range = [ child for child in lst if child.age in age_range ] return children_within_range @classmethod def link_adoptive_family(cls, couple, children): """Link adopted children to their adoptive family.""" for child in children: child.adoptive_parents.extend(couple.persons) for parent in children[0].adoptive_parents: parent.adoptive_children.extend(children) # Surname if couple.is_straight: for child in children: child.surname = couple.man.surname else: for child in children: child.surname = child.adoptive_parents[0].surname for child in children: child.original_surname = child.surname @classmethod def link_adoptive_single_family(cls, parent, children): """Link adopted children to their adoptive family, for single parent.""" for child in children: child.adoptive_parents.append(parent) parent.adoptive_children.extend(children) # Surname for child in children: child.surname = parent.surname child.original_surname = child.surname def check_children_in_foster_care(self): if len(self.children_up_for_adoption) == 0: raise Exception("No children in foster care.") def foster_validation(self): if any(child.is_alive is False for child in self.children): raise Exception("Dead children in foster care centre.") if any(child.age >= Traits.YOUNGADULT.start for child in self.children): raise Exception("Adult persons in foster care centre.") if len(set(self.children)) != len(self.children): raise Exception( "List of children in foster care centre contains duplicates.")
def __init__(self): self.randomizer = Randomizer()
class Neighborhood: NEIGHBORHOOD_APARTMENTS = 10 def __init__(self, names, baby_generator, person_developer, couple_creator, couple_developer, statistics, foster_care_system): self.households = [] self.neighbors = [] self.neighbor_couples = [] # Essential classes self.names = names self.person_developer = person_developer self.couple_creator = couple_creator self.couple_developer = couple_developer # Handler classes self.randomizer = Randomizer() self.death_handler = DeathHandler(self.person_developer) self.marriage_handler = MarriageHandler() self.divorce_handler = DivorceHandler() self.personal_handler = PersonalHandler(self.person_developer) self.lgbta_handler = LgbtaHandler(self.names, self.person_developer) self.addiction_handler = AddictionHandler(self.person_developer) self.pregnancy_handler = PregnancyHandler(baby_generator, statistics, foster_care_system) self.career_handler = CareerHandler(statistics) self.conditions_handler = ConditionsHandler() # Automatically create given number of apartments/households self.create_households() @property def all_households_members_lists(self): """Returns all members from all households.""" return [members for h in self.households for members in h.members_list] def create_households(self): """Creates X number of households and adds them to list of households.""" for i, _ in enumerate(range(self.NEIGHBORHOOD_APARTMENTS), 1): household = Household(i) self.households.append(household) def populate_neighborhood(self, city_population, city_couples): """Populate the neighborhood with X number of city inhabitants. Each person and their family are added to each household and to neighbors list.""" self.choose_first_neighbors(city_population, city_couples) self.add_neighbors_families() self.neighborhood_validation() def choose_first_neighbors(self, city_population, city_couples): """Add first new random neighbors to each available apartment.""" for i in range(len(self.households)): h = self.households[i] done = False while not done: invalid = False # Choose a random living person chosen_person = self.randomizer.get_random_item( city_population) # Check that the person isn't already a neighbor, and that they are of age if chosen_person in self.neighbors or not chosen_person.is_of_age: invalid = True # Check that the person isn't a relative from another neighbor for n in self.neighbors: if chosen_person in n.partners or chosen_person in n.living_family or chosen_person in n.living_inlaws_family: invalid = True if not invalid: # If not, add it to neighbors list and to household's members list self.add_to_neighbors_and_household(h, chosen_person) # Add as couple if applicable for couple in city_couples: if chosen_person in couple.persons: self.neighbor_couples.append(couple) done = True # ADD EACH NEIGHBOR'S FAMILIES def add_neighbors_families(self): """Add each neighbor's families to the household if any.""" for household in self.households: p = household.members[0] self.add_partners(p, household) self.add_children(p, household) if p.is_single_and_unemployed_adult: self.add_parents(p, household) self.add_siblings(p, household) def add_children(self, p, household): """Add underage or unemployed single children.""" for child in p.children: self.add_child(p, child, household) for child in p.adoptive_children: self.add_child(p, child, household) for child in p.step_children: self.add_child(p, child, household) def add_child(self, p, child, household): """Helper method to add person's bio or adoptive children.""" if child.is_alive and child not in household.members: if child.is_single_and_unemployed_adult or not child.is_of_age: self.add_to_neighbors_and_household(household, child) def add_partners(self, p, household): """Add spouse or 1 partner if unmarried""" for spouse in p.spouses: self.add_to_neighbors_and_household(household, spouse) if len(p.spouses) == 0 and len(p.partners) > 0: self.add_to_neighbors_and_household(household, p.partners[0]) def add_parents(self, p, household): """Add parent 1 and their partner(s).""" if len(p.adoptive_parents) > 0 and p.adoptive_parents[0].is_alive: self.add_parent_to_household(p.adoptive_parents[0], household) elif len(p.parents) > 0 and p.parents[0].is_alive: self.add_parent_to_household(p.parents[0], household) def add_parent_to_household(self, parent1, household): """Helper method to add parents to household.""" self.add_to_neighbors_and_household(household, parent1) for parent in parent1.partners: self.add_to_neighbors_and_household(household, parent) def add_siblings(self, p, household): """Add single and unemployed siblings or underage siblings (included adoptive, half and step-siblings).""" for sibling in p.siblings: if sibling not in household.members and ( sibling.is_single_and_unemployed_adult or not sibling.is_of_age): self.add_to_neighbors_and_household(household, sibling) def add_to_neighbors_and_household(self, household, person): """Helper method to add each neighbor to the household and neighbors list.""" household.add_member(person) self.neighbors.append(person) # DISPLAY HOUSEHOLDS def display_households(self): """Display each household's basic info of its members.""" for household in self.households: household.display() # TIME JUMP def time_jump_neighborhood(self, romanceable_outsiders): """Age up neighborhood.""" self.do_person_action(romanceable_outsiders) self.do_couple_action() def do_person_action(self, romanceable_outsiders): """Personal actions for each person.""" for person in self.neighbors: # Age up neighborhood self.personal_handler.age_up(person) # Die if person.is_death_date: household = next(h for h in self.households if h.apartment_id == person.apartment_id) self.death_handler.die(person, household) # Remove from household and neighborhood self.remove_from_household(person) self.remove_from_neighborhood(person) # Remove from neighborhood couples if applicable self.remove_dead_and_brokenup_couples() continue # Advance career yearly self.career_handler.check_employment_and_education_status(person) if person.is_autism_date: self.conditions_handler.display_autism_diagnostic_message( person) if person.is_depression_date: self.conditions_handler.display_depression_diagnostic_message( person) if person.is_therapy_date: self.conditions_handler.display_therapy_start_message(person) if person.is_recovery_date_for_depression: self.conditions_handler.recover_from_depression(person) # Move in to new household if applicable if person.is_move_in_date: new_apartment_id = self.personal_handler.move_in(person) self.determine_new_household(person, new_apartment_id) for child in person.underage_children: self.determine_new_household(child, new_apartment_id) # Start school if applicable if person.is_school_start_date: self.career_handler.start_school(person) # Come out if applicable if person.is_come_out_date: self.lgbta_handler.come_out(person) # Get thrown out of the household / neighborhood if person.is_thrown_out_date: new_apartment_id = self.lgbta_handler.get_thrown_out(person) self.determine_new_household(person, new_apartment_id) # Move out of the household / neighborhood if person.is_move_out_date: new_apartment_id = self.lgbta_handler.move_out(person) self.determine_new_household(person, new_apartment_id) # Become an addict if applicable if person.is_addiction_date: self.addiction_handler.become_an_addict(person) # Recover from addiction if applicable if person.is_rehabilitation_date: self.addiction_handler.get_sober(person) # Relapse if applicable if person.is_relapse_date: self.addiction_handler.relapse(person) # Get into a committed relationship if applicable if person.is_romanceable: # Create new couple if successful match couple = self.couple_creator.create_couple( person, romanceable_outsiders) if couple is not False: self.couple_creator.display_new_relationship_message( person, couple) # Set couple traits self.couple_developer.set_new_couples_goals(couple) # Set new love date for polys self.person_developer.set_new_love_date_for_polys(couple) # Add couple to couples list self.neighbor_couples.append(couple) # Single adoption if person.is_single_adoption_process_date: self.pregnancy_handler.start_single_adoption_process(person) if person.is_single_adoption_date: children = self.pregnancy_handler.adopt_as_single(person) self.handle_new_babies_for_single(children, person) def do_couple_action(self): """Couple actions for each couple.""" for couple in self.neighbor_couples: # Birth if couple.is_birth_date and couple.is_pregnant and couple.expecting_num_of_children >= 1: new_babies = self.pregnancy_handler.give_birth(couple) self.handle_new_babies(new_babies, couple) # Adoption if couple.is_adoption_date: children = self.pregnancy_handler.adopt(couple) self.handle_new_babies(children, couple) # Marriage if couple.is_marriage_date and couple.will_get_married: self.marriage_handler.get_married(couple) # Pregnancy if couple.is_pregnancy_date and couple.will_get_pregnant: self.pregnancy_handler.get_pregnant(couple) # Adoption process if couple.is_adoption_process_date and couple.will_adopt: self.pregnancy_handler.start_adoption_process(couple) # Breakup if couple.is_breakup_date and couple.will_breakup: if couple.is_married: self.divorce_handler.get_divorced(couple) else: self.divorce_handler.get_separated(couple) # One person in couple will leave household / neighborhood d = self.divorce_handler.leave_household(couple) # Leaving divorced person's children will also leave household. if len(d["person"].children) > 0: children_in_household = [ p for p in d["person"].children if p.move_in_date > 0 and p.apartment_id == p.apartment_id ] for child in children_in_household: self.determine_new_household(child, d["id"]) # Leaving divorced person leaves household self.determine_new_household(d["person"], d["id"]) # New love dates for all for person in couple.persons: self.person_developer.set_new_love_date(person) # Remove from neighborhood couples self.remove_dead_and_brokenup_couples() def determine_new_household(self, person, new_apartment_id=None): """Remove person from household and may add them to new household or move out of neighborhood.""" self.remove_from_household(person) if new_apartment_id is None: self.remove_from_neighborhood(person) else: new_household = next(h for h in self.households if h.apartment_id == new_apartment_id) new_household.add_member(person) # Add to neighbors list if not in it if person not in self.neighbors: self.neighbors.append(person) def remove_from_household(self, person): """Helper method to remove person from their household.""" for h in self.households: if h.apartment_id == person.apartment_id: h.remove_member(person) def remove_from_neighborhood(self, person): """Helper method to remove person from the neighborhood.""" self.neighbors = [n for n in self.neighbors if n != person] def handle_new_babies(self, new_babies, couple): """Add baby/babies to household and neighborhood.""" household = [ h for h in self.households if couple.person1.apartment_id == h.apartment_id ] for baby in new_babies: self.add_to_neighbors_and_household(household[0], baby) # Reset vars if couple.is_pregnant: self.pregnancy_handler.reset_pregnancy(couple) elif couple.is_in_adoption_process: self.pregnancy_handler.reset_adoption(couple) # New pregnancy / adoption date if couple.will_have_children: self.couple_developer.set_new_pregnancy_or_adoption_process_date( couple) def handle_new_babies_for_single(self, new_babies, parent): """Add baby/babies to household and neighborhood, for single parent.""" household = [ h for h in self.households if parent.apartment_id == h.apartment_id ] for baby in new_babies: self.add_to_neighbors_and_household(household[0], baby) # Reset vars if parent.is_in_adoption_process: self.pregnancy_handler.reset_single_adoption(parent) # New adoption date self.person_developer.set_single_adoption(parent) def remove_dead_and_brokenup_couples(self): """Remove divorced/separated couples and couples who contain dead persons.""" if len(self.neighbor_couples) > 0: self.neighbor_couples = [ c for c in self.neighbor_couples if all(p.is_alive and p.is_partnered and p.is_neighbor for p in c.persons) ] def neighborhood_validation(self): """Error handling.""" # Individual household validation for household in self.households: household.household_validation() # Neighborhood validation if len(self.neighbors) == 0: raise Exception("There are no neighbors.") if len(set(self.neighbor_couples)) != len(self.neighbor_couples): raise Exception("Neighbor couples list contains duplicates.") if len(set(self.neighbors)) != len(self.neighbors): raise Exception("Neighbor list contains duplicates.") if sum(len(h.members_list) for h in self.households) != len(self.neighbors): raise Exception( "Number of neighbors is not the same as the sum of members of all households." ) if any(n.apartment_id not in range(1, self.NEIGHBORHOOD_APARTMENTS + 1) for n in self.neighbors): raise Exception( "Not all neighbors are assigned to an apartment ID.") if any(n not in self.all_households_members_lists for n in self.neighbors): raise Exception("Not all neighbors are members of a household.") if any(n.apartment_id == h.apartment_id and n not in h.members_list for h in self.households for n in self.neighbors): raise Exception( "Neighbor has an apartment ID assigned to them but is not a member of its household." ) if any(n.is_neighbor is False for n in self.neighbors): raise Exception("Not all neighbors have neighbor status.")
class FosterCareSystem: def __init__(self, statistics): self.statistics = statistics self.randomizer = Randomizer() self.children = [] @property def only_childs(self): return [child for child in self.children if len(child.siblings) == 0] @property def sibling_sets(self): return [child for child in self.children if len(child.siblings) > 0] def check_foster_care_system(self, living_outsiders): """Removes newly adult children from the system and adds new children to it if requirements are met.""" adults = [child for child in self.children if child.is_of_age] siblings_of_adults = [ sibling for adult in adults for sibling in self.children if sibling in adult.siblings ] # Remove adults and their siblings self.remove_from_system(adults) self.remove_from_system(siblings_of_adults) # Add new kids new_children = [ child for person in living_outsiders for child in person.children if person.must_place_children_up_for_adoption and child not in self.children ] self.add_to_system(new_children) def add_to_system(self, children): """Adds given children to the list of child up for adoption.""" self.children.extend(children) def remove_from_system(self, children): """Removes adopted children or newly-adult children from the system.""" self.children = [ child for child in self.children if child not in children ] def adopt_child(self, couple): """Returns only child with statistically random age.""" if len(self.children) == 0: raise Exception("No children in foster care.") age_range = self.statistics.get_age_of_adoptive_children() children_within_range = [ child for child in self.only_childs if child.age in age_range ] child = self.randomizer.get_random_item(children_within_range) self.link_adoptive_family(couple, child) self.remove_from_system(child) return child def adopt_sibling_set(self, couple): """Returns a set of siblings with statistically random age.""" if len(self.children) == 0: raise Exception("No children in foster care.") age_range = self.statistics.get_age_of_adoptive_children() children_within_range = [ child for child in self.sibling_sets if child.age in age_range ] child = self.randomizer.get_random_item(children_within_range) children = [child] + child.siblings self.link_adoptive_family(couple, children) self.remove_from_system(children) return children def link_adoptive_family(self, couple, children): """Link adopted children to their adoptive family.""" for child in children: child.adoptive_parents.extend(couple.persons) child.social_class = child.adoptive_parents[0].social_class for parent in children[0].adoptive_parents: parent.adoptive_children.extend(children) if couple.is_straight: for child in children: child.surname = couple.man.surname child.apartment_id = couple.woman.apartment_id else: for child in children: child.surname = child.adoptive_parents[0].surname child.apartment_id = child.adoptive_parents[0].apartment_id for child in children: child.original_surname = child.surname
def __init__(self, city_data): self.city_data = city_data self.randomizer = Randomizer()
class Statistics: """Statistics base class.""" def __init__(self, city_data): self.city_data = city_data self.randomizer = Randomizer() def get_gender(self): """Statistical chance for gender.""" options = {Traits.MALE: 50, Traits.FEMALE: 50} selected = self.randomizer.get_random_dict_key(options) self.validate_selected(selected, Traits.GENDERS) return selected def get_gender_identity(self): """Statistical chance for gender identity.""" # Link: https://en.wikipedia.org/wiki/Transgender options = {Traits.CISGENDER: 99.4, Traits.TRANSGENDER: 0.6} selected = self.randomizer.get_random_dict_key(options) self.validate_selected(selected, Traits.GENDER_IDENTITIES) return selected def get_race(self): """Statistical chance for race.""" # Link: https://www.census.gov/quickfacts/fact/table/US/PST045216 dbmgr = sql_connect.DatabaseManager("testdb.db") race_data = dbmgr.demo_data(self.city_data, "race") dbmgr.__del__() options = { Traits.WHITE: race_data[0]['white'], Traits.BLACK: race_data[0]['black'], Traits.LATINO: race_data[0]['latino'], Traits.ASIAN: race_data[0]['asian'] } selected = self.randomizer.get_random_dict_key(options) self.validate_selected(selected, Traits.RACES) return selected def get_interracial_love_chance(self): """Statistical chance for interracial love.""" # Link: https://en.wikipedia.org/wiki/Interracial_marriage_in_the_United_States options = {True: 15.1, False: 84.9} return self.randomizer.get_random_dict_key(options) def get_autistic_disorder_chance(self, baby): """Statistical chance of baby developing autism.""" # Link: https://www.autismspeaks.org/what-autism/prevalence options_for_boy = {True: 2.3, False: 97.7} options_for_girl = {True: 0.5, False: 99.5} if baby.is_male: return self.randomizer.get_random_dict_key(options_for_boy) else: return self.randomizer.get_random_dict_key(options_for_girl) def get_social_class(self): """Statistical chance for social class.""" dbmgr = sql_connect.DatabaseManager("testdb.db") social_class_data = dbmgr.demo_data(self.city_data, "social_class") dbmgr.__del__() options = { Traits.UPPER_CLASS: social_class_data[0]['lower_class'], Traits.MIDDLE_CLASS: social_class_data[0]['middle_class'], Traits.LOWER_CLASS: social_class_data[0]['upper_class'] } selected = self.randomizer.get_random_dict_key(options) self.validate_selected(selected, Traits.SOCIAL_CLASSES) return selected def get_suicide_chance_as_coming_out_consequence(self): """Statistical chance of suicide after coming out in conservative family.""" options = {True: 10, False: 90} return self.randomizer.get_random_dict_key(options) def get_suicide_chance_as_depression_consequence(self): """Statistical chance of suicide for a depressed person.""" # Link: http://www.allaboutdepression.com/gen_04.html options = {True: 15, False: 85} return self.randomizer.get_random_dict_key(options) def get_thrown_out_chance(self): """Statistical chance of being thrown out after coming out in conservative family.""" options = {True: 20, False: 80} return self.randomizer.get_random_dict_key(options) def get_employment_chance(self): """Statistical chance of employment.""" # Link: https://tradingeconomics.com/united-states/unemployment-rate # To insert into DB: Employed: 95.9. Unemployed: 4.1. dbmgr = sql_connect.DatabaseManager("testdb.db") employment_data = dbmgr.demo_data(self.city_data, "employment") dbmgr.__del__() options = { Traits.EMPLOYED: employment_data[0]['employed'], Traits.UNEMPLOYED: employment_data[0]['unemployed'] } selected = self.randomizer.get_random_dict_key(options) self.validate_selected(selected, Traits.EMPLOYMENT) return selected def get_chance_for_getting_bachelor_degree(self): """Statistical chance of getting a bachelor degree.""" # Link: http://thehill.com/homenews/state-watch/326995-census-more-americans-have-college-degrees-than-ever-before options = {True: 33.4, False: 66.6} return self.randomizer.get_random_dict_key(options) def get_chance_for_getting_master_degree(self): """Statistical chance of getting a master's degree.""" # Link: http://thehill.com/homenews/state-watch/326995-census-more-americans-have-college-degrees-than-ever-before options = {True: 9.3, False: 90.7} return self.randomizer.get_random_dict_key(options) def get_chance_for_getting_doctor_degree(self): """Statistical chance of getting a doctor's degree.""" # Link: http://thehill.com/homenews/state-watch/326995-census-more-americans-have-college-degrees-than-ever-before options = {True: 2, False: 98} return self.randomizer.get_random_dict_key(options) def get_death_cause(self, person): """Statistical chance for death cause.""" dbmgr = sql_connect.DatabaseManager("testdb.db") death_cause_data = dbmgr.demo_data(self.city_data, "death_cause") dbmgr.__del__() # If death date is False = Old Age if person.death_date is False: return Traits.OLD_AGE # If baby or child = Illness if person.death_date < Traits.TEEN.start: return Traits.ILLNESS options_teen = { Traits.ILLNESS: death_cause_data[0]['teen_illness'], Traits.SUICIDE: death_cause_data[0]['teen_suicide'] } options_young_adult = { Traits.ILLNESS: death_cause_data[0]['young_adult_illness'], Traits.SUICIDE: death_cause_data[0]['young_adult_suicide'], Traits.ACCIDENT: death_cause_data[0]['young_adult_accident'] } options_adult = { Traits.ILLNESS: death_cause_data[0]['adult_illness'], Traits.SUICIDE: death_cause_data[0]['adult_suicide'], Traits.ACCIDENT: death_cause_data[0]['adult_accident'] } options_senior = { Traits.ILLNESS: death_cause_data[0]['senior_illness'], Traits.SUICIDE: death_cause_data[0]['senior_suicide'], Traits.ACCIDENT: death_cause_data[0]['senior_accident'] } if person.death_date in Traits.TEEN.span: selected = self.randomizer.get_random_dict_key(options_teen) elif person.death_date in Traits.YOUNGADULT.span: selected = self.randomizer.get_random_dict_key(options_young_adult) elif person.death_date in Traits.ADULT.span: selected = self.randomizer.get_random_dict_key(options_adult) elif person.death_date in Traits.SENIOR.span: selected = self.randomizer.get_random_dict_key(options_senior) else: raise Exception("Wrong death date.") self.validate_selected(selected, Traits.DEATH_CAUSES) return selected def get_death_date(self): """Statistical chance for death date.""" options_general = {"before_old_age": 50, "old_age": 50} options_before_old_age = { Traits.BABY: 1, Traits.CHILD: 2, Traits.TEEN: 3, Traits.YOUNGADULT: 4, Traits.ADULT: 10, Traits.SENIOR: 80 } selected = self.randomizer.get_random_dict_key(options_general) if selected == "old_age": return False random_life_stage = self.randomizer.get_random_dict_key( options_before_old_age) death_date = self.randomizer.get_random_item(random_life_stage.span) self.validate_selected(death_date, Traits.LIFESPAN) return death_date def get_fertility(self): """Statistical chance of being infertile.""" # Link: https://www.womenshealth.gov/a-z-topics/infertility options = {True: 90, False: 10} return self.randomizer.get_random_dict_key(options) def get_domestic_partnership_desire(self): """Statistical chance for domestic partnership wish.""" options = {True: 90, False: 10} return self.randomizer.get_random_dict_key(options) def get_children_desire(self): """Statistical chance for children wish.""" options = {True: 70, False: 30} return self.randomizer.get_random_dict_key(options) def get_sexual_orientation(self): """Statistical chance for sexual orientation.""" main = {"het": 90, "h**o/bi": 8, "ace": 2} homo_bi = {"h**o": 40, "bi": 60} romantic__aromantic = {"aromantic": 50, "romantic": 50} romantic_orientations = {"het": 90, "h**o/bi": 10} orientation = self.randomizer.get_random_dict_key(main) # Returns "heterosexual" if orientation == "het": return Traits.SEXUAL_ORIENTATIONS_DICT[orientation]["allosexual"] # Returns either "homosexual" or "bisexual" if orientation == "h**o/bi": orientation = self.randomizer.get_random_dict_key(homo_bi) return Traits.SEXUAL_ORIENTATIONS_DICT[orientation]["allosexual"] # Returns [romantic orientation] + "asexual" if orientation == "ace": orientation = self.randomizer.get_random_dict_key( romantic__aromantic) # Returns "aromantic asexual" if orientation == "aromantic": return Traits.SEXUAL_ORIENTATIONS_DICT[orientation] orientation = self.randomizer.get_random_dict_key( romantic_orientations) # Returns "heteroromantic asexual" if orientation == "het": return Traits.SEXUAL_ORIENTATIONS_DICT[orientation]["asexual"] # Returns either "homoromantic asexual" or "biromantic asexual" if orientation == "h**o/bi": orientation = self.randomizer.get_random_dict_key(homo_bi) return Traits.SEXUAL_ORIENTATIONS_DICT[orientation]["asexual"] def get_relationship_orientation(self): """Statistical chance for relationship orientation.""" options = {Traits.MONOAMOROUS: 90, Traits.POLYAMOROUS: 10} return self.randomizer.get_random_dict_key(options) def get_marriage_desire(self): """Statistical chance for marriage wish.""" # Link: http://www.abc.net.au/news/2017-04-26/more-people-than-ever-are-single-and-thats-a-good-thing/8473398 options = {True: 75, False: 25} return self.randomizer.get_random_dict_key(options) def get_liberalism(self): """Statistical chance for liberal/conservative ideology.""" # Link: https://www.theblaze.com/news/2018/01/12/poll-gap-between-liberal-and-conservative-steadily-narrowing-among-american-adults options = {True: 42.6, False: 57.3} return self.randomizer.get_random_dict_key(options) def willing_to_move_outside(self): """Statistical of family willing to move outside of neighborhood""" options = {True: 50, False: 50} return self.randomizer.get_random_dict_key(options) def willing_to_move_back(self): """Statistical of family willing to move back to inside of neighborhood""" options = {True: 50, False: 50} return self.randomizer.get_random_dict_key(options) def get_desired_num_of_children(self): """Statistical chance for desired number of children per couple.""" dbmgr = sql_connect.DatabaseManager("testdb.db") children_data = dbmgr.demo_data(self.city_data, "desired_num_of_children") dbmgr.__del__() options = { Traits.ONE_CHILD: children_data[0]['one_child'], Traits.TWO_CHILDREN: children_data[0]['two_children'], Traits.THREE_CHILDREN: children_data[0]['three_children'], Traits.FOUR_CHILDREN: children_data[0]['four_children'] } selected = self.randomizer.get_random_dict_key(options) self.validate_selected(selected, Traits.ALLOWED_NUM_OF_CHILDREN_PER_COUPLE) return selected def get_pregnancy_num_of_children(self): """Statistical chance of having singleton/twins/triplets.""" # Link: https://en.wikipedia.org/wiki/Multiple_birth dbmgr = sql_connect.DatabaseManager("testdb.db") pregnancy_data = dbmgr.demo_data(self.city_data, "pregnancy") dbmgr.__del__() options = { Traits.SINGLETON: pregnancy_data[0]['singlton'], Traits.TWINS: pregnancy_data[0]['twins'], Traits.TRIPLETS: pregnancy_data[0]['triplets'] } selected = self.randomizer.get_random_dict_key(options) self.validate_selected(selected, Traits.ALLOWED_NUM_OF_CHILDREN_PER_PREGNANCY) return selected def get_adoption_num_of_children(self): """Statistical chance of adopting one child or a sibling set.""" options = {Traits.ONE_CHILD: 70, Traits.SIBLING_SET: 30} selected = self.randomizer.get_random_dict_key(options) self.validate_selected(selected, Traits.ALLOWED_NUM_OF_ADOPTIONS_PER_COUPLE) return self.randomizer.get_random_dict_key(options) def get_age_of_adoptive_children(self): """Statistical chance for age of child in adoption.""" options = {5: 46.4, 10: 27.4, Traits.MAX_AGE_FOR_ADOPTION: 26.1} selected = self.randomizer.get_random_dict_key(options) if selected == 5: return list(range(0, 6)) elif selected == 10: return list(range(6, 11)) elif selected == Traits.MAX_AGE_FOR_ADOPTION: return list(range(11, Traits.MAX_AGE_FOR_ADOPTION + 1)) else: raise Exception("Wrong age.") def get_breakup_chance(self, couple): """Statistical chance of breaking up.""" # Link: http://stories.avvo.com/relationships/divorce/numbers-breakdown-divorce-generation.html # Link: https://psychcentral.com/blog/is-my-marriage-doomed-if-my-parents-got-divorced-when-i-was-a-kid/ # Insert to DB: True: 52.7. False: 48.3. If divorced parents: True: 79. False: 21. dbmgr = sql_connect.DatabaseManager("testdb.db") breakup_data = dbmgr.demo_data(self.city_data, "breakup") dbmgr.__del__() options_for_married_parents = { True: breakup_data[0]['breakup_true'], False: breakup_data[0]['breakup_false'] } options_for_divorced_parents = {True: 79, False: 21} if all(p.has_divorced_parents for p in couple.persons): return self.randomizer.get_random_dict_key( options_for_divorced_parents) return self.randomizer.get_random_dict_key(options_for_married_parents) def get_couple_reconciliation_chance(self): """Statistical chance for a separated couple to get back together.""" # Link: https://www.quora.com/How-common-is-it-for-divorced-couples-to-get-back-together options = {True: 6, False: 94} return self.randomizer.get_random_dict_key(options) def get_intergenerational_chance(self): """Statistical chance for intergenerational relationship.""" options = {True: 10, False: 90} return self.randomizer.get_random_dict_key(options) def get_family_love_chance(self): """Statistical chance for consanguinamorous relationship.""" options = {True: 10, False: 90} return self.randomizer.get_random_dict_key(options) def get_triad_chance(self): """Statistical chance for triads / throuples.""" options = {True: 30, False: 70} return self.randomizer.get_random_dict_key(options) def get_drug_addiction_chance(self): """Statistical chance for drug addiction.""" dbmgr = sql_connect.DatabaseManager("testdb.db") drug_addiction_data = dbmgr.demo_data(self.city_data, "drug_addiction") dbmgr.__del__() options = { True: drug_addiction_data[0]['addict_true'], False: drug_addiction_data[0]['addict_false'] } return self.randomizer.get_random_dict_key(options) def get_alcohol_addiction_chance(self): """Statistical chance for alcohol addiction.""" dbmgr = sql_connect.DatabaseManager("testdb.db") alcohol_addiction_data = dbmgr.demo_data(self.city_data, "alcohol_addiction") dbmgr.__del__() options = { True: alcohol_addiction_data[0]['addict_true'], False: alcohol_addiction_data[0]['addict_false'] } return self.randomizer.get_random_dict_key(options) def get_rehabilitation_chance(self): """Statistical chance of going through rehabilitation for addiction.""" dbmgr = sql_connect.DatabaseManager("testdb.db") rehabilitation_data = dbmgr.demo_data(self.city_data, "rehabilitation") dbmgr.__del__() options = { True: rehabilitation_data[0]['rehabilitation_true'], False: rehabilitation_data[0]['rehabilitation_false'] } return self.randomizer.get_random_dict_key(options) def get_overdose_chance(self): """Statistical chance of overdosing.""" dbmgr = sql_connect.DatabaseManager("testdb.db") overdose_data = dbmgr.demo_data(self.city_data, "overdose") dbmgr.__del__() options = { True: overdose_data[0]['overdose_true'], False: overdose_data[0]['overdose_false'] } return self.randomizer.get_random_dict_key(options) def get_relapse_chance(self): """Statistical chance of relapsing""" dbmgr = sql_connect.DatabaseManager("testdb.db") relapse_data = dbmgr.demo_data(self.city_data, "relapse") dbmgr.__del__() options = { True: relapse_data[0]['relapse_true'], False: relapse_data[0]['relapse_false'] } return self.randomizer.get_random_dict_key(options) def get_depression_chance(self): """Statistical chance of suffering from depression.""" options = {True: 33.3, False: 66.7} return self.randomizer.get_random_dict_key(options) def get_therapy_chance(self): """Statistical chance of going to therapy.""" options = {True: 33.3, False: 66.7} return self.randomizer.get_random_dict_key(options) def get_recovery_chance(self): """Statistical chance of recovering with therapy.""" options = {True: 50, False: 50} return self.randomizer.get_random_dict_key(options) # EARLY / MID / LATE WITHIN RANGE def get_oldest_breakup_date(self, couple): """Returns statistical breakup date for oldest person in couple.""" early = 50 mid = 30 late = 20 return self.get_chance_for_early_mid_late( couple.oldest.span_left_till_old_age, early, mid, late) def get_oldest_pregnancy_date(self, couple): """Returns statistical pregnancy date for oldest person in couple.""" early = 70 mid = 20 late = 10 return self.get_chance_for_early_mid_late( couple.having_children_timespan, early, mid, late) def get_chance_for_early_mid_late(self, lst, early_num, mid_num, late_num): """Helper method for determining statistical chances within early-mid-late range.""" if len(lst) == 0: raise Exception("List is empty.") if len(lst) in range(1, 4): return self.randomizer.get_random_item(lst) lst = list(self.split_list_in_three(lst, 3)) early = lst[0] mid = lst[1] late = lst[2] options = {1: early_num, 2: mid_num, 3: late_num} selected = self.randomizer.get_random_dict_key(options) if selected == 1: return self.randomizer.get_random_item(early) if selected == 2: return self.randomizer.get_random_item(mid) return self.randomizer.get_random_item(late) @staticmethod def split_list_in_three(lst, n): k, m = divmod(len(lst), n) return (lst[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)) @classmethod def validate_selected(cls, selected, lst): if selected not in lst: raise Exception("Statistically selected value is not valid.")
def __init__(self, stages): self.stages = stages self.randomizer = Randomizer()
class Education: """Education base class.""" SCHOOL_START_DATE = 6 BACHELOR_START_DATE = 18 MASTER_START_DATE = 24 DOCTOR_START_DATE = 26 SCHOOL_NUM_OF_YEARS = (12, 12) BACHELOR_NUM_OF_YEARS = (4, 4) MASTER_NUM_OF_YEARS = (2, 2) DOCTOR_NUM_OF_YEARS = (6, 10) YEARS_TO_COMPLETE = [0, SCHOOL_NUM_OF_YEARS, BACHELOR_NUM_OF_YEARS, MASTER_NUM_OF_YEARS, DOCTOR_NUM_OF_YEARS] FAILING_CLASS_MAX = 3 # Degrees UNEDUCATED = 0 SCHOOL = 1 BACHELOR = 2 MASTER = 3 DOCTOR = 4 LITERAL_DEGREES = ['Uneducated', 'High School Diploma', 'Bachelor Degree', 'Master Degree', 'Doctoral Degree'] # External Factor. Value range from 0 to 1 DRUG_ADDICTION_EFFECT = 0.25 ALCOHOL_ADDICTION_EFFECT = 0.25 CHANCE_OF_BAD_DECISION = 0.02 def __init__(self): self.available_degree = self.SCHOOL self.acquired_degree = [self.UNEDUCATED] self.in_study = False self.current_year = 0 self.years_to_complete_degree = 0 self.total_fail = 0 self.randomizer = Randomizer() def __str__(self): return self.LITERAL_DEGREES[self.acquired_degree[-1]] @property def current_degree(self): """Returns person's current degree level.""" return self.acquired_degree[-1] def start_degree(self, degree): """Starts new degree.""" start = self.YEARS_TO_COMPLETE[degree][0] end = self.YEARS_TO_COMPLETE[degree][1] self.years_to_complete_degree = self.randomizer.get_random_number(start, end) self.in_study = True self.total_fail = 0 self.current_year = 0 def advance_degree(self, is_drug_addict=False, is_alcohol_addict=False): """Advance degree.""" chance_to_fail = self.DRUG_ADDICTION_EFFECT * is_drug_addict + self.ALCOHOL_ADDICTION_EFFECT * is_alcohol_addict + self.CHANCE_OF_BAD_DECISION chance_to_success = 100 - (chance_to_fail * 100) if self.randomizer.get_random_number(0, 100) <= chance_to_success: if self.current_year != self.years_to_complete_degree: self.current_year += 1 else: self.acquire_degree() if self.current_degree + 1 != self.DOCTOR: self.available_degree = self.current_degree + 1 else: if self.current_degree + 1 == self.DOCTOR: return self.total_fail += 1 if self.total_fail > self.FAILING_CLASS_MAX: self.fail_out() def acquire_degree(self): """Append new obtained degree and finish studies.""" self.current_year = 0 self.in_study = False self.total_fail = 0 self.acquired_degree.append(self.current_degree + 1) def fail_out(self): """Fail out of current degree.""" self.in_study = False self.total_fail = 0 self.current_year = 0 def init_degree(self, person): """Initialize education for non-natural born persons.""" if person.age < self.SCHOOL_START_DATE: return False elif person.age < self.BACHELOR_START_DATE: self.start_degree(self.SCHOOL) self.years_to_complete_degree = self.SCHOOL_NUM_OF_YEARS[0] - (person.age - 6) elif person.age >= self.DOCTOR_START_DATE and person.will_do_doctor: self.acquired_degree.append(self.MASTER) self.start_degree(self.DOCTOR) self.years_to_complete_degree -= self.randomizer.get_random_number(1, person.age - 25) elif person.age >= self.MASTER_START_DATE and person.will_do_master: self.acquired_degree.append(self.BACHELOR) self.start_degree(self.MASTER) self.years_to_complete_degree -= self.randomizer.get_random_number(0, 1) elif person.will_do_bachelor: self.acquired_degree.append(self.SCHOOL) self.start_degree(self.SCHOOL) self.years_to_complete_degree -= self.randomizer.get_random_number(0, 3) else: self.acquired_degree.append(self.SCHOOL) self.in_study = False self.years_to_complete_degree = 0
class PersonDeveloper: def __init__(self, setup, life_stages, statistics): self.setup = setup self.stages = life_stages self.statistics = statistics self.randomizer = Randomizer() def set_new_stage_traits(self, person): """Link each new stage to their methods for setting new traits.""" if person.stage == self.stages.BABY or person.stage == self.stages.CHILD: pass elif person.stage == self.stages.TEEN: person = self.set_teen_traits(person) elif person.stage == self.stages.ADULT: person = self.set_adult_traits(person) elif person.stage == self.stages.YOUNGADULT: person = self.set_youngadult_traits(person) elif person.stage == self.stages.SENIOR: person = self.set_senior_traits(person) else: raise Exception("Person's stage is wrong.") return person def set_teen_traits(self, teen): "Teen traits." teen.gender_identity = self.statistics.get_gender_identity( ) # Gender identity must be set first teen.sexual_orientation = self.statistics.get_sexual_orientation() teen.target_gender = [ gender for gender in self.get_target_gender(teen) ] # Set date to come out if LGBTA if teen.is_lgbta: teen.come_out_date = self.randomizer.get_random_item( teen.span_left_till_next_stage) return teen @classmethod def get_target_gender(cls, teen): """Returns target gender(s) based on sexual orientation logic.""" if teen.is_male or (teen.is_female and teen.is_trans): same = Traits.MALE opposite = Traits.FEMALE elif teen.is_female or (teen.is_male and teen.is_trans): same = Traits.FEMALE opposite = Traits.MALE if teen.sexual_orientation in [ Traits.HETEROSEXUAL, Traits.HETEROROMANTIC_ASEXUAL ]: yield opposite if teen.sexual_orientation in [ Traits.HOMOSEXUAL, Traits.HOMOROMANTIC_ASEXUAL ]: yield same if teen.sexual_orientation in [ Traits.BISEXUAL, Traits.BIROMANTIC_ASEXUAL ]: yield same yield opposite if teen.sexual_orientation == Traits.AROMANTIC_ASEXUAL: yield None def set_youngadult_traits(self, person): """Young adult traits.""" person.occupation = self.randomizer.get_random_item( self.setup.PROFESSIONS) person.employment = self.statistics.get_employment_chance() # Set relationship orientation (mono/poly) person.relationship_orientation = self.statistics.get_relationship_orientation( ) # Set (initial) liberalism if person.is_lgbta or person.is_poly: person.is_liberal = True else: person.is_liberal = self.statistics.get_liberalism() # Cannot have biological children if LGTA if person.is_lgbta and not person.is_bi: person.can_have_bio_children = False # Set relationship-oriented traits self.set_love_traits(person) # Set chance of drug/alcohol addiction self.set_addiction_traits(person) return person @classmethod def set_adult_traits(cls, person): """Adult traits.""" person.can_have_bio_children = False return person @classmethod def set_senior_traits(cls, person): """Senior traits.""" person.employment = Traits.RETIRED return person # LOVE def set_love_traits(self, person): """Returns person with statistical / random traits for wish for romance / marriage / children. Chance for family or intergenerational love. Sets date to fall in love if applicable.""" if len(person.span_left_till_old_age) <= 1: return person if person.is_poly: person.in_love_as_throuple = self.statistics.get_triad_chance() if person.is_liberal: person.wants_domestic_partnership = self.statistics.get_domestic_partnership_desire( ) person.wants_children = self.statistics.get_children_desire() else: self.set_conservative_traits(person) if person.wants_domestic_partnership: person.wants_marriage = self.statistics.get_marriage_desire() self.set_new_love_date(person) else: self.set_aromantic_traits(person) def set_new_love_date(self, person): """Sets in_love_date within person's age and X. Chance of intergenerational / family love.""" if self.is_family_love_a_possibility(person): person.in_love_with_family = self.statistics.get_family_love_chance( ) if person.in_love_with_family: self.set_family_love_traits(person) else: person.in_love_with_intergenerational = self.statistics.get_intergenerational_chance( ) if person.in_love_with_intergenerational: person.is_liberal = True # Assign date to fall in love. Ex: within 10 years. # If person will be dead before then, just loop through their remaining years. if len(person.span_left_till_old_age) < 10: person.in_love_date = self.randomizer.get_random_item( person.span_left_till_old_age) else: person.in_love_date = self.randomizer.get_random_item( range(person.age, person.age + 11)) def set_new_love_date_for_polys(self, couple): """After couple creation, set new future love date for each poly person from couple if any.""" for person in couple.persons: if person.is_romanceable: self.set_new_love_date(person) return couple @classmethod def set_conservative_traits(cls, person): """Wants marriage and children if conservative.""" person.wants_domestic_partnership = True person.wants_marriage = True person.wants_children = True @classmethod def set_aromantic_traits(cls, person): """Is liberal and does not want committed relationships if aromantic.""" person.is_liberal = True person.in_love_date = False person.in_love_with_family = False person.in_love_with_intergenerational = None # Age not applicable person.wants_marriage = False @classmethod def is_family_love_a_possibility(cls, person): """Determine if person could fall in love with a family member.""" return len(person.living_bio_family) > 0 and any( f.is_of_age and f.gender in person.target_gender for f in person.living_bio_family) @classmethod def set_family_love_traits(cls, person): """Traits for person in love with a family member.""" person.is_liberal = True person.in_love_date = person.age person.can_have_bio_children = False person.in_love_with_intergenerational = None # Age not applicable # ADDICTION def set_addiction_traits(self, person): """Chance for alcohol and/or drug addiction. Addiction date.""" self.addiction_chance(person) if person.will_become_drug_addict or person.will_become_alcohol_addict: self.set_date_for_addiction(person) def addiction_chance(self, person): """Drug addiction or alcohol addiction.""" person.will_become_drug_addict = self.statistics.get_drug_addiction_chance( ) if not person.will_become_drug_addict: person.will_become_alcohol_addict = self.statistics.get_alcohol_addiction_chance( ) def set_date_for_addiction(self, person): """Set date when person becomes addicted.""" range_to_become_addict = person.span_left_till_old_age early = 70 mid = 20 late = 10 person.addiction_date = self.statistics.get_chance_for_early_mid_late( range_to_become_addict, early, mid, late) def set_addiction_consequences(self, person): """Chance for rehabilitation / overdose / left untreated if addict.""" self.rehabilitation_vs_overdose_chance(person) # Set dates for rehabilitation / overdose. if person.will_overdose: range_for_overdose = list(range(1, 20)) person.death_date = person.age + self.randomizer.get_random_item( range_for_overdose) self.set_type_of_addiction_for_death_cause(person) elif person.will_recover: range_for_rehabilitation = list(range(1, 20)) person.rehabilitation_date = person.age + self.randomizer.get_random_item( range_for_rehabilitation) return person def rehabilitation_vs_overdose_chance(self, person): """Rehabilitation vs overdose chance.""" person.will_recover = self.statistics.get_alcohol_addiction_chance() if not person.will_recover: person.will_overdose = self.statistics.get_alcohol_addiction_chance( ) @classmethod def set_type_of_addiction_for_death_cause(cls, person): """Death by drug overdose or alcohol overdose.""" if person.is_drug_addict: person.death_cause = Traits.DRUG_OVERDOSE else: person.death_cause = Traits.ALCOHOL_OVERDOSE def relapse_chance(self, person): """Chance of relapsing and relapse date if so.""" person.will_relapse = self.statistics.get_relapse_chance() # Set relapse date if applicable if person.will_relapse: range_for_relapse = list(range(1, 10)) person.relapse_date = person.age + self.randomizer.get_random_item( range_for_relapse) return person
class Job: """Job base class.""" BACHELOR_JOB_LIST = 'BACHELOR' FAMOUS_JOB_LIST = 'FAMOUS' LOW_JOB_LIST = 'LOW' PART_TIMER = -0.5 # half salary INTERN = 0 FRESHGRADUATE = 1 JUNIOR = 2 SENIOR = 3 LEAD = 4 MANAGER = 5 EXECUTIVE = 6 GOOD_PERFORMANCE = 1 BAD_PERFORMANCE = -1 FLAT_PERFORMANCE = 0 PERFORMANCE_LIST = [ GOOD_PERFORMANCE, BAD_PERFORMANCE, FLAT_PERFORMANCE, FLAT_PERFORMANCE ] SALARY_MIN_STANDARD = 20000 # per year SALARY_MAX_STANDARD = 30000 # per year MAXIMUM_SALARY_CHANGE = 30 # in percentages def __init__(self, level=0, salary=0, employment=Traits.UNEMPLOYED): self.level = level self.salary = salary # per year self.employment = employment self.unemployed_year = 0 self.title = None self.current_performance = 0 self.randomizer = Randomizer() self.setup = Setup() def __str__(self): ret_val = { 'title': self.title, 'level': self.level, 'salary': self.salary, 'employment': self.employment } return str(ret_val) def progress_job(self): """Auto progress job performance""" self.current_performance += self.randomizer.get_random_item( self.PERFORMANCE_LIST) self.change_salary_rate = self.randomizer.get_random_number(0, 30) / 100 if self.current_performance > 2: self.promotion(self.change_salary_rate) elif self.current_performance > 1: self.promotion(self.change_salary_rate, job_increase=True) elif self.current_performance < -3: self.termination() elif self.current_performance < -2: self.demotion(self.change_salary_rate, job_decrease=True) elif self.current_performance < -1: self.demotion(self.change_salary_rate) def get_job(self, person): """Set occupation and job level.""" job_chance = [self.LOW_JOB_LIST, self.FAMOUS_JOB_LIST] if person.education.current_degree >= Education.BACHELOR: job_chance.append(self.BACHELOR_JOB_LIST) if person.is_female: self.change_to_female_titles() self.set_job_level(person) self.title = self.randomizer.get_random_item(self.setup.PROFESSIONS[ self.randomizer.get_random_item(job_chance)]) self.set_salary() self.employment = Traits.EMPLOYED def change_to_female_titles(self): """Change male only job titles to female titles.""" if self.title == "Waiter": self.title = "Waitress" if self.title == "Actor": self.title = "Actress" def set_job_level(self, person): """Set job level based on person's achieved education.""" if person.education == Education.UNEDUCATED: self.level = self.PART_TIMER if person.education == Education.SCHOOL: self.level = self.INTERN elif person.education == Education.BACHELOR: self.level = self.FRESHGRADUATE elif person.education == Education.MASTER: self.level = self.SENIOR elif person.education == Education.DOCTOR: self.level = self.EXECUTIVE def set_salary(self): """Determines random job salary.""" self.salary = self.randomizer.get_random_number( self.SALARY_MIN_STANDARD, self.SALARY_MAX_STANDARD) * (self.level + 1) def promotion(self, salary_increment, job_increase=False): """Job promotion.""" if job_increase and self.level < self.EXECUTIVE: self.level += 1 self.current_performance = self.FLAT_PERFORMANCE self.salary = self.salary * (1 + salary_increment) def demotion(self, salary_decrease, job_decrease=False): """Job demotion.""" if job_decrease and self.level > 0: self.level -= 1 self.current_performance = self.FLAT_PERFORMANCE self.salary = 0 if 1 - salary_decrease < 0 else self.salary * ( 1 - salary_decrease) def termination(self): """Job termination""" self.salary = 0 self.level = 0 self.employment = Traits.UNEMPLOYED self.title = "ex-" + self.title self.current_performance = self.FLAT_PERFORMANCE
def __init__(self, setup): self.setup = setup self.randomizer = Randomizer()
def __init__(self, stages, city_data): self.stages = stages self.randomizer = Randomizer() self.city_data = city_data
class CoupleDeveloper: """Couple developer base class.""" NEXT_YEAR = 1 MAX_YEAR_FOR_MOVE_IN = 6 MAX_YEAR_FOR_MARRIAGE = 8 YEARS_TILL_ADOPTION = 2 def __init__(self, statistics): self.statistics = statistics self.randomizer = Randomizer() def set_new_couples_goals(self, couple): """Set new couple's goals.""" if len( couple.oldest.span_left_till_old_age ) <= self.NEXT_YEAR: # No time left for marriage/breakup if old age. return couple # Set random move in date if applicable self.set_move_in(couple) # If couple wants to get married, set random marriage date. if couple.will_get_married: self.set_marriage_date(couple) # Statistical break up chance. couple.will_breakup = self.statistics.get_breakup_chance(couple) # If couple will break up, set break-up date to each person if couple.will_breakup: self.set_breakup_date(couple) # If couple will have children, set number of desired children and first child pregnancy/adoption date if couple.will_have_children: # Statistical chance of desired number of children couple.desired_num_of_children = self.statistics.get_desired_num_of_children( ) couple.desired_children_left = couple.desired_num_of_children couple = self.set_new_pregnancy_or_adoption_process_date(couple) self.unique_dates_validation(couple) return couple def set_move_in(self, couple): """Set move in date and household ID if applicable.""" if all(p.is_neighbor for p in couple.persons) and all( couple.persons[0].apartment_id == p.apartment_id for p in couple.persons): for p in couple.persons: p.move_in_date = p.age # If all live in the same household already, assign move in date to present time # If neighbors from different households, assign move in date for random person if all(p.is_neighbor for p in couple.persons) and any( couple.persons[0].apartment_id != p.apartment_id for p in couple.persons[0]): chosen_person = self.randomizer.get_random_item(couple.persons) self.set_move_in_date_and_id(chosen_person, couple) # Assign move in date for the outsider if not all are neighbors if any(p.is_neighbor for p in couple.persons): neighbor = next(p for p in couple.persons if p.is_neighbor) outsider = next(p for p in couple.persons if p.is_neighbor is False) # If neighbor does not have another partner, and outsider is not married, move in if len(neighbor.partners ) < 2 and outsider.is_married_or_remarried is False: self.set_move_in_date_and_id(outsider, couple) def set_move_in_date_and_id(self, person, couple): """Set move in date and household.""" moving_range = range(self.NEXT_YEAR, self.MAX_YEAR_FOR_MOVE_IN) person.move_in_date = person.age + self.randomizer.get_random_item( moving_range) person.house_to_move_in = next(p.apartment_id for p in couple.persons if p != person) def set_marriage_date(self, couple): """Sets couple's marriage date based on move in date.""" marriable_range = range(self.NEXT_YEAR, self.MAX_YEAR_FOR_MARRIAGE) if couple.move_in_date == -1: date = couple.oldest.age + self.randomizer.get_random_item( marriable_range) else: date = couple.move_in_date + self.randomizer.get_random_item( marriable_range) if date not in couple.oldest.span_left_till_old_age: date = self.randomizer.get_random_item( couple.oldest.span_left_till_old_age) couple.marriage_date = date def set_breakup_date(self, couple): """Sets couple's break up date.""" if abs(Traits.SENIOR.end - couple.marriage_date) <= self.NEXT_YEAR or abs( Traits.SENIOR.end - couple.move_in_date) <= self.NEXT_YEAR: # Couple will not break up if no time left after move in date or marriage date. couple.will_breakup = False return date = couple.marriage_date while date <= couple.move_in_date or date <= couple.marriage_date or date in range( couple.pregnancy_date, couple.birth_date + 2) or date in range( couple.adoption_process_date, couple.adoption_date + 2): date = self.statistics.get_oldest_breakup_date(couple) # If selected breakup date is before move in date, # add age difference to breakup date (which is based on oldest person's age) if couple.move_in_date + couple.age_difference >= date: couple.breakup_date = date + (couple.age_difference + self.NEXT_YEAR) # If oldest person will be dead by breakup date, set will break up to False if couple.breakup_date not in couple.oldest.span_left_till_old_age: couple.will_breakup = False couple.breakup_date = -1 else: couple.breakup_date = date def set_new_pregnancy_or_adoption_process_date(self, couple): """Sets pregnancy or adoption date and birth date.""" if abs(Traits.ADULT.start - couple.marriage_date) <= 2 or abs(Traits.ADULT.start - couple.move_in_date) <= 2: return couple if abs(couple.oldest.age - couple.breakup_date) <= 4 or abs( couple.marriage_date - couple.breakup_date) <= 4 or abs(couple.move_in_date - couple.breakup_date) <= 4: return couple date = couple.breakup_date while date <= couple.move_in_date or date <= couple.marriage_date or date in range( couple.breakup_date - 3, couple.breakup_date + 3): date = self.statistics.get_oldest_pregnancy_date(couple) if couple.will_get_pregnant: couple.pregnancy_date = date couple.birth_date = date + self.NEXT_YEAR elif couple.will_adopt: couple.adoption_process_date = date couple.adoption_date = date + self.YEARS_TILL_ADOPTION else: raise Exception( "Couple is set on having a child but won't get pregnant nor adopt." ) self.unique_dates_validation(couple) return couple @classmethod def unique_dates_validation(cls, couple): if couple.will_get_married and couple.marriage_date >= couple.oldest.age: if couple.marriage_date not in couple.oldest.span_left_till_old_age: raise Exception( "Marriage date cannot be set outside oldest person's lifetime." ) if couple.will_breakup: if couple.breakup_date not in couple.oldest.span_left_till_old_age: raise Exception( "Breakup date cannot be set outside oldest person's lifetime." ) if couple.pregnancy_date > 0: if couple.pregnancy_date not in couple.oldest.span_left_till_old_age or \ couple.birth_date not in couple.oldest.span_left_till_old_age: raise Exception( "Pregnancy/birth date cannot be set outside oldest person's lifetime." ) if couple.oldest.is_young_adult is False: raise Exception( "Pregnancy/birth date cannot be set if couple is older than young adult." ) if couple.will_adopt and couple.adoption_process_date > 0: if couple.adoption_process_date not in couple.oldest.span_left_till_old_age or \ couple.adoption_date not in couple.oldest.span_left_till_old_age: raise Exception( "Adoption process/adoption date cannot be set outside oldest person's lifetime." ) if couple.will_breakup and couple.will_get_married: if couple.breakup_date == couple.marriage_date: raise Exception( "Breakup date and marriage date cannot be set at the same time." ) if couple.breakup_date > 0 and couple.pregnancy_date > 0: if couple.breakup_date == couple.pregnancy_date or couple.breakup_date == couple.birth_date: raise Exception( "Breakup date and pregnancy/birth date cannot be set at the same time." ) if couple.breakup_date > 0 and couple.adoption_process_date > 0: if couple.breakup_date == couple.adoption_process_date or couple.breakup_date == couple.adoption_date: raise Exception( "Breakup date and adoption process/adoption date cannot be set at the same time." )
class PersonDeveloper: """Base class for person developer.""" def __init__(self, setup, statistics): self.setup = setup self.statistics = statistics self.randomizer = Randomizer() def set_new_stage_traits(self, person): """Link each new stage to their methods for setting new traits.""" if person.stage == Traits.BABY: pass elif person.stage == Traits.CHILD: self.set_child_traits(person) elif person.stage == Traits.TEEN: self.set_teen_traits(person) elif person.stage == Traits.ADULT: self.set_adult_traits(person) elif person.stage == Traits.YOUNGADULT: self.set_youngadult_traits(person) elif person.stage == Traits.SENIOR: self.set_senior_traits(person) else: raise Exception("Person's stage is wrong.") @classmethod def set_child_traits(cls, child): child.school_start_date = child.education.SCHOOL_START_DATE def set_teen_traits(self, teen): """Teen traits.""" if Traits.AUTISTIC_DISORDER in teen.conditions: self.set_autistic_teen_traits(teen) return teen.gender_identity = self.statistics.get_gender_identity( ) # Gender identity must be set first teen.sexual_orientation = self.statistics.get_sexual_orientation() teen.target_gender = [ gender for gender in self.get_target_gender(teen) ] if teen.is_lgbta: teen.come_out_date = self.randomizer.get_random_item( teen.span_left_till_next_stage) def set_autistic_teen_traits(self, teen): """Cisgender, aromantic asexual, no coming out date for autistic persons.""" teen.gender_identity = Traits.CISGENDER teen.sexual_orientation = Traits.AROMANTIC_ASEXUAL teen.target_gender = [ gender for gender in self.get_target_gender(teen) ] @classmethod def get_target_gender(cls, teen): """Returns target gender(s) based on sexual orientation logic.""" if teen.is_male or (teen.is_female and teen.is_trans): same = Traits.MALE opposite = Traits.FEMALE elif teen.is_female or (teen.is_male and teen.is_trans): same = Traits.FEMALE opposite = Traits.MALE if teen.sexual_orientation in [ Traits.HETEROSEXUAL, Traits.HETEROROMANTIC_ASEXUAL ]: yield opposite if teen.sexual_orientation in [ Traits.HOMOSEXUAL, Traits.HOMOROMANTIC_ASEXUAL ]: yield same if teen.sexual_orientation in [ Traits.BISEXUAL, Traits.BIROMANTIC_ASEXUAL ]: yield same yield opposite if teen.sexual_orientation == Traits.AROMANTIC_ASEXUAL: yield None def set_coming_out_consequences(self, teen): """Chance of teen moving out / being thrown out / committing suicide if conservative family.""" if not teen.has_conservative_parents: return # Suicide chance if not self.suicide_consequence(teen): # Thrown out chance if not self.thrown_out_consequence(teen): # Otherwise, will move out teen.will_move_out = True teen.move_out_date = Traits.YOUNGADULT.start def suicide_consequence(self, teen): """Determine chance of suicide as coming out in conservative family consequence.""" if not self.statistics.get_suicide_chance_as_coming_out_consequence(): return False teen.death_date = teen.age + 1 if len( teen.span_left_till_next_stage ) < 1 else self.randomizer.get_random_item( teen.span_left_till_next_stage) teen.death_cause = Traits.SUICIDE return True def thrown_out_consequence(self, teen): """Determine chance of being thrown out of home as coming out consequence.""" if not self.statistics.get_thrown_out_chance(): return False teen.will_be_thrown_out = True teen.thrown_out_date = Traits.YOUNGADULT.start return True def set_youngadult_traits(self, person): """Young adult traits.""" # Education if Traits.AUTISTIC_DISORDER not in person.conditions: person.will_do_bachelor = self.statistics.get_chance_for_getting_bachelor_degree( ) person.will_do_master = self.statistics.get_chance_for_getting_master_degree( ) person.will_do_doctor = self.statistics.get_chance_for_getting_master_degree( ) # Set relationship orientation (mono/poly) person.relationship_orientation = self.statistics.get_relationship_orientation( ) # Set (initial) liberalism if person.is_lgbta or person.is_poly: person.is_liberal = True else: person.is_liberal = self.statistics.get_liberalism() # Cannot have biological children if LGTA if person.is_lgbta and not person.is_bi: person.can_have_bio_children = False # Set relationship-oriented traits self.set_love_traits(person) # Set chance of drug/alcohol addiction self.set_addiction_traits(person) @classmethod def set_adult_traits(cls, person): """Adult traits.""" person.can_have_bio_children = False def set_senior_traits(self, person): """Senior traits.""" person.job.employment = Traits.RETIRED if person.is_neighbor: self.display_retired_message(person) @classmethod def display_retired_message(cls, person): print("\n{} has retired.".format(person)) # LOVE def set_love_traits(self, person): """Returns person with statistical / random traits for wish for romance / marriage / children. Chance for family or intergenerational love. Sets date to fall in love if applicable.""" if len(person.span_left_till_old_age) <= 1: return if Traits.AUTISTIC_DISORDER in person.conditions: self.set_autistic_yadult_traits(person) return if person.is_poly: person.in_love_as_throuple = self.statistics.get_triad_chance() if person.is_liberal: person.wants_domestic_partnership = self.statistics.get_domestic_partnership_desire( ) person.wants_children = self.statistics.get_children_desire() else: self.set_conservative_traits(person) if person.wants_domestic_partnership: person.wants_marriage = self.statistics.get_marriage_desire() self.set_new_love_date(person) else: self.set_aromantic_traits(person) def set_autistic_yadult_traits(self, person): """No romance nor children for autistic persons.""" person.relationship_orientation = False person.wants_children = False person.can_have_bio_children = False self.set_aromantic_traits(person) def set_new_love_date(self, person): """Sets in_love_date within person's age and X. Chance of intergenerational / family love.""" if self.is_family_love_a_possibility(person): person.in_love_with_family = self.statistics.get_family_love_chance( ) if person.in_love_with_family: self.set_family_love_traits(person) else: person.in_love_with_intergenerational = self.statistics.get_intergenerational_chance( ) if person.in_love_with_intergenerational: person.is_liberal = True # If mixed race, race compatibility not applicable if person.is_mixed_race: person.in_love_with_another_race = None else: person.in_love_with_another_race = self.statistics.get_interracial_love_chance( ) if person.in_love_with_another_race: person.is_liberal = True # Assign date to fall in love. Ex: within 10 years. # If person will be dead before then, just loop through their remaining years. if len(person.span_left_till_old_age) <= 1: return if len(person.span_left_till_old_age) < 10: person.in_love_date = self.randomizer.get_random_item( person.span_left_till_old_age) else: person.in_love_date = self.randomizer.get_random_item( range(person.age, person.age + 11)) def set_new_love_date_for_polys(self, couple): """After couple creation, set new future love date for each poly person from couple if any.""" for person in couple.persons: if person.is_romanceable: self.set_new_love_date(person) @classmethod def set_conservative_traits(cls, person): """Wants marriage and children if conservative.""" person.wants_domestic_partnership = True person.wants_marriage = True person.wants_children = True def set_aromantic_traits(self, person): """Is liberal and does not want committed relationships if aromantic.""" person.is_liberal = True person.in_love_date = False person.in_love_with_family = False person.in_love_with_intergenerational = None # Age not applicable person.wants_marriage = False self.set_single_adoption(person) def set_single_adoption(self, person): """Single adoption traits if applicable.""" if person.wants_children and person.desired_children_left > 0 and person.is_young_adult: person.desired_num_of_children = self.statistics.get_desired_num_of_children( ) person.desired_children_left = person.desired_num_of_children person.single_adoption_process_date = person.age + self.randomizer.get_random_item( range(1, Traits.ADULT.start - 2)) person.single_adoption_date = person.single_adoption_process_date + 2 @classmethod def is_family_love_a_possibility(cls, person): """Determine if person could fall in love with a family member.""" return len(person.living_bio_family) > 0 and any( f.is_of_age and f.gender in person.target_gender for f in person.living_bio_family) @classmethod def set_family_love_traits(cls, person): """Traits for person in love with a family member.""" person.is_liberal = True person.in_love_date = person.age person.can_have_bio_children = False person.in_love_with_intergenerational = None # Age not applicable person.in_love_with_another_race = None # Race not applicable # ADDICTION def set_addiction_traits(self, person): """Chance for alcohol and/or drug addiction. Addiction date.""" self.addiction_chance(person) if person.will_become_drug_addict or person.will_become_alcohol_addict: self.set_date_for_addiction(person) def addiction_chance(self, person): """Drug addiction or alcohol addiction.""" person.will_become_drug_addict = self.statistics.get_drug_addiction_chance( ) if not person.will_become_drug_addict: person.will_become_alcohol_addict = self.statistics.get_alcohol_addiction_chance( ) def set_date_for_addiction(self, person): """Set date when person becomes addicted.""" range_to_become_addict = person.span_left_till_old_age early = 70 mid = 20 late = 10 person.addiction_date = self.statistics.get_chance_for_early_mid_late( range_to_become_addict, early, mid, late) def set_addiction_consequences(self, person): """Chance for rehabilitation / overdose / left untreated if addict.""" will_recover = self.statistics.get_rehabilitation_chance() if will_recover: range_for_rehabilitation = list(range(1, 20)) person.rehabilitation_date = person.age + self.randomizer.get_random_item( range_for_rehabilitation) else: will_overdose = self.statistics.get_alcohol_addiction_chance() # Set dates for rehabilitation / overdose. if will_overdose: range_for_overdose = list(range(1, 20)) person.death_date = person.age + self.randomizer.get_random_item( range_for_overdose) self.set_type_of_addiction_for_death_cause(person) @classmethod def set_type_of_addiction_for_death_cause(cls, person): """Death by drug overdose or alcohol overdose.""" if person.is_drug_addict: person.death_cause = Traits.DRUG_OVERDOSE else: person.death_cause = Traits.ALCOHOL_OVERDOSE def relapse_chance(self, person): """Chance of relapsing and relapse date if so.""" will_relapse = self.statistics.get_relapse_chance() # Set relapse date if applicable if will_relapse: range_for_relapse = range(1, 10) person.relapse_date = person.age + self.randomizer.get_random_item( range_for_relapse) def set_depression_for_housemates(self, person, household): """Adds statistical chance of depression to each housemate of dead person.""" housemates = [p for p in household.members if p != person] for p in housemates: if p.age >= Traits.TEEN.start: depressed = self.statistics.get_depression_chance() if depressed: p.conditions.append(Traits.DEPRESSION) p.depression_date = p.age + 1 # Depression will be diagnosed next year self.set_depression_consequences(p) def set_depression_consequences(self, person): """Depressed person will go to therapy, commit suicide, or depression will be left untreated.""" will_go_to_therapy = self.statistics.get_therapy_chance() if will_go_to_therapy: person.therapy_date = person.depression_date + 1 # Therapy will start one year after depression diagnosis will_recover = self.statistics.get_recovery_chance() if will_recover: recovery_range = range(1, 5) person.depression_recovery_date = person.therapy_date + self.randomizer.get_random_item( recovery_range) else: suicide = self.statistics.get_suicide_chance_as_depression_consequence( ) if suicide: suicidal_range = range(1, 5) person.death_date = person.depression_date + self.randomizer.get_random_item( suicidal_range) person.death_cause = Traits.SUICIDE
class CityCoupleCreator: """Creates new relationships.""" def __init__(self): self.randomizer = Randomizer() self.compatibility = Compatibility() def create_couple(self, person, romanceable_outsiders): """Creates couple if found a match.""" romanceables = [r for r in romanceable_outsiders if r != person] candidates = self.get_candidates_list(person, romanceables) if not self.there_are_candidates(candidates): return False if person.in_love_as_throuple: return self.get_throuple(person, candidates) return self.get_couple(person, candidates) def get_throuple(self, person, candidates): """Returns new throuple.""" found_persons = self.get_random_candidate(candidates) found_person1 = found_persons[0] found_person2 = found_persons[1] self.set_as_partner(person, found_person1, found_person2) self.update_relationship_status_to_committed( person, found_person1, found_person2) return self.create_new_relationship(person, found_person1, found_person2) def get_couple(self, person, candidates): """Returns new couple.""" found_person = self.get_random_candidate(candidates) self.set_as_partner(person, found_person) self.update_relationship_status_to_committed(person, found_person) return self.create_new_relationship(person, found_person) def get_candidates_list(self, person, romanceables): """Returns list of compatible individuals or pairs.""" if person.in_love_as_throuple is False: return [candidate for candidate in romanceables if self.compatibility.are_compatible(person, candidate)] else: pairs = list(combinations(romanceables, 2)) pair_candidates = [] for romanceable in pairs: if self.compatibility.are_compatible(person, romanceable[0], romanceable[1]): pair_candidates.extend([[romanceable[0], romanceable[1]]]) return pair_candidates @classmethod def there_are_candidates(cls, candidates): return candidates is not None and len(candidates) >= 1 def get_random_candidate(self, candidates): return self.randomizer.get_random_item(candidates) @classmethod def set_as_partner(cls, person, found_person, found_person2=None): if found_person2 is None: person.partners.append(found_person) found_person.partners.append(person) else: person.partners.append(found_person) person.partners.append(found_person2) found_person.partners.append(person) found_person.partners.append(found_person2) found_person2.partners.append(person) found_person2.partners.append(found_person) @classmethod def update_relationship_status_to_committed(cls, person1, person2, person3=None): if not person1.is_married_or_remarried: person1.relationship_status = Traits.COMMITTED if not person2.is_married_or_remarried: person2.relationship_status = Traits.COMMITTED if person3 is not None and not person3.is_married_or_remarried: person3.relationship_status = Traits.COMMITTED @classmethod def create_new_relationship(cls, person1, person2, person3=None): """Create new relationship.""" if person3 is None: if person1 in person2.living_bio_family: couple = ConsangCouple(person1, person2) elif person1.gender != person2.gender: couple = StraightCouple(person1, person2) else: couple = GayCouple(person1, person2) return couple return Throuple(person1, person2, person3)
class DivorceHandler: """Handles divorce and separation.""" def __init__(self): self.randomizer = Randomizer() def get_divorced(self, couple): """Handles couple divorce""" self.set_divorced_status(couple) self.remove_spouses(couple) self.add_to_exspouses(couple) self.revert_username(couple) if any(p.is_neighbor for p in couple.persons): self.display_divorce_message(couple) @classmethod def set_divorced_status(cls, couple): for person in couple.persons: person.relationship_status = Traits.DIVORCED @classmethod def remove_spouses(cls, couple): for person in couple.persons: person.spouses = [ spouse for spouse in person.spouses if spouse not in couple.persons ] person.partners = [ partner for partner in person.partners if partner not in couple.persons ] @classmethod def add_to_exspouses(cls, couple): for person in couple.persons: exes = [ex for ex in couple.persons if ex != person] for ex in exes: person.ex_spouses.append(ex) @classmethod def revert_username(cls, couple): for person in couple.persons: person.surname = person.original_surname @classmethod def display_divorce_message(cls, couple): print( f"\n{couple.person1} and {couple.person2} have gotten a divorce.") def get_separated(self, couple): """Handles couple separation""" self.set_separated_status(couple) self.remove_partners(couple) self.add_to_expartners(couple) if any(p.is_neighbor for p in couple.persons): self.display_separation_message(couple) @classmethod def set_separated_status(cls, couple): for person in couple.persons: if person.is_married_or_remarried is False and len( person.partners) < 2: person.relationship_status = Traits.SEPARATED @classmethod def remove_partners(cls, couple): for person in couple.persons: person.partners = [ partner for partner in person.partners if partner not in couple.persons ] @classmethod def add_to_expartners(cls, couple): for person in couple.persons: exes = [ex for ex in couple.persons if ex != person] for ex in exes: person.ex_partners.append(ex) @classmethod def display_separation_message(cls, couple): print(f"\n{couple.person1} and {couple.person2} have separated.") def leave_household(self, couple): """Returns person who will leave the apartment and the new apartment ID if any.""" if any(p.move_in_date > 0 for p in couple.persons): # If someone had moved in, they'll be the one to move out. leaving_person = next(p for p in couple.persons if p.move_in_date > 0) else: # Else, random person will move out. leaving_person = self.randomizer.get_random_item(couple.persons) new_apartment_id = self.get_id_from_neighborhood_friends( leaving_person) return {"person": leaving_person, "id": new_apartment_id} def get_id_from_neighborhood_friends(self, person): """Returns None if person has no neighborhood friends, or liberal friend's apartment ID if so.""" if len(person.neighbor_friends) == 0: self.display_left_neighborhood_message(person) return None else: friend = next(friend for friend in person.neighbor_friends) self.display_new_household_message(person, friend) return friend.apartment_id @classmethod def display_left_neighborhood_message(cls, person): if len(person.children) == 0: print("{} has moved out and no longer lives in the neighborhood.". format(person.name)) return children_in_household = [ p for p in person.children if p.move_in_date > 0 and p.apartment_id == p.apartment_id ] if len(children_in_household) == 0: print("{} has moved out and no longer lives in the neighborhood.". format(person.name)) elif len(children_in_household) == 1: if person.is_male: print( "{} and his child have moved out and no longer live in the neighborhood." .format(person.name)) else: print( "{} and her child have moved out and no longer live in the neighborhood." .format(person.name)) else: if person.is_male: print( "{} and his children have moved out and no longer live in the neighborhood." .format(person.name)) else: print( "{} and her children have moved out and no longer live in the neighborhood." .format(person.name)) @classmethod def display_new_household_message(cls, person, friend): if person.is_male: print("{} now lives in his friend {}'s apartment, {}.".format( person.name, friend, friend.apartment_id)) else: print("{} now lives in her friend {}'s apartment, {}.".format( person.name, friend, friend.apartment_id))