def runCapTests(self): f1 = Family(None) with open("data.csv", "w", newline="") as csvFile: writer = csv.writer(csvFile) writer.writerow(["Cap"]) for i in range(10000): f1.generatePerson(None, age=80, gender="male") f1.generatePerson(None, age=80, gender="female") val1 = f1.getPerson(0).calculateCap(f1.getPerson(1)) val2 = f1.getPerson(1).calculateCap(f1.getPerson(0)) writer.writerow([val1]) writer.writerow([val2]) f1.removePersonByIndex(1) f1.removePersonByIndex(0)
def runCapVsRomTests(self): community = Community() f1 = Family() community.addFamily(f1) with open("data.csv", "w", newline="") as csvFile: writer = csv.writer(csvFile) writer.writerow(["Cap", "Rom"]) for i in range(10000): f1.generatePerson(None, age=80, gender="male") f1.generatePerson(None, age=80, gender="female") val1 = f1.getPerson(0).calculateCap(f1.getPerson(1)) val2 = f1.getPerson(1).calculateCap(f1.getPerson(0)) val3 = f1.getPerson(0).calculateRomanticInterest(f1.getPerson(1)) val4 = f1.getPerson(1).calculateRomanticInterest(f1.getPerson(0)) writer.writerow([val1, val3]) writer.writerow([val2, val4]) f1.removePersonByIndex(1) f1.removePersonByIndex(0)
def passTime(self, autoDateIncrease=True): ''' Passes time, updating things in this order: 1. Increase date 2. Update moods (remove expired modifiers) 3. Decide season events 4. Pass time in families (subroutine). 5. Social interactions including on-the-fly mood changes ''' if autoDateIncrease: self.date += 1 self.mayorTime += 1 for p in self.allPeople(): if autoDateIncrease: p.age += 1 p.log("== {} ==".format(p.ageToString())) # update modifiers before everything else, to prevent added modifiers from being # immediately decreased in duration for p in self.allPeople(): p.updateModifiers() self.log("==== {} ====".format(self.dateToString().upper())) if self.season() == 3: # harsh winter if random.randint(1, HARSH_WINTER_CHANCE) == 1: self.log("The winter is a harsh one") self.harshWinter = True for p in self.allPeople(): p.addModifier(12) # add 'cold' modifier else: self.harshWinter = False if self.season() == ELECTION_SEASON and ( self.mayorTime >= MAYOR_TERM_LENGTH or self.mayor is None): self.log("An election is occuring") # Work out who can stand for election # Max 3 candidates are chosen # Everyone recieves a suitability rating, based on their extroversion + agreeableness allP = self.allPeople() candidates = [] lowestRating = -1 for p in allP: rating = p.mayoralSuitability() if (rating > lowestRating or len(candidates) < 3) and p.age >= MIN_MAYOR_AGE: candidates.append(p) candidates = sorted(candidates, key=lambda x: x.mayoralSuitability(), reverse=True) if len(candidates) > 3: del candidates[-1] lowestRating = candidates[-1].mayoralSuitability() if len(candidates) < 3: self.log( "There are not enough eligible candidates to hold an election." ) else: # We now have the top three candidates. # Commence voting! self.log("The candidates standing are: {}, {} and {}".format( candidates[0].printableFullName(), candidates[1].printableFullName(), candidates[2].printableFullName())) votes = {c: 0 for c in candidates} for p in allP: if p.age >= MIN_VOTING_AGE: lowestDiff = 1 chosen = None for c in candidates: if p == c: # Always choose yourself if possible lowestDiff = -1000 chosen = p else: orientationDiff = abs( p.politicalOrientation() - c.politicalOrientation()) # Modify by rapport orientationDiff -= p.getRapport( c) * RAPPORT_VOTING_MODIFIER if orientationDiff < lowestDiff: chosen = c lowestDiff = orientationDiff if chosen is not None: votes[chosen] += 1 p.log("I voted for {}".format( chosen.printableFullName())) candidatesByVotes = sorted(votes.keys(), key=lambda x: votes[x], reverse=True) if votes[candidatesByVotes[0]] == votes[candidatesByVotes[1]]: # Deal with a hung election self.log("{} and {} are tied on {} votes".format( candidatesByVotes[0].printableFullName(), candidatesByVotes[1].printableFullName(), votes[candidatesByVotes[0]])) if self.mayor is not None: self.log( "{} leaves office after {} years as mayor, due to a hung election" .format(self.mayor.printableFullName(), self.mayorTotalTime())) self.mayor.log( "I left mayoral office after a hung election". format(self.mayor.printableFullName())) self.mayor.logKeyEvent("left mayoral office") self.mayor.isMayor = False self.mayor = None self.mayorFamily = None self.mayorTime = 0 self.mayorTerms = 0 # Set end date for this person self.endMayorTime() else: self.log("No mayor has been elected.") else: # We have a winner! It doesn't need to be a majority, just the highest vote count. winner = candidatesByVotes[0] # Add lost election modifiers candidatesByVotes[1].addModifier(19) candidatesByVotes[2].addModifier(19) if self.mayor is not winner: self.log("{} wins with {} votes".format( winner.printableFullName(), votes[winner])) winner.logKeyEvent("became mayor") winner.log("I became mayor") winner.addModifier(18) else: self.log("{} continues in office with {} votes".format( winner.printableFullName(), votes[winner])) self.mayorTerms += 1 self.log("2nd: {} with {} votes".format( candidatesByVotes[1].printableFullName(), votes[candidatesByVotes[1]])) self.log("3rd: {} with {} votes".format( candidatesByVotes[2].printableFullName(), votes[candidatesByVotes[2]])) if self.mayor is not None: if self.mayor is not winner: self.log( "{} leaves office after {} years as mayor". format(self.mayor.printableFullName(), self.mayorTotalTime())) self.mayor.log( "I left mayoral office, beaten by {}".format( winner.printableFullName())) self.mayor.logKeyEvent("left mayoral office") # Set end date for this person self.endMayorTime() self.mayor.isMayor = False if self.mayor is not winner: # Set start date for this person self.startMayorTime(winner) self.mayorTerms = 0 # Actally set the mayor variables self.mayor = winner self.mayorTime = 0 self.mayorFamily = self.mayor.family self.mayor.isMayor = True # Decide whether to trigger 'invading army' event if len(self.families ) > COMMUNITY_FAMILY_LIMIT and COMMUNITY_FAMILY_LIMIT != 0: namegen = NameGen() factionName = namegen.factionName() self.log("An invading army, {}, is passing through the community". format(factionName)) for i in range(ATTACK_KILL_AMOUNT): fInd = random.randint(0, len(self.families) - 1) f = self.families[fInd] self.log("The {} family is wiped out by the attack".format( f.familyName)) for p in f.people: p.die() self.removeFamily(f) self.deadFamilies.append(f) # Decide whether to trigger 'family joins event' if len(self.families) < COMMUNITY_FAMILY_MIN: for i in range(COMMUNITY_FAMILY_JOIN): # generate family tempFam = Family(self) p1 = Person([None, None], gender="male", age=random.randint(18 * 4, 25 * 4), married=True) p2 = Person([None, None], gender="female", age=random.randint(18 * 4, 25 * 4), married=True) p1.partner = p2 p2.partner = p1 p1.updateRapport(p2, START_RELATIVE_RAPPORT) p2.updateRapport(p1, START_RELATIVE_RAPPORT) tempFam.addPerson(p1) tempFam.addPerson(p2) for j in range(random.randint(2, 4)): child = tempFam.generatePerson([p1, p2], age=random.randint( 0, START_CHILDREN_MAX_AGE)) p1.children.append(child) p2.children.append(child) p1.updateRapport(child, START_RELATIVE_RAPPORT) p2.updateRapport(child, START_RELATIVE_RAPPORT) self.addFamily(tempFam) self.log( "The {} family, a family of {}s has joined the community". format(tempFam.familyName, tempFam.profession)) for f in self.families: if len(f.people) == 0: # everyone's died self.log( "The old house of the {} family is left deserted".format( f.familyName)) self.removeFamily(f) self.deadFamilies.append(f) else: f.passTime(self.date % 4, self.harshWinter) for p in self.allPeople(minAge=START_INTERACTION_MIN_AGE): # A person interacts with 0-10 people per day, based on extroversion interactions = math.ceil(p.getAttr("e") * 10) if p.isMayor: interactions = math.ceil(interactions * MAYOR_INTERACTIONS_MOD) for i in range(math.ceil(p.getAttr("e") * 10)): # This next bit works by mapping by rapport. Basically, # a person will be more likely to initiate social interactions # with someone who they have more rapport with. mappedByRapport = {} totalRapport = 0 for i in self.allPeople(): if i == p: continue mappedByRapport[i] = p.getRapport( i) + 1 # +1 to make positive # Younger people will want to talk to people of their own age. # So, decrease the mapping by a number based on age. # How does the formula work? Black magic and duct tape. # Of course, this doesn't apply to family. # This assumes that there is no age prejudice above 16 - TODO? if i not in p.family.people: addition = (8 - abs((i.age - p.age) // 4)) * max( 0, ((16 * 4 - p.age) / 16 * 4)) mappedByRapport[i] = max(0, mappedByRapport[i] + addition) totalRapport += mappedByRapport[i] # Choose a person to interact with randomly, but weighted by rapport chosenNum = random.uniform(0, totalRapport) currentNum = 0 for b in mappedByRapport.keys(): if currentNum + mappedByRapport[b] >= chosenNum: # we have our person! # Interactions produce rapport. The person initating the # conversation gains more rapport for that person than the # person does for the initiator. if p.likes(b) and b.likes(p): if random.randint( 1, DT_CHANCE ) == 1 and p.age > DT_MIN_AGE and b.age > DT_MIN_AGE: # deep talk p.updateRapport( b, DT_RAPPORT_GAIN * (p.calculateCap(b) / CAP_MODIFIER)) b.updateRapport( p, DT_RAPPORT_GAIN * (b.calculateCap(p) / CAP_MODIFIER) * INTERACTED_WITH_MOD) p.log("I had a deep talk with {}".format( b.printableFullName())) b.log("{} had a deep talk with me".format( p.printableFullName())) # For now, this can lead to a partnership # TODO change wording and other stuff for existing partner if p.romanticallyLikes(b) and p.age >= MIN_ASKOUT_AGE\ and b.age >= MIN_ASKOUT_AGE and not p.married and not b.married\ and not p.isRelative(b) and p.gender != b.gender\ and ((p.timeWithPartner >= 2 and b.timeWithPartner >= 2) or (p.partner is None and b.partner is None)): cont = True # A person will only abandon a partner if the romantic interest # to a new person is higher if p.partner is not None: if p.calculateRomanticInterest( p.partner ) >= p.calculateRomanticInterest(b): cont = False if b.partner is not None: if b.calculateRomanticInterest( b.partner ) >= b.calculateRomanticInterest(p): cont = False if cont: p.log("I asked out {}".format( b.printableFullName())) b.log("{} asked me out".format( p.printableFullName())) if b.romanticallyLikes(p): b.log("I accepted {}'s proposal". format(p.firstName())) p.log("{} accepted my proposal". format(b.firstName())) self.log( "{} and {} are now going out". format(p.printableFullName(), b.printableFullName())) if b.partner is not None: b.breakUp() if p.partner is not None: p.breakUp() b.partner = p p.partner = b b.addModifier(4) p.addModifier(4) else: b.log("I declined {}'s proposal". format(p.firstName())) p.log("{} rebuffed me".format( b.firstName())) p.addModifier( 16) # add rebuffed modifier else: # quick chat p.updateRapport( b, CHAT_RAPPORT_GAIN * (p.calculateCap(b) / CAP_MODIFIER)) b.updateRapport( p, CHAT_RAPPORT_GAIN * (b.calculateCap(p) / CAP_MODIFIER) * INTERACTED_WITH_MOD) p.log("I had a quick chat with {}".format( b.printableFullName())) b.log("{} had a quick chat with me".format( p.printableFullName())) else: if p.isRelative(b): famMod = FAMILY_ARGUMENT_MOD else: famMod = 1 # Decide whether to initiate a fight fought = False if p.getRapport( b ) <= FIGHT_MAX_RAPPORT and p.age > FIGHT_MIN_AGE: chance = max( 2, FIGHT_BASE_CHANCE - (p.getAttr("e") * 2 + p.getAttr("n") * 2)) if random.uniform(0, chance) <= 1: p.updateRapport( b, FIGHT_RAPPORT_GAIN * INTERACTED_WITH_MOD * famMod) b.updateRapport( p, FIGHT_RAPPORT_GAIN * famMod) p.log("I started a fight with {}".format( b.printableFullName())) b.log("{} started a fight with me".format( p.printableFullName())) if random.randint(1, FIGHT_DEATH_CHANCE) == 1: if random.randint(1, 2) == 1: self.log( "{} killed {} after {} started a fight" .format( p.printableFullName(), b.printableFullName(), p.firstName())) b.die() if p.sociopathy( ) >= SOCIOPATH_THRESH: p.log( "I killed {}, but feel no remorse." .format(b.firstName())) else: p.log( "I killed {}. I feel terrible." .format(b.firstName())) p.addModifier(21) else: self.log( "{} killed {} in self-defence after {} started a fight" .format( b.printableFullName(), p.printableFullName(), p.firstName())) p.die() if b.sociopathy( ) >= SOCIOPATH_THRESH: b.log( "I killed {} in self-defence, but feel no remorse." .format(p.firstName())) else: b.log( "I killed {} in self-defence." .format(p.firstName())) b.addModifier(22) fought = True if not fought: if b.age > MIN_ARGUMENT_AGE and p.age > MIN_ARGUMENT_AGE: p.updateRapport( b, ARGUMENT_RAPPORT_GAIN * INTERACTED_WITH_MOD * famMod) b.updateRapport( p, ARGUMENT_RAPPORT_GAIN * famMod) p.log("I had an argument with {}".format( b.printableFullName())) b.log("{} had an argument with me".format( p.printableFullName())) else: p.updateRapport( b, ANGRY_LOOK_RAPPORT_GAIN * INTERACTED_WITH_MOD * famMod) b.updateRapport( p, ANGRY_LOOK_RAPPORT_GAIN * famMod) p.log("I gave {} an angry look".format( b.printableFullName())) b.log("{} gave me an angry look".format( p.printableFullName())) break else: currentNum += mappedByRapport[b]
def __init__(self): self.families = [] self.deadFamilies = [] self.date = 0 # SET TO 0 IF NOT self.harshWinter = False self.eventLog = [] self.mayor = None self.mayorFamily = None self.mayorTime = 0 self.mayorTerms = 0 # In the format ["mayor name", start date, end date] self.mayorHistory = [] # In the format ["title", "author name", date] self.greatWorks = [] # generate 3 families, each with 2 adults and randomly 0-3 children # Do this by generating 6 adults (aged 20-40) and matching them based on romantic interest # Highest mutual romantic interest = pair tempAdults = [] for i in range(6): gender = "female" if i % 2 == 0: gender = "male" tempAdults.append( Person([None, None], gender=gender, age=random.randint(18 * 4, 25 * 4), married=True)) # keep track of already paired adults pairedInd = [] pairs = [] for i in range(len(tempAdults)): if i in pairedInd: continue highest = [0, 0] for j in range(i + 1, len(tempAdults)): if j in pairedInd or tempAdults[i].gender == tempAdults[ j].gender: continue # calculate mutual romantic interest # This is done by dividing the sum of the interests by the difference, # so that values that are closer are higher-valued rom = tempAdults[i].calculateRomanticInterest(tempAdults[j]) recipRom = tempAdults[j].calculateRomanticInterest( tempAdults[i]) mutualRom = (rom + recipRom) / max(0.00001, abs(rom - recipRom)) if mutualRom > highest[1]: highest = [j, mutualRom] pairedInd = pairedInd + [i, highest[0]] pairs.append([i, highest[0]]) # now we have pairs, but as indices of tempAdults for pair in pairs: a1 = tempAdults[pair[0]] a2 = tempAdults[pair[1]] a1.partner = a2 a2.partner = a1 a1.updateRapport(a2, START_RELATIVE_RAPPORT) a2.updateRapport(a1, START_RELATIVE_RAPPORT) # Create a family for these adults tempFamily = Family(self) self.families.append(tempFamily) tempFamily.addPerson(a1) tempFamily.addPerson(a2) # Now generate children childrenCount = random.randint(2, 4) for i in range(childrenCount): child = tempFamily.generatePerson([a1, a2], age=random.randint( 0, START_CHILDREN_MAX_AGE)) a1.addChild(child) a2.addChild(child) # Make sure to set a base rapport a1.updateRapport(child, START_RELATIVE_RAPPORT) a2.updateRapport(child, START_RELATIVE_RAPPORT) child.updateRapport(a1, START_RELATIVE_RAPPORT) child.updateRapport(a2, START_RELATIVE_RAPPORT) # Give enough food for the year tempFamily.food = (10 * 2 + 6 * childrenCount) * 4 # Pass time once without increasing the date self.passTime(False)
def run(self): c = Community() f1 = Family(c) f2 = Family(c) c.addFamily(f1) c.addFamily(f2) c.removeFamily(f2) f2 = c.newFamily() f1ByInd = c.getFamilyByIndex(0) p1 = Person([None, None], gender="male", family=f1) p2 = Person([None, None], gender="female", family=f1) p1.partner = p2 p2.partner = p1 p1.marryPartner() f1.addPerson(p1) f1.addPerson(p2) p3 = f1.generatePerson([p1, p2]) p3ByInd = f1.getPerson(2) p1.addChild(p3) p2.addChild(p3) p4 = f1.generatePerson([p1, p2]) f1.removePerson(p4) f1.addPerson(p4) f1.removePersonByIndex(3) p5 = Person([None, None], gender="male", family=f1) p6 = Person([None, None], gender="female", family=f1) p5.partner = p6 p6.partner = p5 p5.updateRapport(p6, -0.5) p5.breakUp() p5.calculateCap(p6) p5.likes(p6) p5.calculateRomanticInterest(p6) p5.romanticallyLikes(p6) p1.isRelative(p3) p1.firstName() p1.surname() p1.printableFullName() p1.setFirstName("test") p1.setSurname("test") p1.regenerateFirstName() p1.rapportStatus(p2) p1.friends() p1.countFriends() p1.getMood() p1.oneWordMood() p1.moodReasons() p1.father() p1.mother() p1.baseMood() p1.addModifier(2) p1.emotionality() p1.politicalOrientation() p1.sociopathy() p1.isChild() p1.getAttr("o") p1.explainCap(p2) p1.ageToString() p1.attributesToString() p1.attributesAsDescription() p1.otherAttributesAsDescription() p1.getLastInteractions(5) p1.compareTo(p2) p1.logKeyEvent("test") p1.inspect() p1.die() c.log("test message") f1.log("test message") allPeople = c.allPeople() c.passTime() c.graveyard()