Initial commit

This commit is contained in:
Tony Bark 2025-03-11 22:26:45 -04:00
commit f58a2e46bd
44 changed files with 4051 additions and 0 deletions

0
story/__init__.py Normal file
View file

114
story/censored_words.txt Normal file
View file

@ -0,0 +1,114 @@
BlowJob
Clit
Cock
CockSucker
Goddamned
MothaFucker
MothaFuker
MothaFukkah
MothaFukker
MotherFucker
MotherFukah
MotherFuker
MotherFukkah
MotherFukker
MuthaFucker
MuthaFukah
MuthaFuker
MuthaFukkah
MuthaFukker
Shitty
Shity
anus
ass
asshole
assholes
assjob
assrammer
asswipe
b1tch
bastard
bastards
bitch
bitches
blowjob
boobs
bullshit
butthole
buttwipe
clit
clits
cock
cockhead
cocks
cocksucker
cum
cunt
cunts
damn
dick
dildo
dildos
ejackulate
ejakulate
fatass
foreskin
fuck
fucka
fucker
fuckin
fucking
fucks
fuk
hell
hells
hentai
jackoff
jerkoff
jizz
masturbate
motherfucker
negro
nigga
niggas
nigger
niggr
nigur
niiger
niigr
nutsack
orgasm
penis
pussy
rectum
scrotum
semen
sex
sexy
shit
shits
shitting
shitted
slut
slutty
stripper
testical
testicle
tit
tits
titt
underage
vag1na
vagiina
vagina
vaj1na
vajina
vucking
whore
xrated
xxx
rape
rimjob
shitting
raped
raping

View file

@ -0,0 +1,37 @@
import json
import os
import tracery
from tracery.modifiers import base_english
def apply_grammar(key, rules):
grammar = tracery.Grammar(rules)
grammar.add_modifiers(base_english)
return grammar.flatten("#{}#".format(key))
def load_rules(setting):
with open(
os.path.join(
os.path.dirname(os.path.abspath(__file__)), "{}_rules.json".format(setting)
),
"r",
) as f:
rules = json.load(f)
return rules
def generate(setting, character_type, key):
"""
Provides a randomized prompt according to the grammar rules in <setting>_rules.json
"""
rules = load_rules(setting)
artefact = apply_grammar("{}_{}".format(character_type, key), rules)
return artefact
def direct(setting, key):
rules = load_rules(setting)
artefact = apply_grammar(key, rules)
return artefact

View file

