Initial code commit
This commit is contained in:
commit
d8cb73fa6c
|
@ -0,0 +1,113 @@
|
|||
|
||||
# Created by https://www.gitignore.io/api/python
|
||||
# Edit at https://www.gitignore.io/?templates=python
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# End of https://www.gitignore.io/api/python
|
||||
|
||||
# save
|
||||
save.json
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 Robert 'khuxkm' Miles, https://khuxkm.tilde.team <khuxkm@tilde.team>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,18 @@
|
|||
# PearlyGates
|
||||
|
||||
A game about heaven and hell, where you get to be the scales of morality.
|
||||
|
||||
## How to play?
|
||||
|
||||
Download the repo, and run `main.py` in your Python interpreter of choice. Be
|
||||
sure to install the `tracery` library, which is required for the name generator.
|
||||
|
||||
## How to contribute?
|
||||
|
||||
For the most part, the code in `person.py` is self-explanatory. For example:
|
||||
|
||||
- To add a trait, add an entry to `TRAITS`.
|
||||
- To add a set of traits you cannot have at the same time (like misogynist and feminist), add an entry to `EXCLUSIONARY_TRAITS` (see comment above definition).
|
||||
- To change the average life expectancy, change `AVERAGE_LIFE_EXPECTANCY`.
|
||||
|
||||
The rest of the code may be poorly commented. Sorry, other coders (and future me).
|
|
@ -0,0 +1,120 @@
|
|||
import person, json, random
|
||||
|
||||
# save file defaults
|
||||
SAVE = dict(
|
||||
most_holy=dict(),
|
||||
most_sinful=dict(),
|
||||
last_20_holy_ages=[],
|
||||
last_20_sinful_ages=[],
|
||||
holy=0,
|
||||
sinful=0,
|
||||
intro=False,
|
||||
day=1
|
||||
)
|
||||
try:
|
||||
with open("save.json") as f:
|
||||
# load save file
|
||||
savefile = json.load(f)
|
||||
# if a key is missing from the save file it'll be set to the default automatically
|
||||
SAVE.update(savefile)
|
||||
except: pass
|
||||
|
||||
# convenience functions
|
||||
next_person = lambda: person.Person.generate()
|
||||
avg = lambda l: sum(l)/len(l)
|
||||
|
||||
print("Welcome to PearlyGates!")
|
||||
if not SAVE["intro"]:
|
||||
# display story intro fragment
|
||||
print()
|
||||
print("Saint Peter has retired, and has left you in charge of the gate to heaven.")
|
||||
print("Rather than being held to any standard, you are given full freedom to send")
|
||||
print("people to heaven or hell based on your arbitrary guidelines.")
|
||||
print()
|
||||
print("So, let's begin!")
|
||||
# don't show it on next startup
|
||||
SAVE["intro"]=True
|
||||
running=True
|
||||
while running:
|
||||
print("It is day {!s}.".format(SAVE["day"]))
|
||||
print("Would you like to:")
|
||||
print("1) See the dead people?")
|
||||
print("2) See stats")
|
||||
print("3) Quit")
|
||||
choice = None
|
||||
while choice is None:
|
||||
try:
|
||||
choice = int(input("? ").strip())
|
||||
assert choice in (1,2,3)
|
||||
except:
|
||||
print("Choose 1, 2, or 3.")
|
||||
choice=None
|
||||
if choice==2: # stats
|
||||
print("Stats:")
|
||||
print("-"*80)
|
||||
print("Amount of people sent to heaven all-time: {!s}".format(SAVE["holy"]))
|
||||
print("Average age of holy person (over last 20): {!s}".format(avg(SAVE["last_20_holy_ages"])))
|
||||
print("Top 5 most common holy traits:")
|
||||
# get all traits of people marked "holy"
|
||||
holy_traits = list(SAVE["most_holy"].items())
|
||||
# sort by occurrences descending
|
||||
holy_traits.sort(key=lambda x: -x[1])
|
||||
# display top 5
|
||||
for trait, count in holy_traits[:5]:
|
||||
print(f" - {trait} ({count!s})")
|
||||
print("-"*80)
|
||||
print("Amount of people sent to hell all-time: {!s}".format(SAVE["sinful"]))
|
||||
print("Average age of sinful person (over last 20): {!s}".format(avg(SAVE["last_20_sinful_ages"])))
|
||||
print("Top 5 most common sinful traits:")
|
||||
# get all traits of people marked "sinful"
|
||||
sinful_traits = list(SAVE["most_sinful"].items())
|
||||
# sort by occurences descending
|
||||
sinful_traits.sort(key=lambda x: -x[1])
|
||||
# show top 5
|
||||
for trait, count in sinful_traits[:5]:
|
||||
print(f" - {trait} ({count!s})")
|
||||
print("-"*80)
|
||||
elif choice==3:
|
||||
print("Goodbye!")
|
||||
running = False
|
||||
else:
|
||||
for i in range(random.randint(7,13)):
|
||||
# get new person and show summary
|
||||
p = next_person()
|
||||
print(p.toString())
|
||||
print("Send them to:")
|
||||
print("1) Heaven")
|
||||
print("2) Hell")
|
||||
choice = None
|
||||
while choice is None:
|
||||
try:
|
||||
choice = int(input("? ").strip())
|
||||
assert choice in (1,2)
|
||||
except:
|
||||
print("Choose 1 or 2.")
|
||||
choice=None
|
||||
if choice==1: # heaven
|
||||
print(p.pronoun,"smile"+('' if p.pronoun=="They" else "s"),"at you as",p.pronoun.lower(),"enter"+('' if p.pronoun=="They" else "s")+" the pearly gates.")
|
||||
# update stats
|
||||
SAVE["holy"]+=1
|
||||
SAVE["last_20_holy_ages"]=(SAVE["last_20_holy_ages"]+[p.age])[-20:]
|
||||
for trait in p.traits:
|
||||
if trait not in SAVE["most_holy"]:
|
||||
SAVE["most_holy"][trait]=1
|
||||
else:
|
||||
SAVE["most_holy"][trait]+=1
|
||||
else:
|
||||
print(p.pronoun,"scream"+('' if p.pronoun=="They" else "s"),"as",p.pronoun.lower(),"fall"+('' if p.pronoun=="They" else "s")+" into the depths of hell.")
|
||||
# update stats
|
||||
SAVE["sinful"]+=1
|
||||
SAVE["last_20_sinful_ages"]=(SAVE["last_20_sinful_ages"]+[p.age])[-20:]
|
||||
for trait in p.traits:
|
||||
if trait not in SAVE["most_sinful"]:
|
||||
SAVE["most_sinful"][trait]=1
|
||||
else:
|
||||
SAVE["most_sinful"][trait]+=1
|
||||
# dawn of the next day
|
||||
SAVE["day"]+=1
|
||||
|
||||
with open("save.json","w") as f:
|
||||
json.dump(SAVE,f)
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"first_name_male": ["James","John","Robert","Michael","William","David","Richard","Charles","Joseph","Thomas","Christopher","Daniel","Paul","Mark","Donald","George","Kenneth","Steven","Edward","Brian","Ronald","Anthony","Kevin","Jason","Matthew","Gary","Timothy","Jose","Larry","Jeffrey","Frank","Scott","Eric","Stephen","Andrew","Raymond","Gregory","Joshua","Jerry","Dennis","Walter","Patrick","Peter","Harold","Douglas","Henry","Carl","Arthur","Ryan","Roger","Joe","Juan","Jack","Albert","Jonathan","Justin","Terry","Gerald","Keith","Samuel","Willie","Ralph","Lawrence","Nicholas","Roy","Benjamin","Bruce","Brandon","Adam","Harry","Fred","Wayne","Billy","Steve","Louis","Jeremy","Aaron","Randy","Howard","Eugene","Carlos","Russell","Bobby","Victor","Martin","Ernest","Phillip","Todd","Jesse","Craig","Alan","Shawn","Clarence","Sean","Philip","Chris","Johnny","Earl","Jimmy","Antonio"],
|
||||
"first_name_female": ["Mary","Patricia","Linda","Barbara","Elizabeth","Jennifer","Maria","Susan","Margaret","Dorothy","Lisa","Nancy","Karen","Betty","Helen","Sandra","Donna","Carol","Ruth","Sharon","Michelle","Laura","Sarah","Kimberly","Deborah","Jessica","Shirley","Cynthia","Angela","Melissa","Brenda","Amy","Anna","Rebecca","Virginia","Kathleen","Pamela","Martha","Debra","Amanda","Stephanie","Carolyn","Christine","Marie","Janet","Catherine","Frances","Ann","Joyce","Diane","Alice","Julie","Heather","Teresa","Doris","Gloria","Evelyn","Jean","Cheryl","Mildred","Katherine","Joan","Ashley","Judith","Rose","Janice","Kelly","Nicole","Judy","Christina","Kathy","Theresa","Beverly","Denise","Tammy","Irene","Jane","Lori","Rachel","Marilyn","Andrea","Kathryn","Louise","Sara","Anne","Jacqueline","Wanda","Bonnie","Julia","Ruby","Lois","Tina","Phyllis","Norma","Paula","Diana","Annie","Lillian","Emily","Robin"],
|
||||
"first_name": ["#first_name_male#","#first_name_female#"],
|
||||
"lastname": ["Smith","Johnson","Williams","Brown","Jones","Miller","Davis","Garcia","Rodriguez","Wilson","Martinez","Anderson","Taylor","Thomas","Hernandez","Moore","Martin","Jackson","Thompson","White","Lopez","Lee","Gonzalez","Harris","Clark","Lewis","Robinson","Walker","Perez","Hall","Young","Allen","Sanchez","Wright","King","Scott","Green","Baker","Adams","Nelson","Hill","Ramirez","Campbell","Mitchell","Roberts","Carter","Phillips","Evans","Turner","Torres","Parker","Collins","Edwards","Stewart","Flores","Morris","Nguyen","Murphy","Rivera","Cook","Rogers","Morgan","Peterson","Cooper","Reed","Bailey","Bell","Gomez","Kelly","Howard","Ward","Cox","Diaz","Richardson","Wood","Watson","Brooks","Bennett","Gray","James","Reyes","Cruz","Hughes","Price","Myers","Long","Foster","Sanders","Ross","Morales","Powell","Sullivan","Russell","Ortiz","Jenkins","Gutierrez","Perry","Butler","Barnes","Fisher","Henderson","Coleman","Simmons","Patterson","Jordan","Reynolds","Hamilton","Graham","Kim","Gonzales","Alexander","Ramos","Wallace","Griffin","West","Cole","Hayes","Chavez","Gibson","Bryant","Ellis","Stevens","Murray","Ford","Marshall","Owens","Harrison","Ruiz","Kennedy","Wells","Alvarez","Woods","Mendoza","Castillo","Olson","Webb","Washington","Tucker","Freeman","Burns","Henry","Vasquez","Snyder","Simpson","Crawford","Jimenez","Porter","Mason","Shaw","Gordon","Wagner","Hunter","Romero","Hicks","Dixon","Hunt","Palmer","Robertson","Black","Holmes","Stone","Meyer","Boyd","Mills","Warren","Fox","Rose","Rice","Moreno","Schmidt","Patel","Ferguson","Nichols","Herrera","Medina","Ryan","Fernandez","Weaver","Daniels","Stephens","Gardner","Payne","Kelley","Dunn","Pierce","Arnold","Tran","Spencer","Peters","Hawkins","Grant","Hansen","Castro","Hoffman","Hart","Elliott","Cunningham","Knight","Bradley"],
|
||||
"stereotypical_male_name": ["#first_name_male# #lastname#"],
|
||||
"stereotypical_female_name": ["#first_name_female# #lastname#"],
|
||||
"random_name": ["#first_name# #lastname#"]
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
import tracery, json, sys, random, traceback, math
|
||||
|
||||
# name generator
|
||||
NAMES = None
|
||||
try:
|
||||
with open("name.json") as f: NAMES = tracery.Grammar(json.load(f))
|
||||
except Exception as e:
|
||||
if type(e)==FileNotFoundError:
|
||||
print("ERROR! name.json not found!",file=sys.stderr)
|
||||
print("Download `name.json` and place it in the same folder as `person.py`.",file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
sys.exit(-1)
|
||||
|
||||
# traits list
|
||||
TRAITS = """Transgender
|
||||
Homosexual
|
||||
Christian
|
||||
Muslim
|
||||
Hindu
|
||||
Jewish
|
||||
Plays video games
|
||||
Is a jerk to service workers (cashiers, waiters, etc)
|
||||
Is nice to service workers (cashiers, waiters, etc)
|
||||
Kicks puppies
|
||||
Might be a nazi?
|
||||
Racist
|
||||
Misogynist
|
||||
Feminist
|
||||
Bigoted
|
||||
Died rich
|
||||
Died poor""".splitlines()
|
||||
|
||||
# list of traits you can't have together
|
||||
# if a person is generated with more than one of the traits in a tuple,
|
||||
# they will reroll all but one of those traits
|
||||
EXCLUSIONARY_TRAITS = [
|
||||
("Hindu","Jewish","Muslim"),
|
||||
("Is a jerk to service workers (cashiers, waiters, etc)","Is nice to service workers (cashiers, waiters, etc)"),
|
||||
("Died rich","Died poor"),
|
||||
("Misogynist","Feminist")
|
||||
]
|
||||
|
||||
# average life expectancy
|
||||
# according to the UN, world life expectancy was ~72.6 in 2019, so we'll go with that
|
||||
AVERAGE_LIFE_EXPECTANCY=72.6
|
||||
|
||||
# pronouns list
|
||||
# male uses he/him, female uses she/her, and non-binary can use any pronoun in the list below
|
||||
PRONOUNS = """He
|
||||
She
|
||||
They
|
||||
Ze
|
||||
Xe
|
||||
Ve
|
||||
Vi""".splitlines()
|
||||
|
||||
class Person:
|
||||
GENDER_DESCRIPTORS = ["Male","Female","Non-binary"]
|
||||
def __init__(self,name,age,gender=0,traits=["Boring"]):
|
||||
self.name=name
|
||||
self.age=age
|
||||
self.gender=gender
|
||||
self.traits=traits
|
||||
# force gender-specific pronouns on binary genders
|
||||
if self.gender==0:
|
||||
self.pronoun="He"
|
||||
elif self.gender==1:
|
||||
self.pronoun="She"
|
||||
elif self.gender==2:
|
||||
# non-binary people get to have whatever pronoun they desire from the list
|
||||
self.pronoun=random.choice(PRONOUNS)
|
||||
def toString(self):
|
||||
# change gender number to gender descriptor
|
||||
gender = self.GENDER_DESCRIPTORS[self.gender]
|
||||
out=""
|
||||
out+=(f"{self.name}, {self.age}\n")
|
||||
out+=(f"Gender: {gender}\n")
|
||||
c = len(self.traits)
|
||||
out+=(f"Traits: ({c!s})\n")
|
||||
for trait in self.traits:
|
||||
out+=(f" - {trait}\n")
|
||||
return out.strip()
|
||||
def __str__(self):
|
||||
return self.toString()
|
||||
@classmethod
|
||||
def generate(cls):
|
||||
# pick a random gender
|
||||
gender = random.randint(0,len(cls.GENDER_DESCRIPTORS)-1)
|
||||
tag = "random_name"
|
||||
# ~67% chance name is guaranteed to be stereotypical of gender if gender in binary
|
||||
if gender==0:
|
||||
tag = random.choice(["stereotypical_male_name","stereotypical_male_name","random_name"])
|
||||
elif gender==1:
|
||||
tag = random.choice(["stereotypical_female_name","stereotypical_female_name","random_name"])
|
||||
# generate name from tracery grammar
|
||||
name = NAMES.flatten("#"+tag+"#")
|
||||
# pick age, skewed towards average life expectancy
|
||||
age = random.choice([math.floor,math.ceil])(random.triangular(5,100,AVERAGE_LIFE_EXPECTANCY))
|
||||
# pick 3-7 random traits
|
||||
traits = [random.choice(TRAITS) for i in range(random.randint(3,7))]
|
||||
check = True
|
||||
while check:
|
||||
check = False
|
||||
for trait_exc in EXCLUSIONARY_TRAITS:
|
||||
# indexes of conflicting traits
|
||||
ind = [i for i, c in enumerate(traits) if c in trait_exc]
|
||||
# if person doesn't have anything in the list, go to next
|
||||
if not ind: continue
|
||||
ind.pop(0) # keep first rolled trait
|
||||
if ind: # more than one?
|
||||
for i in ind:
|
||||
# reroll
|
||||
traits[i]=random.choice(TRAITS)
|
||||
check = True # check for conflicting traits again
|
||||
# don't allow duplicates
|
||||
c = len(traits) # length before
|
||||
traits = list(set(traits)) # remove duplicates
|
||||
while len(traits)<c: # if the list had duplicates
|
||||
check = True # make sure to check again after you...
|
||||
traits.append(random.choice(TRAITS)) # ...add new traits
|
||||
# return object
|
||||
return cls(name,age,gender,traits)
|
|
@ -0,0 +1 @@
|
|||
tracery>=0.1.1
|
Loading…
Reference in New Issue