Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
 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()
Ejemplo n.º 3
0
 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()
Ejemplo n.º 4
0
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.")
Ejemplo n.º 5
0
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.")
Ejemplo n.º 6
0
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))
Ejemplo n.º 7
0
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.")
Ejemplo n.º 9
0
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.")
Ejemplo n.º 14
0
 def __init__(self):
     self.randomizer = Randomizer()
Ejemplo n.º 15
0
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
Ejemplo n.º 17
0
 def __init__(self, city_data):
     self.city_data = city_data
     self.randomizer = Randomizer()
Ejemplo n.º 18
0
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.")
Ejemplo n.º 19
0
 def __init__(self, stages):
     self.stages = stages
     self.randomizer = Randomizer()
Ejemplo n.º 20
0
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
Ejemplo n.º 22
0
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
Ejemplo n.º 23
0
 def __init__(self, setup):
     self.setup = setup
     self.randomizer = Randomizer()
Ejemplo n.º 24
0
 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."
                )
Ejemplo n.º 26
0
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)
Ejemplo n.º 28
0
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))