@ -0,0 +1,85 @@
{
"rare_sense" : ["taste", "smell", "watch", "observe", "monitor", "look"],
"sense" : ["see", "hear", "sense", "feel", "notice", "#rare_sense#"],
"remember" : ["remember", "recall", "recollect"],
"think" : ["wonder", "#decide#", "#remember#", "realize", "imagine"],
"decide" : ["decide", "choose"],
"action" : ["#sense#", "#think#"],
"apocalypse_reason" : ["#mystic_reason#", "#real_reason#"],
"mystic_reason" : ["the gods punished the humanity for its sins", "the Hell came to the Earth", "the Prophecy of the Apocalypse turned out to be true"],
"real_reason" : ["the #bombader# bombed your country to the ground", "the environmental catastrophe of the #catastrophe_adj# #environmental_catastrophe#", "the #catastrophe_adj# #plague#"],
"bombader" : ["its own government", "American", "Chinese", "Russian", "French", "British", "Indian", "North-Korean", "#evil_adj# scientist", "#evil_adj# businessman"],
"evil_adj" : ["evil", "ruthless", "mad", "lunatic", "reckless", "cruel"],
"environmental_catastrophe" : ["floods", "hurricans", "earthquakes"],
"catastrophe_adj" : ["deadly", "unstopable", "great"],
"plague" : ["plague", "sickness", "epidemic", "pandemic"],
"old_metal_adj" : ["old", "rusty", "broken", "crappy"],
"old_metal_adj_opt" : ["#old_metal_adj# ", ""],
"old_cloth_adj" : ["old", "torn", "crappy", "dirty"],
"old_cloth_adj_opt" : ["#old_cloth_adj# ", ""],
"character_name" : ["Aarav", "Abra", "Adaiah", "Addison", "Adrian", "Adriel", "Aharon", "Aitan", "Akiva", "Alder", "Aleks", "Aleksa", "Aleksia", "Alijah", "Altair", "Alvaro", "Amity", "Amzi", "Andromeda", "Apollo", "Aram", "Arava", "Arbor", "Arcadia", "Archer", "Arden", "Argider", "Ariadne", "Arkadi", "Arkady", "Arke", "Arlo", "Armani", "Arza", "Ashe", "Asher", "Ashlen", "Ashtyn", "Ashyra", "Aster", "Avalon", "Avi", "Aviva", "Azariah", "Azra", "Bandit", "Beck", "Beckett", "Beckham", "Berke", "Beverly", "Blanche", "Blythe", "Boheme", "Brandt", "Bravo", "Briar", "Bridger", "Briggs", "Brinley", "Britt", "Bronwen", "Bryn", "Caden", "Cael", "Cairo", "Calder", "Callum", "Caradoc", "Carlye", "Caro", "Carter", "Carter", "Carver", "Cassidy", "Cathal", "Cathan", "Cato", "Cedar", "Ceil", "Chava", "Chosen", "Ciar", "Ciji", "Cillian", "Circe", "Cleo", "Cleve", "Clio", "Clovis", "Codi", "Colter", "Colton", "Cora", "Creed", "Crew", "Crow", "Cruz", "Cy", "Cyran", "Dahlia", "Dakota", "December", "Declan", "Delaney", "Delta", "Denver", "Destry", "Deva", "Deveraux", "Devrim", "Devyn", "Dhani", "Djuna", "Dmitri", "Dov", "Dune", "Easton", "Echo", "Eli", "Elizaveta", "Else", "Ember", "Ember", "Emre", "Emry", "Enoch", "Ensley", "Erskine", "Eryn", "Eshe", "Eszti", "Evadne", "Everett", "Everly", "Evron", "Evrose", "Explorer", "Ezri", "Falconer", "Fallon", "Faust", "Fawke", "Felix", "Fielder", "Finch", "Fischer", "Foster", "Fox", "Ginger", "Gunner", "Hadleigh", "Hadley", "Halcyon", "Haleigh", "Halloran", "Harlem", "Harlow", "Harte", "Henri", "Hero", "Holden", "Holland", "Horus", "Hunter", "Icarus", "Indie", "Irving", "Ivalo", "Ive", "Ivo", "Izaiah", "Jasper", "Jericho", "Jezebel", "Jinx", "Joji", "Jovan", "Jovie", "Jupiter", "Kaatje", "Kacey", "Kafka", "Kahlo", "Kai", "Kasper", "Katja", "Kavan", "Keaton", "Keenan", "Keeva", "Kenji", "Kensington", "Kenza", "Kerrigan", "Kessie", "Keverne", "Keyne", "Kezia", "Keziah", "Kieran", "Kingsley", "Kingston", "Kinsey", "Kipp", "Kitto", "Kiva", "Kjell", "Knox", "Kwame", "Kyah", "Kyler", "Kyra", "Laiken", "Laine", "Lake", "Lancaster", "Lander", "Landon", "Lash", "Lazarus", "Legend", "Lennox", "Lev", "Levi", "Leviathan", "Liam", "Locke", "Lourdes", "Lujza", "Lykke", "Lynx", "Maddox", "Maire", "Majken", "Malachi", "Mallory", "Malo", "March", "Marina", "Marine", "Marjo", "Marjorie", "Marley", "Marley", "Marlo", "Marlo", "Mateo", "Mathilde", "Maverick", "Mavon", "Mavra", "Maxfield", "Mazarine", "Meike", "Mekhi", "Merc", "Merce", "Mercedes", "Mercer", "Mercy", "Mesa", "Messiah", "Micah", "Milo", "Mitya", "Monroe", "Morrigan", "Moshe", "Nakotah", "Navy", "Nazareth", "Nevaeh", "Nevara", "Neve", "Neviah", "Niamh", "Nicola", "Nixi", "Nixie", "Oakes", "Okello", "Orion", "Ozias", "Pagan", "Pascale", "Pastor", "Paxton", "Penn", "Peyton", "Piper", "Porter", "Presley", "Proctor", "Quince", "Quinn", "Raiden", "Rainer", "Raiza", "Raleigh", "Ransom", "Raphael", "Raven", "Ravi", "Reagan", "Reeve", "Regan", "Reign", "Reign", "Reinhardt", "Ren", "Reno", "Revel", "Reverie", "Rhett", "Rhyatt", "Rhys", "Riet", "Ripley", "Rivage", "River", "Rivka", "Rivo", "Roan", "Rocco", "Roe", "Rogue", "Rory", "Roscoe", "Rowan", "Rue", "Rune", "Ryder", "Ryleigh", "Sade", "Saga", "Saint", "Sakae", "Saoirse", "Savita", "Sawyer", "Sayer", "Seiji", "Shaviv", "Shirley", "Shivani", "Sian", "Silas", "Silje", "Simone", "Sinclair", "Sinjon", "Svea", "Sy'Rai", "Sylvester", "Szymon", "Tai", "Taj", "Takeo", "Tarak", "Taye", "Teal", "Teasagh", "Thea", "Theodrekr", "Thorne", "Tiaamii", "Tierney", "Tikvah", "Tove", "True", "Tucker", "Tycho", "Vale", "Valen", "Valkyrie", "Vashti", "Veer", "Vihaan", "Viktorie", "Violante", "Vito", "Viva", "Viveka", "Volker", "Voltaire", "Vrai", "Walker", "Warner", "Warren", "Waverly", "Waylon", "West", "Westley", "Weston", "Wilder", "Xanthe", "Xaviera", "Xzavier", "Zachariah", "Zahava", "Zahavi", "Zahraa", "Zaki", "Zander", "Zariah", "Zen", "Zhivago", "Zimran", "Zinedine", "Zipporah", "Zocha", "Zofka", "Zvi", "Zyla"],
"adverb" : ["finally", "suddenly", "surprisingly", "a day later"],
"end_sentence" : ["You #action#", "#adverb.capitalize#, you #action#"],
"apocalypse_context" : "You are <NAME>, #character_type.a# trying to survive after #apocalypse_reason#. You have #item1# and #item2#.",
"day" : ["long day", "long #time#", "few hours", "routine #time#"],
"stay_time" : ["the evening", "#few# of hourse", "#few# days"],
"time" : ["morning", "afternoon", "evening"],
"abandoned_adj" : ["abandoned", "old", "forsaken", "ravaged"],
"few" : ["a couple of", "some", "a few"],
"colony" : ["colony", "city", "village", "town", "metropolis"],
"scaveged_item" : ["car", "truck", "cottage", "house", "bunker", "camper"],
"food_type" : ["meat", "bean", "corn"],
"worthy_metal" : ["gold", "silver", "platinum", "silicium"],
"found_item" : ["nothing", "#few# cans of #food_type#", "#few# pieces of #worthy_metal#", "#scavenger_item.a#"],
"scavenger_action" : ["You decide to stay there for #stay_time#." , "You head toward the nearby #colony#."],
"scavenger_item" : ["#old_metal_adj_opt#crowbar", "#old_metal_adj_opt#handspike", "#old_metal_adj_opt#knife", "#old_metal_adj_opt#rifle", "#old_metal_adj_opt#gun", "#old_cloth_adj_opt#backpack", "#old_metal_adj_opt#canteen", "#old_cloth_adj_opt#gas mask", "#old_metal_adj_opt#lockpick", "#old_cloth_adj_opt#cloak", "#old_cloth_adj_opt#hat"],
"scavenger_context" : "#[character_type:lonely scavenger][item1:#scavenger_item.a#][item2:#scavenger_item.a#]apocalypse_context#",
"scavenger_prompt" : "You spend a #day# trying to get out as much as you can from a #abandoned_adj# #scaveged_item#. At the end you find #found_item#. #scavenger_action# #end_sentence#",
"body_side" : ["left", "right"],
"finger_number" : ["two", "three", "four", "six", "seven", "eight"],
"young_age" : ["eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen"],
"since_young_age" : "since you were #young_age#",
"body_side_or_double" : ["#body_side# #part#", "#part.s#"],
"body_part" : ["#[part:hand]body_side_or_double#", "#[part:#arm_or_leg#]body_side_or_double#", "face", "back", "whole body", "chest"],
"fingers_or_toes" : ["#finger_number# fingers on your #[part:hand]body_side_or_double#", "#finger_number# toes on your #[part:leg]body_side_or_double#"],
"arm_or_leg" : ["arm", "leg"],
"mistakes" : ["misbehaving", "mistakes", "faults"],
"master" : ["guard", "master", "supervisor"],
"subject" : ["science", "history", "literature", "maths"],
"god" : ["god", "deity", "divine being"],
"bad_treatment" : ["But you manage to escape today from your #master#.", "You get flagged today for your #mistakes# by your #master#.", "You don't get any food for your #mistakes# today from your #master#."],
"mutant_condition" : ["#fingers_or_toes#", "fur on your #body_part#", "feather on your #body_part#", "scales on your #body_part#", "glandular skin on your #body_part#", "a third #arm_or_leg#", "different colored eyes", "snake like tongue", "spikes on your #body_part#"],
"mutant_bad_prompt_reason" : ["a sin", "a curse", "a punishment", "a crime", "an atrocity", "a scandal", "a sign of inferiority"],
"mutant_bad_prompt_effect" : ["enslaved #since_young_age#. #bad_treatment#", "in prison #since_young_age#. #bad_treatment#", "banished #since_young_age#. After a long journey, you find a #abandoned_adj# #scaveged_item#."],
"mutant_bad_prompt" : "#mutant_bad_prompt_reason#, and you have been #mutant_bad_prompt_effect#",
"mutant_good_prompt_reason" : ["a virtue", "a blessing", "a sign of supremacy", "a praise"],
"mutant_good_prompt_effect" : ["get the best education possible. You go to #subject# class today.", "are treated as a #god#. You prepare for a holy ceremony."],
"mutant_good_prompt" : "#mutant_good_prompt_reason#, and you #mutant_good_prompt_effect#",
"mutant_bad_or_good_prompt" : ["#mutant_bad_prompt#", "#mutant_good_prompt#"],
"mutant_prompt" : "In the #colony# you were born in, your strange condition was considered #mutant_bad_or_good_prompt# #end_sentence#",
"mutant_context" : "#[character_type:mutant][item1:#mutant_condition#][item2:#mutant_condition#]apocalypse_context#",
"pub" : ["pub", "bar", "louge", "saloon"],
"pub_with_adj" : "#old_building_adj_opt##pub#",
"leave" : ["walk out of", "leave", "go out of", "rush out of"],
"mission" : ["mission", "quest", "assignment", "operation"],
"device" : ["car", "truck", "motorbike", "motorcycle", "camper"],
"target_location" : ["somewhere here", "in a far away #colony#", "in a nearby #colony#", "in a currently unknown location"],
"elite_proffesion" : ["scientist", "captain", "mayor", "officer", "conspirator", "gang leader", "doctor", "journalist"],
"somebody" : ["merchant", "stranger", "girl", "mercenary", "scavenger", "technician", "old friend"],
"somebody_opt" : ["#somebody.a#", "#somebody.s#"],
"with_somebody" : "with #somebody_opt#",
"with_somebody_opt" : [" #with_somebody#", ""],
"pub_action" : ["are sitting#with_somebody_opt#", "are drinking#with_somebody_opt#", "are watching some dancers#with_somebody_opt#", "are gambling#with_somebody_opt#", "are trying to make a deal #with_somebody#"],
"old_building_adj" : ["old", "cheap", "infamous", "ravaged", "dirty"],
"old_building_adj_opt" : ["#old_building_adj# ", ""],
"leaving_reason" : ["feel bored", "realize that you have no money left", "need to get going", "get insulted by #somebody_opt#"],
"headhunter_item" : ["#old_metal_adj_opt#knife", "#old_metal_adj_opt#machete", "#old_metal_adj_opt#rifle", "#old_metal_adj_opt#gun", "#old_cloth_adj_opt#gas mask", "#old_cloth_adj_opt#cloak", "#old_metal_adj_opt#handgun", "#old_metal_adj_opt#shotgun", "#old_metal_adj_opt#grenade", "#old_metal_adj_opt#binoculars", "#old_metal_adj_opt#scope"],
"headhunter_action" : ["capture", "find", "kill", "round up", "murder", "assasinate"],
"headhunter_status" : ["You #pub_action# in #pub_with_adj.a#, but you #leaving_reason#. You #decide# to #leave# the building.", "You are driving your #old_metal_adj_opt##device#. You go past many #abandoned_adj# #scaveged_item.s#. You arrive at #colony.a# and stop the engine."],
"headhunter_mission" : "You are on #mission.a# to #headhunter_action# #elite_proffesion.a# named #character_name#. Your target lives #target_location#.",
"headhunter_prompt" : "#headhunter_status# #headhunter_mission#\n\n#end_sentence#",
"headhunter_context" : "#[character_type:headhunter][item1:#headhunter_item.a#][item2:#headhunter_item.a#]apocalypse_context#"
}

View file

@ -0,0 +1,237 @@
{
"rare_sense" : ["taste", "smell", "watch", "observe", "monitor", "look"],
"sense" : ["see", "hear", "sense", "feel", "notice", "#rare_sense#"],
"remember" : ["remember", "recall", "recollect"],
"think" : ["wonder", "decide", "#remember#", "realize", "imagine"],
"action" : ["#sense#", "#think#"],
"to_fro" : ["to and fro", "back and forth"],
"two_to_ten" : ["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"],
"realm" : ["realm", "kingdom", "country", "province", "land", "duchy", "barony"],
"season" : ["spring", "summer", "winter", "autumn"],
"animal" : ["lion", "elk", "badger", "fox", "raven", "goat", "wolf", "dove"],
"royalty" : ["king", "queen", "princess", "prince"],
"nobility" : ["duke", "duchess", "lord", "lady", "count", "countess", "baron", "baroness"],
"from_fantasy" : "from the #realm# of #fantasy_name#",
"fantasy_name" : "#character_name#",
"character_name" : ["Larion", "Vijeh", "Francia", "Paiva", "Mederos", "Radu", "Hatami", "Shirish", "Saralyn", "Leka", "Rukaj", "Nardis", "Isett", "Jacczak", "Hamma", "Narala", "Alstine", "Gimello", "Elsbury", "Rubino", "Misra", "Paterno", "Gassan", "Galardo", "Raeder", "Garriel", "Routh", "Bindi", "Renfro", "Harnid", "Enlou", "Amato", "Zurito", "Dimyan", "Arteaga", "Isgrigg", "Maida", "Mudra", "Beranek", "Aric", "Sadri", "Javan", "Wedriwin", "Umiemma", "Thaosean", "Alilawia", "Gwoia", "Galuswen", "Astedrinyth", "Wicolian", "Ziadan", "Thema", "Unirakon", "Severiveth", "Onalath", "Aaolla", "Airadan", "Legiallan", "Zayhan", "Afania", "Ibalegord", "Oligolind", "Celadon", "Alaleria", "Ocelith", "Eowaoviel", "Brigobard", "Griwen", "Frykoth", "Crilawen", "Memas", "Adrardong", "Nomaf", "Crirabeth", "Cadaed", "Broethien", "Astok", "Seraria", "Dreratlan", "Frireven", "Birahan", "Horeria", "Areriw", "Jerenia", "Alaodan", "Paeviel", "Cigowyr", "Lariesa", "Eroreth", "Sevoan", "Careg", "Thoijan", "Raywen", "Seikor", "Wilini", "Alerradon", "Unerrarith", "Agrohawyn", "Zilirith", "Brilann", "Eliawien", "Wirakor", "Gaeven", "Kiewin", "Umalia", "Prirep", "Rhalebeth", "Aaylin", "Qelindra", "Adwayder", "Rothien", "Brendabaen", "Galeliven", "Ethaykon", "Waowiel", "Qerijan", "Aavia", "Kaeinnon", "Rhycia", "Gligobard", "Zolle", "Sear", "Haeasien", "Fiakor"],
"town_name" : ["clearkeep", "faybury", "hazelpoint", "scorchfort", "earthpost", "mossband", "deadhelm", "dragonwick", "stormwood", "rosetide", "flatpass", "winteryard", "brinehaven", "springpost", "maplestar", "cavebell", "baregrave", "swampbrook", "wildescar", "nightwind", "oxgate", "lakeford", "cavecliff", "moonbell", "bearwater", "dimchill", "silkrun", "summerbourne", "bayvein", "rockspire", "sandbourne", "glimmerpond", "swyncoast", "snowham", "glassice", "starryholt", "ashhedge", "starryice", "estercastle", "wellpine", "fairfort", "newhill", "miststone", "glasshedge", "silveroak", "byburn", "oakpond", "whitehill", "butterway", "shadowmist", "highpond", "goldcliff", "southstone", "snowhaven", "fayham", "violetwick", "mallowdell", "bluegrass", "coldholt", "woodcourt", "ironmarsh", "brightiron", "beechhollow", "welllyn", "mallowshadow", "goldlight", "fayfall", "mormont", "wyvernburn", "greenmeadow", "wellwood", "westerrose", "westerelf", "snowdeer", "orport", "greenelf", "riverbridge", "highhedge", "mallowholt", "blueglass", "snowbeach", "goldview", "lorview", "rockhedge", "witchmoor", "fallmont", "linacre", "northby", "clearmaple", "woodbridge", "coldmaple", "vertville", "springacre", "shadowsage", "southkeep", "bymeadow", "wolfpond", "woodmallow", "flowerhall", "riverhall", "northwheat", "fallland", "brightland", "orcastle", "bymist", "aldfair", "pryborough", "fairway", "esterapple", "lochland", "newsummer", "landgate", "beachstone", "fairmere", "westwilde", "snowway", "lochoak", "esternesse", "summerriver", "starrylake", "icefort", "newrock", "landbridge", "vertlea", "courtland", "bygriffin", "byhollow", "lochmill", "brightbeach", "mallowhaven", "shadowgold", "deepcrest", "wellbarrow", "summerlake", "waterwick", "summercliff", "bluehurst", "marblerose", "dragonlake", "lightflower", "westerspring", "fairport", "lochby", "wayness", "deernesse", "greyrock", "dellmeadow", "morcliff", "mallowmarsh", "crystalmill", "normont", "fallborough", "flowerlea", "glassmerrow", "aelfort", "greydell", "mallowmarble", "deepsummer", "starryfog", "foxmoor", "deepbell", "highhaven", "seamoor", "brightton", "blackacre", "butterfox", "corburn", "butterhedge", "swynbourne", "dorbank", "shadowkeep", "wildehall", "greenburn", "eastland", "wheathall", "blueholt", "edgenesse", "courtley", "summerby", "pryshore", "edgehaven", "crystalham"],
"creature" : ["bugbear", "centaur", "chimera", "cockatrice", "cyclops", "demon", "devil", "dragon", "dryad", "dwarf", "elemental", "elf", "faun", "giant", "gnome", "goblin", "golem", "gorgon", "griffon", "harpy", "hell hound", "hobgoblin", "imp", "kobold", "lycanthrope", "manticore", "merfolk", "minotaur", "naga", "ogre", "pegasus", "roc", "selkie", "spectre", "troll", "unicorn", "vampire", "wight", "wraith", "zombie"],
"village" : ["village", "town", "city", "hamlet"],
"village_adj" : ["charming", "sleepy", "little", "small", "bustling", "quaint", "industrious", "festive", "remote", "secluded", "nearby", "close by"],
"village_adj_opt" : ["#village_adj# ", ""],
"village_full" : "the #village_adj_opt##village# of #town_name.capitalize#",
"farmer" : ["ackerman", "cowherd", "crofter", "dairymaid", "dung carter", "farmer", "gardener", "goatherd", "hayward", "herder", "ostler", "plowman", "reapers", "sheepshearer", "shepherd", "swineherd", "thresher", "tillerman", "woodcutter", "woolcomber", "woolman"],
"hunter" : ["climmer", "falconer", "fewterer", "forester", "fowler", "gamekeeper", "hawker", "hunter", "huntsman", "master of hounds", "molecatcher", "parker", "rat catcher", "sperviter", "trapper"],
"fisher" : ["fisher", "fisherman", "leech-collector", "oyster raker", "oysterer", "seaweed harvester"],
"artist" : ["artist", "artisan", "fresco painter", "glasspainter", "limner", "painter", "sculptor"],
"writer" : ["composer", "illuminator", "limner", "playwright", "poet", "writer"],
"profession" : ["#writer#", "#artist#", "#fisher#", "#hunter#", "#farmer#"],
"color" : ["black", "white", "red", "gray", "blue", "#uncommon_color#"],
"uncommon_color" : ["green", "brown", "green", "brown", "golden", "silver", "scarlet"],
"weapon" : ["staff", "sword", "spear", "flail", "mace", "dagger", "bow", "arrow", "lance"],
"relative" : ["father", "mother", "brother", "sister", "cousin", "uncle", "aunt"],
"noble_item" : ["pouch of gold", "pouch of silver", "small dagger", "fine cane", "house seal", "narrow rapier", "vial of perfume", "pendant", "silk shirt", "leather purse", "map of the #realm#", "pair of spectacles", "land deed", "comb"],
"subordinate" : ["servant", "cook", "laborer", "groundskeeper", "housekeeper", "attendant", "serf", "hireling"],
"nice_home" : ["keep", "castle", "manor", "mansion", "abbey", "estate", "tower"],
"he_she" : ["he", "she"],
"a_the" : ["a", "the", "your"],
"attacked" : ["under attack", "being invaded", "in danger", "being surrounded", "being sieged"],
"noble_one" : ["One morning, you are awakened by one of your #subordinate.s#. #he_she.capitalize# tells you that your #nice_home# is #attacked#. You look out #a_the# window and see"],
"element" : ["fire", "water", "earth", "air"],
"more_element" : ["the underworld", "the sea", "the heavens"],
"celestial" : ["the sun", "the moon", "the stars"],
"domain" : ["#element#", "#more_element#", "#celestial#", "harvest", "fertility", "war", "good fortune", "thresholds", "love", "wisdom", "#profession.s#"],
"god" : ["god", "deity", "goddess"],
"god_adj" : ["just", "cruel", "omniscient", "all-knowing", "mischevious", "wise", "reckless", "omnipotent", "#color#", "prudent", "jealous", "kind", "graceful", "magnificent", "traditional", "famous", "new", "old", "ancient", "wrathful", "fruitful", "gracious"],
"deity" : ["#god# of #domain#", "patron #god# of #domain#", "#god_adj# #god# #fantasy_name#"],
"group" : ["group", "bunch", "delegation", "host"],
"humanoid" : ["elf", "dwarf", "gnome", "halfling", "hobbit", "goblin"],
"noble_class" : ["trader", "noble", "merchant", "soldier", "captain", "aristocrat", "duke", "count", "lord", "priest"],
"attrib" : ["boisterous", "dignified", "stately", "drunken", "cheerful", "somber", "shifty", "tired-looking"],
"char" : ["#noble_class#", "#humanoid#"],
"character" : ["#char#", "#attrib# #char#"],
"toast" : ["offers a toast", "gives a speech", "recites a prayer", "announces future plans", "loudly argues", "sings a ballad"],
"grand_home_adj" : ["great", "grand", "large", "spacious"],
"grand_home_adj_opt" : ["#grand_home_adj# ", ""],
"noble_location" : ["with #group.a# of #character.s#", "in a #grand_home_adj_opt##nice_home#"],
"festival" : ["#season# festival", "festival of the #deity#", "festival of the #animal#", "festival of the year of the #animal#"],
"season_opt" : ["#season# ", ""],
"noble_two" : "Throughout the #realm# it is the #festival#. To celebrate, you are feasting #noble_location#. #subordinate.s.capitalize# bustle #to_fro#.\n\nAs #character.a# #toast#, you #action#",
"week_month" : ["week", "fortnight", "month"],
"week_months" : ["weeks", "fortnights", "months"],
"time_periods" : ["#week_month#", "#two_to_ten# #week_months#"],
"chat" : ["speak", "chat", "talk", "converse"],
"ask" : ["ask", "request", "demand"],
"noble_organization" : ["organization", "delegation", "guild", "league", "faction"],
"matter" : ["about a matter of importance to the #realm#", "on behalf of a foreign #noble_organization# of #humanoid.s#", "with information regarding a rival house", "with demands from some #professioe.s#"],
"trustworthy" : ["trustworthy", "reliable", "dependable", "well-intentioned", "worth your time"],
"noble_three" : "Every day for the past #week_month#, a certain #character# has come to your #grand_home_adj_opt##nice_home# #ask#ing to #chat# with you, apparently #matter#. Unsure if #he_she# is #trustworthy#, you finally agree to meet.\n\nAt the meeting, you #action#" ,
"noble_four" : "You and your bodyguards are #on_the_way# #towards# #village_full# to celebrate the #festival# with your subjects. You #road_encounter#.\n\nYou #action#",
"noble_prompt": ["#noble_one#", "#noble_two#", "#noble_three#", "#noble_four##"],
"noble_context" : "You are <NAME>, a noble #from_fantasy#. You have #noble_item.a# and #noble_item.a#.",
"crest" : ["crest of the #deity#", "#animal# crest"],
"old_paper_adj" : ["wrinkled", "torn", "tattered", "creased"],
"metal_adj" : ["rusty", "shiny", "steel", "iron", "bronze", "tarnished", "gleaming"],
"metal_adj_opt" : ["#metal_adj# ", "#old_object_adj# ", ""],
"old_object_adj" : ["old", "worn", "dusty"],
"good_object_adj" : ["sturdy", "trusty"],
"object_adj" : ["#old_object_adj#", "#good_object_adj#"],
"normal_name" : ["Steve", "Bob", "Richard", "Susan", "Deborah", "Lily"],
"mount" : ["horse", "pony", "donkey", "camel", "mare", "stallion"],
"knight_item" : ["#metal_adj_opt#shield", "shield inscribed with the #crest#", "#metal_adj_opt#helmet", "#metal_adj_opt#sword", "#metal_adj_opt#lance", "#mount#", "squire named #normal_name#", "map of the #realm#", "food ration", "leather saddle"],
"towards" : ["to", "towards"],
"you_ve" : ["you", "you've", "you have"],
"kill" : ["kill", "defeat", "slay", "destroy", "subdue", "strike down", "smite"],
"evil_adj" : ["cursed", "evil", "wretched", "wicked", "accursed", "vile", "nefarious", "cruel", "tyrannical"],
"monster" : ["dragon", "behemoth", "serpent", "creature", "beast", "horror", "monster", "fiend"],
"he_she_it" : ["he", "she", "it"],
"cardinal_dir" : ["north", "south", "east", "west"],
"the_your" : ["the", "your"],
"place_adj" : ["dark", "endless", "strange", "gloomy", "misty", "muddy", "barren", "gray", "creepy"],
"place" : ["valley", "forest", "plain", "mountain", "swamp", "river", "cave", "cavern", "lake", "expanse", "cliff"],
"monster_quest" : ["#kill# the #evil_adj# #monster# of Larion. #you_ve.capitalize# heard #he_she_it# lives to the #cardinal_dir# of #the_your# #realm#"],
"artifact_adj" : ["lost", "holy", "gold", "fabled", "mythic", "legendary", "blessed", "cursed", "forgotten", "#color#"],
"artifact" : ["grail", "fleece", "gauntlet", "banner", "stone", "sword", "helmet", "ring", "book", "chalice", "tome", "gem", "bow", "scepter", "staff"],
"whole_artifact" : "#artifact_adj# #artifact# of #fantasy_name#",
"find" : ["find", "discover", "recover"],
"rumored" : ["is rumored to", "is said to", "was prophesied to"],
"artifact_description" : ["#rumored# have great power", "#rumored# aid the righteous", "#rumored# have belonged to the dead #royalty#", "#rumored# to ward away evil"],
"artifact_quest" : "#find# the #whole_artifact#, which #artifact_description#",
"magical_role" : ["wizard", "necromancer", "sorcerer", "warlock", "enchanter", "diviner"],
"evil_role" : ["witch", "necromancer", "lich", "sorcerer"],
"evil_humanoid" : ["ogre", "troll", "giant", "orc", "goblin"],
"kidnapper" : ["#evil_adj# #monster#", "#evil_adj# #evil_humanoid#", "#evil_adj# #evil_role#"],
"bad_place" : ["den", "cave", "clutches", "grasp", "#nice_home#", "prison", "hall", "fortress", "catacombs"],
"victim" : ["#royalty#", "#nobility# of #fantasy_name#"],
"victim_adj" : ["young", "youthful", "beautiful", "poor", "well-loved"],
"victim_adj_opt" : ["#victim_adj# ", ""],
"rescue" : ["save", "rescue", "free", "liberate", "deliver", "set free"],
"bad_guys_place" : ["#bad_place# of the #kidnapper#", "#kidnapper#'s #bad_place#"],
"rescue_quest" : "#rescue# the #victim_adj_opt##victim# from the #bad_guys_place#",
"quest_type" : ["#monster_quest#", "#artifact_quest#", "#rescue_quest#"],
"could" : ["will", "may", "should", "could"],
"adventure" : ["adventure", "quest", "journey", "crusade"],
"beginning" : ["going on", "beginning", "setting out on"],
"quest" : ["You are #beginning# #adventure.a# to #quest_type#. You set out #towards# #place_adj.a# #place# that #could# take you there.\n\nAs you approach, you #action#"],
"knight_prompt" : "#quest#",
"knight_context" : "You are <NAME>, a knight #from_fantasy#. You have #knight_item.a# and #knight_item.a#.",
"random_object" : ["#animal#", "#evil_role#", "#evil_humanoid#", "#monster#", "#humanoid#", "#profession#"],
"mind" : ["mind", "head", "memory", "consciousness"],
"flicker" : ["flicker", "echo", "dance", "flit", "jump"],
"looks_like" : ["what looks like", "what appears to be"],
"looks_like_opt" : ["#looks_like# ", ""],
"shape" : ["circular", "spherical", "oval", "triangular", "pyramidal", "square", "concave","hexagonal", "octagonal"],
"shape_opt" : ["#shape# ", ""],
"encounter" : ["encounter", "come upon", "run into", "cross paths with", "see"],
"wood" : ["wooden", "oak"],
"alt_element" : ["ice", "wind", "metal"],
"potion_appearance" : ["ruby", "pink", "orange", "yellow", "emerald", "dark", "green", "cyan", "sky", "blue", "brilliant", "blue", "magenta", "purple-red", "puce", "milky", "swirly", "bubbly", "smoky", "cloudy", "effervescent", "black", "golden", "brown", "fizzy", "dark", "white", "murky"],
"wizard_animal" : ["crow", "raven", "hawk", "chameleon", "snake", "lizard", "cat", "kitten"],
"beaker" : ["beaker", "flask", "vessel"],
"potion" : ["#beaker# of #potion_appearance# liquid", "#potion_appearance# potion"],
"wizard_item" : ["staff", "crystal ball", "#potion#", "#book#", "cloak", "pet #wizard_animal#", "pointed hat", "quill", "#shape_opt#amulet"],
"dnd_school" : ["abjuration", "conjuration", "divination", "enchantment", "evocation", "illusion", "necromancy", "transmutation"],
"magic_book" : ["spellbook", "tome", "scroll"],
"spellbook" : ["#magic_book# of #dnd_school#"],
"history" : ["history", "wars", "politics", "culture", "nuances", "complexity", "barbarism"],
"subject" : ["#creature.s#", "the #history# of the #realm.s# of #fantasy_name#", "the history of #creature.s# in the #realm# of #fantasy_name#"],
"normal_book" : ["book about #creature.s#", "book about the history of the #realm.s# of #fantasy_name#"],
"book" : ["#normal_book#", "#spellbook#", "book", "spellbook"],
"magic_discipline" : ["alchemy"],
"through" : ["through", "across", "throughout", "about"],
"wizard_drafts" : ["After #week_month.a# of travel you arrive at the ruin ", "Your apprentice has done it again; #he_she# has summoned a being "],
"road" : ["road", "river", "path"],
"wizard_exchange" : ["notes", "spells", "gossip", "goods", "research", "banter", "knowledge"],
"wizard_colleage" : ["another wizard", "a fellow wizard", "a sorcerer", "a #evil_role#", "a #attrib# #char#"],
"on_the_way" : ["on the way", "on your way", "on the #road#"],
"wandering" : ["traveling", "wandering", "errant", "roaming", "roving", "meandering"],
"wander" : ["travel", "wander", "roam", "rove", "meander"],
"wandering_opt" : ["#wandering# ", ""],
"troupe" : ["group", "band", "troupe", "crew", "gang", "party"],
"dancer" : ["dancer", "actor", "entertainer"],
"road_encounter" : ["#encounter# #looks_like_opt##creature.a#", "#encounter# a #wandering_opt#troupe of #profession.s#"],
"wizard_one" : ["You are #on_the_way# #towards# #village_full# to exchange #wizard_exchange# with #wizard_colleage#. You #road_encounter#.\n\nYou #action#"],
"old" : ["old", "aged", "experienced", "elderly", "venerable"],
"around_opt" : [" about", " around", ""],
"wizard_pos" : ["wise", "intelligent", "crafty", "powerful", "magical"],
"wizard_two" : ["You face #place_adj.a# #place# as images of #random_object.s# #flicker# #through# your #mind#. What was that? Though #old# and #wizard_pos#, your memory extends only a few #week_months# back. Now you #wander##around_opt#, trying to #remember# what happened.\n\nYou #action#"],
"great" : ["great", "renowned", "world-famous", "legendary"],
"stare" : ["look", "stare", "gaze"],
"sneak" : ["sneak", "creep", "inch", "move", "go"],
"go_near" : ["#stare# at", "#sneak# towards", "approach"],
"private" : ["private", "restricted", "forbidden", "confidential", "secure", "secret", "personal"],
"chamber" : ["chamber", "study", "office", "antechamber", "room", "basement", "attic"],
"great_wizard" : ["#great# #magical_role# #fantasy_name#", "#fantasy_name# the #color.capitalize#"],
"order" : ["order", "command", "remind", "direct", "demand"],
"wizard_three" : "You are apprentice to the #great_wizard#. Before leaving on an errand, they #order.ed# you to never enter their #private# #chamber#... but your curiosity has gotten the best of you.\n\nAs you #go_near# the door, you #action#",
"wizard_prompt" : ["#wizard_one#", "#wizard_two#", "#wizard_three#"],
"wizard_context" : "You are <NAME>, a wizard #from_fantasy#. You have #wizard_item.a# and #wizard_item.a#.",
"peasant_item" : ["#metal_adj_opt#pitchfork", "shirt on your back", "#metal_adj_opt#shovel", "dirty rag", "#metal_adj_opt#spade", "#metal_adj_opt#sickle", "shears", "basket", "plough", "#metal_adj_opt#scythe", "dirty hat"],
"farm_animal" : ["pig", "cow", "chicken", "llama", "goat", "horse", "donkey", "rabbit", "hog", "mule", "sow"],
"farm_animal_sing" : ["sheep", "livestock", "cattle"],
"farm_animal_type" : ["#farm_animal.s#", "#farm_animal_sing#"],
"woolen" : ["sheep", "goats", "llamas"],
"milky" : ["cows", "goats"],
"working" : ["working", "toiling", "laboring", "sweating"],
"morning_time" : ["at the break of dawn", "at daybreak", "early", "early in the morning", "at the cock's crow"],
"field_verb" : ["plowing", "tilling", "cultivating", "caring for"],
"peasant_work" : ["feeding the #farm_animal_type#", "#field_verb# the fields", "harvesting the crops", "planting new crops", "#working# in the fields", "shearing the #woolen#", "milking the #milky#", "shoveling dung"],
"begin" : ["begin", "go about", "start", "get to", "get about"],
"second_thing" : ["For a while, you have wanted to run away.", "In the distance, your master is yelling at some other workers.", "The sun beats down upon you.", "This has been your daily routine since you were young.", "You mutter a prayer to the #deity#."],
"peasant_prompt" : "You wake up #morning_time# and #begin# #peasant_work#. #second_thing# You #action#",
"peasant_context" : "You are <NAME>, a peasant #from_fantasy#. You have #peasant_item.a# and #peasant_item.a#.",
"rogue_item" : ["pair of gloves", "cloak", "deck of cards", "dagger", "lockpick", "mask", "shawl", "pipe", "key", "length of rope", "bundle of darts", "sickle", "mace"],
"walk" : ["walk","go"],
"street" : ["street", "streets", "square", "road"],
"city" : ["city", "town", "village", "market"],
"steal" : ["steal from", "shoplift", "burgle", "break into"],
"rob" : ["steal from", "rob", "jump", "pickpocket", "con"],
"target" : ["someplace to #steal#", "someone to #rob#", "some #profession# to #rob#"],
"along" : ["along", "down"],
"townsfolk" : ["townsfolk", "local vendors", "people", "crowd", "populace"],
"amongst" : ["amongst", "in the midst of", "among"],
"rogue_location" : ["#along# the #city# #street#", "#amongst# the #townsfolk#"],
"seeking" : ["seeking", "looking for", "searching for", "in search of"],
"sense" : ["see", "hear", "sense", "remember", "feel", "notice"],
"rogue_one" : "You #walk# #rogue_location# #seeking# #target#. You #action#",
"position" : ["at the doorstep", "along the way", "suddenly", "before long", "out of the blue", "once in your room"],
"time" : ["morning", "afternoon", "evening"],
"day" : ["long day", "long #time#", "few hours", "routine #time#"],
"stealing" : ["stealing", "mischief", "pickpocketing"],
"inn_adj" : ["your favorite", "a nearby", "a crowded", "a busy", "a cheap"],
"rogue_two" : "After a #day# of #stealing#, you head towards #inn_adj# inn to stay for the night. #position.capitalize# you #action#",
"isnt" : ["isn't", "is not"],
"enough" : ["enough", "sufficient", "good enough"],
"not_enough" : ["#isnt# #enough#", "#isnt# #enough# anymore"],
"simple" : ["simple", "basic", "general", "standard"],
"rogue_three" : "This #time# you decided that #simple# #stealing# #not_enough#. You long for a more ambitious goal: taking for yourself #whole_artifact#.\n\nYou #action#",
"rogue_prompt" : ["#rogue_one#","#rogue_two#", "#rogue_three#"],
"rogue_context" : "You are <NAME>, a rogue #from_fantasy#. You have #rogue_item.a# and #rogue_item.a#."
}

108
story/story_data.yaml Normal file
View file

@ -0,0 +1,108 @@
settings:
fantasy:
description: "living in the kingdom of Larion. "
characters:
noble:
prompts: ["You are awakened by one of your servants who tells you that your keep is under attack. You look out the window and see"]
item1: "pouch of gold"
item2: "small dagger"
knight:
prompts: ["You are on a quest to defeat the evil dragon of Larion. You've heard he lives up at the north of the kingdom. You set on the path to defeat him and walk into a dark forest. As you enter the forest you see"]
item1: "steel longsword"
item2: "wooden shield"
wizard:
prompts: ["You finish your long journey and finally arrive at the ruin you've been looking for. You look around and see"]
item1: "staff"
item2: "spellbook"
peasant:
prompts: ["You wake up and begin working in the fields. You see"]
item1: "pitchfork"
item2: "nothing else"
rogue:
prompts: ["You walk down the city street looking for somewhere to steal from. You look around and see"]
item1: "long steel dagger"
item2: "length of rope"
apocalyptic:
description: " trying to survive in a post apocalyptic world by scavenging among the ruins of what is left. "
characters:
scavenger:
prompts: ["You walk for two hours and take a break. You've left your town in search of food. You look around and see "]
item1: "rusty knife"
item2: "canteen"
mutant:
prompts: ["In the colony you were born in, your strange condition was considered a curse, and you has been banished since you were sixteen. After a long journey, you find an abandoned bunker. You see"]
item1: "scales on your face"
item2: "third leg"
headhunter:
prompts: ["You are driving your rusty motorbike. You go past many abandoned bunkers. You arrive at a colony and stop the engine. You take a look around and see"]
item1: "binoculars"
item2: "crappy shotgun"
mystery:
description: "living in Chicago. "
characters:
patient:
prompts: ["You wake up in an old rundown hospital with no memory of how you got there. You take a look around the room and see"]
item1: "hospital bracelet"
item2: "pack of bandages"
detective:
prompts: ["You enter the forest where you believe the criminal you're searching for fled to. Suddenly"]
item1: "pistol"
item2: "police badge"
spy:
prompts: ["You listen to the Russian diplomats and hear them discussing"]
item1: "concealed pistol"
item2: "syringe of poison"
zombies:
description: " trying to survive in a world filled with infected zombies everywhere. "
characters:
soldier:
prompts: ["Your unit lost a lot of men when the infection broke, but you've managed to keep the small town
you're stationed near safe for now. You look over the town and think"]
item1: "automatic rifle"
item2: "grenade"
survivor:
prompts: ["You have managed to survive several months avoiding zombies and scavenging food.
You cautiously enter a rundown store and hear"]
item1: "pistol"
item2: "backpack"
scientist:
prompts: ["You pound your fist on the table, angry that you still haven't found the cure to the infection. You turn to your assistant and"]
item1: "backpack"
item2: "solar powered tablet"

339
story/story_manager.py Normal file
View file

@ -0,0 +1,339 @@
import json
import os
import subprocess
import uuid
from subprocess import Popen
from story.utils import *
class Story:
def __init__(
self, story_start, context="", seed=None, game_state=None, upload_story=False
):
self.story_start = story_start
self.context = context
self.rating = -1
self.upload_story = upload_story
# list of actions. First action is the prompt length should always equal that of story blocks
self.actions = []
# list of story blocks first story block follows prompt and is intro story
self.results = []
# Only needed in constrained/cached version
self.seed = seed
self.choices = []
self.possible_action_results = None
self.uuid = None
if game_state is None:
game_state = dict()
self.game_state = game_state
self.memory = 20
def __del__(self):
if self.upload_story:
self.save_to_storage()
console_print("Game saved.")
console_print(
"To load the game, type 'load' and enter the following ID: " + self.uuid
)
def init_from_dict(self, story_dict):
self.story_start = story_dict["story_start"]
self.seed = story_dict["seed"]
self.actions = story_dict["actions"]
self.results = story_dict["results"]
self.choices = story_dict["choices"]
self.possible_action_results = story_dict["possible_action_results"]
self.game_state = story_dict["game_state"]
self.context = story_dict["context"]
self.uuid = story_dict["uuid"]
if "rating" in story_dict.keys():
self.rating = story_dict["rating"]
else:
self.rating = -1
def initialize_from_json(self, json_string):
story_dict = json.loads(json_string)
self.init_from_dict(story_dict)
def add_to_story(self, action, story_block):
self.actions.append(action)
self.results.append(story_block)
def latest_result(self):
mem_ind = self.memory
if len(self.results) < 2:
latest_result = self.story_start
else:
latest_result = self.context
while mem_ind > 0:
if len(self.results) >= mem_ind:
latest_result += self.actions[-mem_ind] + self.results[-mem_ind]
mem_ind -= 1
return latest_result
def __str__(self):
story_list = [self.story_start]
for i in range(len(self.results)):
story_list.append("\n" + self.actions[i] + "\n")
story_list.append("\n" + self.results[i])
return "".join(story_list)
def to_json(self):
story_dict = {}
story_dict["story_start"] = self.story_start
story_dict["seed"] = self.seed
story_dict["actions"] = self.actions
story_dict["results"] = self.results
story_dict["choices"] = self.choices
story_dict["possible_action_results"] = self.possible_action_results
story_dict["game_state"] = self.game_state
story_dict["context"] = self.context
story_dict["uuid"] = self.uuid
story_dict["rating"] = self.rating
return json.dumps(story_dict)
def save_to_storage(self):
print("Saving to storage has been disabled due to abuse of the cloud bucket. Save will now be stored locally.")
self.uuid = str(uuid.uuid1())
save_path = "./saved_stories/"
if not os.path.exists(save_path):
os.makedirs(save_path)
story_json = self.to_json()
file_name = "story" + str(self.uuid) + ".json"
f = open(os.path.join(save_path, file_name), "w")
f.write(story_json)
f.close()
return self.uuid
def load_from_storage(self, story_id):
save_path = "./saved_stories/"
if not os.path.exists(save_path):
return "Error save not found."
file_name = "story" + story_id + ".json"
exists = os.path.isfile(os.path.join(save_path, file_name))
if exists:
with open(os.path.join(save_path, file_name), "r") as fp:
game = json.load(fp)
self.init_from_dict(game)
return str(self)
else:
print("Save not found locally. Trying in the cloud bucket (only valid for saves before Dec 24 2019)")
cmd = "gsutil cp gs://aidungeonstories/" + file_name + " " + save_path
os.system(cmd)
exists = os.path.isfile(os.path.join(save_path, file_name))
if exists:
with open(os.path.join(save_path, file_name), "r") as fp:
game = json.load(fp)
self.init_from_dict(game)
return str(self)
else:
return "Error save not found locally or in the cloud."
def get_rating(self):
while True:
try:
rating = input("Please rate the story quality from 1-10: ")
rating_float = max(min(float(rating), 10), 1)
except ValueError:
print("Please return a valid number.")
else:
self.rating = rating_float
return
class StoryManager:
def __init__(self, generator):
self.generator = generator
self.story = None
def start_new_story(
self, story_prompt, context="", game_state=None, upload_story=False
):
block = self.generator.generate(context + story_prompt)
block = cut_trailing_sentence(block)
self.story = Story(
context + story_prompt + block,
context=context,
game_state=game_state,
upload_story=upload_story,
)
return str(self.story)
def load_new_story(self, story_id, upload_story=False):
save_path = "./saved_stories/"
file_name = "story" + story_id + ".json"
exists = os.path.isfile(os.path.join(save_path, file_name))
if not exists:
print("Save not found locally. Trying in the cloud bucket (only valid for saves before Dec 24 2019)")
cmd = "gsutil cp gs://aidungeonstories/" + file_name + " " + save_path
os.system(cmd)
exists = os.path.isfile(os.path.join(save_path, file_name))
if not exists:
return "Error save not found locally or on the cloud."
with open(os.path.join(save_path, file_name), "r") as fp:
game = json.load(fp)
self.story = Story("", upload_story=upload_story)
self.story.init_from_dict(game)
return str(self.story)
def load_story(self, story, from_json=False):
if from_json:
self.story = Story("")
self.story.initialize_from_json(story)
else:
self.story = story
return str(story)
def json_story(self):
return self.story.to_json()
def story_context(self):
return self.story.latest_result()
class UnconstrainedStoryManager(StoryManager):
def act(self, action_choice):
result = self.generate_result(action_choice)
self.story.add_to_story(action_choice, result)
return result
def generate_result(self, action):
block = self.generator.generate(self.story_context() + action)
return block
class ConstrainedStoryManager(StoryManager):
def __init__(self, generator, action_verbs_key="classic"):
super().__init__(generator)
self.action_phrases = get_action_verbs(action_verbs_key)
self.cache = False
self.cacher = None
self.seed = None
def enable_caching(
self, credentials_file=None, seed=0, bucket_name="dungeon-cache"
):
self.cache = True
self.cacher = Cacher(credentials_file, bucket_name)
self.seed = seed
def start_new_story(self, story_prompt, context="", game_state=None):
if self.cache:
return self.start_new_story_cache(story_prompt, game_state=game_state)
else:
return super().start_new_story(
story_prompt, context=context, game_state=game_state
)
def start_new_story_generate(self, story_prompt, game_state=None):
super().start_new_story(story_prompt, game_state=game_state)
self.story.possible_action_results = self.get_action_results()
return self.story.story_start
def start_new_story_cache(self, story_prompt, game_state=None):
response = self.cacher.retrieve_from_cache(self.seed, [], "story")
if response is not None:
story_start = story_prompt + response
self.story = Story(story_start, seed=self.seed)
self.story.possible_action_results = self.get_action_results()
else:
story_start = self.start_new_story_generate(
story_prompt, game_state=game_state
)
self.story.seed = self.seed
self.cacher.cache_file(self.seed, [], story_start, "story")
return story_start
def load_story(self, story, from_json=False):
story_string = super().load_story(story, from_json=from_json)
return story_string
def get_possible_actions(self):
if self.story.possible_action_results is None:
self.story.possible_action_results = self.get_action_results()
return [
action_result[0] for action_result in self.story.possible_action_results
]
def act(self, action_choice_str):
try:
action_choice = int(action_choice_str)
except:
print("Error invalid choice.")
return None, None
if action_choice < 0 or action_choice >= len(self.action_phrases):
print("Error invalid choice.")
return None, None
self.story.choices.append(action_choice)
action, result = self.story.possible_action_results[action_choice]
self.story.add_to_story(action, result)
self.story.possible_action_results = self.get_action_results()
return result, self.get_possible_actions()
def get_action_results(self):
if self.cache:
return self.get_action_results_cache()
else:
return self.get_action_results_generate()
def get_action_results_generate(self):
action_results = [
self.generate_action_result(self.story_context(), phrase)
for phrase in self.action_phrases
]
return action_results
def get_action_results_cache(self):
response = self.cacher.retrieve_from_cache(
self.story.seed, self.story.choices, "choices"
)
if response is not None:
print("Retrieved from cache")
return json.loads(response)
else:
print("Didn't receive from cache")
action_results = self.get_action_results_generate()
response = json.dumps(action_results)
self.cacher.cache_file(
self.story.seed, self.story.choices, response, "choices"
)
return action_results
def generate_action_result(self, prompt, phrase, options=None):
action_result = (
phrase + " " + self.generator.generate(prompt + " " + phrase, options)
)
action, result = split_first_sentence(action_result)
return action, result

292
story/utils.py Normal file
View file

@ -0,0 +1,292 @@
# coding: utf-8
import re
from difflib import SequenceMatcher
import yaml
from profanityfilter import ProfanityFilter
YAML_FILE = "story/story_data.yaml"
with open("story/censored_words.txt", "r") as f:
censored_words = [l.replace("\n", "") for l in f.readlines()]
pf = ProfanityFilter(custom_censor_list=censored_words)
def console_print(text, width=75):
last_newline = 0
i = 0
while i < len(text):
if text[i] == "\n":
last_newline = 0
elif last_newline > width and text[i] == " ":
text = text[:i] + "\n" + text[i:]
last_newline = 0
else:
last_newline += 1
i += 1
print(text)
def get_similarity(a, b):
return SequenceMatcher(None, a, b).ratio()
def get_num_options(num):
while True:
choice = input("Enter the number of your choice: ")
try:
result = int(choice)
if result >= 0 and result < num:
return result
else:
print("Error invalid choice. ")
except ValueError:
print("Error invalid choice. ")
def player_died(text):
"""
TODO: Add in more sophisticated NLP, maybe a custom classifier
trained on hand-labelled data that classifies second-person
statements as resulting in death or not.
"""
lower_text = text.lower()
you_dead_regexps = [
"you('re| are) (dead|killed|slain|no more|nonexistent)",
"you (die|pass away|perish|suffocate|drown|bleed out)",
"you('ve| have) (died|perished|suffocated|drowned|been (killed|slain))",
"you (\w* )?(yourself )?to death",
"you (\w* )*(collapse|bleed out|chok(e|ed|ing)|drown|dissolve) (\w* )*and (die(|d)|pass away|cease to exist|(\w* )+killed)",
]
return any(re.search(regexp, lower_text) for regexp in you_dead_regexps)
def player_won(text):
lower_text = text.lower()
won_phrases = [
"you ((\w* )*and |)live happily ever after",
"you ((\w* )*and |)live (forever|eternally|for eternity)",
"you ((\w* )*and |)(are|become|turn into) ((a|now) )?(deity|god|immortal)",
"you ((\w* )*and |)((go|get) (in)?to|arrive (at|in)) (heaven|paradise)",
"you ((\w* )*and |)celebrate your (victory|triumph)",
"you ((\w* )*and |)retire",
"The rest is history...",
]
return any(re.search(regexp, lower_text) for regexp in won_phrases)
def remove_profanity(text):
return pf.censor(text)
def cut_trailing_quotes(text):
num_quotes = text.count('"')
if num_quotes % 2 is 0:
return text
else:
final_ind = text.rfind('"')
return text[:final_ind]
def split_first_sentence(text):
first_period = text.find(".")
first_exclamation = text.find("!")
if first_exclamation < first_period and first_exclamation > 0:
split_point = first_exclamation + 1
elif first_period > 0:
split_point = first_period + 1
else:
split_point = text[0:20]
return text[0:split_point], text[split_point:]
def cut_trailing_action(text):
lines = text.split("\n")
last_line = lines[-1]
if (
"you ask" in last_line
or "You ask" in last_line
or "you say" in last_line
or "You say" in last_line
) and len(lines) > 1:
text = "\n".join(lines[0:-1])
return text
def cut_trailing_sentence(text):
text = standardize_punctuation(text)
last_punc = max(text.rfind("."), text.rfind("!"), text.rfind("?"))
if last_punc <= 0:
last_punc = len(text) - 1
et_token = text.find("<")
if et_token > 0:
last_punc = min(last_punc, et_token - 1)
act_token = text.find(">")
if act_token > 0:
last_punc = min(last_punc, act_token - 1)
text = text[:last_punc+1]
text = cut_trailing_quotes(text)
text = cut_trailing_action(text)
return text
def replace_outside_quotes(text, current_word, repl_word):
text = standardize_punctuation(text)
reg_expr = re.compile(current_word + '(?=([^"]*"[^"]*")*[^"]*$)')
output = reg_expr.sub(repl_word, text)
return output
def is_first_person(text):
count = 0
for pair in first_to_second_mappings:
variations = mapping_variation_pairs(pair)
for variation in variations:
reg_expr = re.compile(variation[0] + '(?=([^"]*"[^"]*")*[^"]*$)')
matches = re.findall(reg_expr, text)
count += len(matches)
if count > 3:
return True
else:
return False
def is_second_person(text):
count = 0
for pair in second_to_first_mappings:
variations = mapping_variation_pairs(pair)
for variation in variations:
reg_expr = re.compile(variation[0] + '(?=([^"]*"[^"]*")*[^"]*$)')
matches = re.findall(reg_expr, text)
count += len(matches)
if count > 3:
return True
else:
return False
def capitalize(word):
return word[0].upper() + word[1:]
def mapping_variation_pairs(mapping):
mapping_list = []
mapping_list.append((" " + mapping[0] + " ", " " + mapping[1] + " "))
mapping_list.append(
(" " + capitalize(mapping[0]) + " ", " " + capitalize(mapping[1]) + " ")
)
# Change you it's before a punctuation
if mapping[0] is "you":
mapping = ("you", "me")
mapping_list.append((" " + mapping[0] + ",", " " + mapping[1] + ","))
mapping_list.append((" " + mapping[0] + "\?", " " + mapping[1] + "\?"))
mapping_list.append((" " + mapping[0] + "\!", " " + mapping[1] + "\!"))
mapping_list.append((" " + mapping[0] + "\.", " " + mapping[1] + "."))
return mapping_list
first_to_second_mappings = [
("I'm", "you're"),
("Im", "you're"),
("Ive", "you've"),
("I am", "you are"),
("was I", "were you"),
("am I", "are you"),
("wasn't I", "weren't you"),
("I", "you"),
("I'd", "you'd"),
("i", "you"),
("I've", "you've"),
("was I", "were you"),
("am I", "are you"),
("wasn't I", "weren't you"),
("I", "you"),
("I'd", "you'd"),
("i", "you"),
("I've", "you've"),
("I was", "you were"),
("my", "your"),
("we", "you"),
("we're", "you're"),
("mine", "yours"),
("me", "you"),
("us", "you"),
("our", "your"),
("I'll", "you'll"),
("myself", "yourself"),
]
second_to_first_mappings = [
("you're", "I'm"),
("your", "my"),
("you are", "I am"),
("you were", "I was"),
("are you", "am I"),
("you", "I"),
("you", "me"),
("you'll", "I'll"),
("yourself", "myself"),
("you've", "I've"),
]
def capitalize_helper(string):
string_list = list(string)
string_list[0] = string_list[0].upper()
return "".join(string_list)
def capitalize_first_letters(text):
first_letters_regex = re.compile(r"((?<=[\.\?!]\s)(\w+)|(^\w+))")
def cap(match):
return capitalize_helper(match.group())
result = first_letters_regex.sub(cap, text)
return result
def standardize_punctuation(text):
text = text.replace("", "'")
text = text.replace("`", "'")
text = text.replace("", '"')
text = text.replace("", '"')
return text
def first_to_second_person(text):
text = " " + text
text = standardize_punctuation(text)
for pair in first_to_second_mappings:
variations = mapping_variation_pairs(pair)
for variation in variations:
text = replace_outside_quotes(text, variation[0], variation[1])
return capitalize_first_letters(text[1:])
def second_to_first_person(text):
text = " " + text
text = standardize_punctuation(text)
for pair in second_to_first_mappings:
variations = mapping_variation_pairs(pair)
for variation in variations:
text = replace_outside_quotes(text, variation[0], variation[1])
return capitalize_first_letters(text[1:])