Last active
May 15, 2020 18:10
-
-
Save darthschmoo/00b56bdee43347e794091a91732e1f18 to your computer and use it in GitHub Desktop.
Ruby Random Epic Fantasy Title Generator, Take II
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# TODOS: move lists into configuration file, plurals, better finding/replacing of variables | |
class LineParser | |
def without_comments( line ) | |
line.gsub( /#.*/, "" ) | |
end | |
end | |
class ListParser < LineParser | |
def self.parse( text ) | |
self.new.parse( text ) | |
end | |
def parse( text ) | |
return text if text.is_a?( Hash ) | |
{}.tap do |lists| | |
current_list = nil | |
for line in text.split("\n") | |
case without_comments( line ) | |
when /^\s+$/ # empty line | |
# do nothing | |
when /^\S/ # no space, move on to next key | |
current_list = line.strip | |
lists[current_list] ||= [] | |
when /^\s+\S/ | |
lists[current_list] ||= [] | |
lists[current_list] << line.strip | |
end | |
end | |
end | |
end | |
end | |
class TemplateParser < LineParser | |
def self.parse( text ) | |
self.new.parse( text ) | |
end | |
# each line represents one template | |
def parse( text ) | |
[].tap do |templates| | |
for line in text.split( "\n" ) | |
case without_comments( line ) | |
when /^\s*$/ | |
# empty line, do nothing | |
else | |
templates << line | |
end | |
end | |
end | |
end | |
end | |
class TitleGenerator | |
VERSION = "0.0.2" | |
def initialize( opts = { templates: [], lists: {} } ) | |
@templates = parse_templates( opts[:templates] ) | |
@lists = parse_lists( opts[:lists] ) | |
# @pluralizer = Pluralizer.new | |
end | |
def parse_templates( t ) | |
TemplateParser.parse( t ) | |
end | |
def parse_lists( l ) | |
ListParser.parse( l ) | |
end | |
def print_titles( n = 1 ) | |
n.times do | |
puts self.title | |
end | |
end | |
def title | |
templ = pick( templates ) | |
titl = post_fix( fill_in( templ ) ) | |
if debugging? | |
puts "TITLE: #{ titl.inspect }" | |
puts "\n\n---------------------------------------" | |
end | |
titl | |
end | |
def variable_substitute( s ) | |
tags = s.scan( /\<[A-Z0-9_|]+\>/ ) | |
for tag in tags | |
list = lookup( tag ) | |
choice = pick( list ) || "" # returns nil when the given list is empty | |
puts " #{s.inspect} ---> #{choice.inspect}" if debugging? | |
s = s.gsub( tag, choice ) | |
end | |
s | |
end | |
def fill_in( template ) | |
puts " TEMPLATE: #{template}" if debugging? | |
rval = template.split.map{ |s| | |
variable_substitute( s ) | |
}.join( " " ) | |
if rval.include?( "<" ) # Still have variables to fill, so send it through another round of replacements | |
fill_in( rval ) | |
else | |
rval | |
end | |
end | |
def post_fix( t ) | |
# fixing these the stupid way | |
t = t.gsub( " ,", "," ) | |
t = t.gsub( " 's", "'s" ) | |
a_to_an( t ) | |
end | |
def a_to_an( t ) | |
original_chunks = t.split( /\s+/ ) | |
final_chunks = [] | |
(original_chunks.length - 1).times do |i| | |
chunk = original_chunks[i] | |
if chunk.downcase == "a" | |
next_chunk = original_chunks[i + 1] | |
first_letter = next_chunk[0].downcase | |
vowel = %w(a e i o u).include?( first_letter ) | |
final_chunks << (vowel ? chunk + "n" : chunk ) | |
else | |
final_chunks << chunk | |
end | |
end | |
final_chunks << original_chunks.last | |
rval = final_chunks.join( " " ) | |
rval | |
end | |
# Look up the items in the list(s) <THING|OTHER_THING|THIRD_THING> | |
def lookup( s ) | |
key = s[1..-2] | |
keys_to_list( key.split( "|" )) | |
end | |
def keys_to_list( keys ) | |
keys.map{|k| | |
if lists.has_key?( k ) | |
lists[k] | |
else | |
warn( "Missing list: #{k.inspect}" ) | |
[] | |
end | |
}.flatten | |
end | |
def templates | |
@templates | |
end | |
# Randomly pick a single element out of an array | |
def pick( list ) | |
list[ rand( list.length) ] | |
end | |
def lists | |
@lists | |
end | |
def debugging? | |
false | |
end | |
end | |
templates = "<EPIC_FANTASY_TITLE>" | |
lists = " | |
EPIC_FANTASY_TITLE | |
<A|THE|NULL> <S0> of <S1> and <S1> | |
<A|THE|NULL> <ADJ0|NULL> <S0> of <ADJ0> <S1> | |
<S0> of the <ADJ0> <S1> | |
<ADJ0> <S1> and <ADJ0> <S1> | |
<A|THE> <PLACE|PERSON|SEASON> of <ADJ0> <ANIMALS> | |
<A|THE> <ADJ0> <PERSON> | |
<OBJECT_PHRASE> , <OBJECT_PHRASE> | |
<A|THE|NULL> <S0> of <S1> <SERIES> | |
<S0> of the <PLACE> | |
<PLACE|PERSON> of <ELEMENT|EMOTION|JEWEL> | |
<A|THE> <ADJ_ATTRIBUTE|ADJ_MISC> <S0> | |
The <PERSON|ITEM> in the <ENVIRONMENT|PLACE> | |
A <TIME> of <S1> | |
The <TIME> of the <ADJ0|NULL> <S0> <SERIES_PHRASE> | |
The <ADJ0> <S0> Trilogy | |
<ENVIRONMENT|ELEMENT|RACE><BRINGER> <SERIES_PHRASE> | |
The <EVENT> of <PLACE_PHRASE> | |
The <LORE> of the <COUNT2> <PLACE|RANK|MAGICIAN> | |
The <LORE> of the <COUNT> <PLACE|RANK|MAGICIAN>s | |
<S0> <SERIES_PHRASE> | |
TIME | |
Time | |
Day | |
Year | |
Hour | |
Century | |
Age | |
LORE | |
Tale | |
Saga | |
Legend | |
Scroll | |
NULL | |
# Empty | |
A | |
A | |
THE | |
The | |
GENERIC_PERSON | |
Man | |
Woman | |
Child | |
RELATION | |
Daughter | |
Son | |
Mother | |
Father | |
RANK | |
King | |
Queen | |
Prince | |
Duke | |
Knight | |
Earl | |
Apprentice | |
Master | |
OCCUPATION | |
Blacksmith | |
Courtesan | |
General | |
Innkeper | |
Lorekeeper | |
Pirate | |
Prophet | |
Soldier | |
Traveler | |
Thief | |
Warlord | |
RACE | |
Centaur | |
Demon | |
Dragon | |
Dwarf | |
Elf | |
God | |
Goddess | |
Gorgon | |
Ghost | |
Gryphon | |
Orc | |
Mermaid | |
Titan | |
Werewolf | |
Vampire | |
MAGICIAN | |
Adept | |
Lich | |
Necromancer | |
Oracle | |
Seer | |
Sorcerer | |
Sorceress | |
Witch | |
Wizard | |
Warlock | |
PERSON | |
<GENERIC_PERSON|RELATION|RANK|OCCUPATION|RACE|MAGICIAN> | |
ENVIRONMENT | |
Desert | |
Field | |
Forest | |
Mountain | |
Swamp | |
Jungle | |
River | |
Valley | |
PLACE | |
Abbey | |
Citadel | |
City | |
Court | |
Empire | |
Forge | |
Gate | |
Grave | |
Graveyard | |
# Home (not really working) | |
Isle | |
Keep | |
Kingdom | |
Land | |
Monastery | |
Palace | |
Path | |
Realm | |
Refuge | |
Ruins | |
Tavern | |
Tomb | |
Tower | |
Void | |
Wasteland | |
ITEM | |
Blade | |
Book | |
Crown | |
Curse | |
Dagger | |
Fire | |
Fires | |
Flames | |
Mirror | |
Relic | |
Robe | |
Scroll | |
Scepter | |
Star | |
Sword | |
Throne | |
Wish | |
EVENT | |
Peace | |
War | |
Battle | |
Council | |
Death | |
Siege | |
S0 | |
<PERSON|PLACE|ENVIRONMENT|ITEM|ANIMAL|EVENT|TIME|LORE> | |
ANIMAL | |
Basilisk | |
Bear | |
Cormorant | |
Crow | |
Dragon | |
Fox | |
Hawk | |
Horse | |
Lion | |
Raven | |
Wolf | |
ANIMALS | |
Basilisks | |
Bears | |
Cormorants | |
Crows | |
Dragons | |
Foxes | |
Hawks | |
Horses | |
Lions | |
Ravens | |
Wolves | |
ELEMENT | |
Earth | |
Air | |
Fire | |
Flame | |
Water | |
Wind | |
Sky | |
Iron | |
Steel | |
Stone | |
Lightning | |
Storm | |
Ice | |
Cold | |
Blood | |
Smoke | |
Bone | |
Glass | |
Fog | |
Ash | |
Embers | |
Night | |
Light | |
Rain | |
EMOTION | |
Joy | |
Sadness | |
Dream | |
Nightmares | |
Rage | |
Sleep | |
Terror | |
Evil | |
Revenge | |
SEASON | |
Summer | |
Winter | |
DIRECTION | |
North | |
South | |
East | |
West | |
UNKNOWN | |
Culling | |
Fate | |
Darkness | |
Twilight | |
S1 | |
<ANIMALS|ELEMENT|EMOTION|SEASON|DIRECTION> | |
PERSON_PHRASE | |
<PERSON> | |
<EMOTION|ELEMENT|ADJ0> <PERSON> | |
JEWEL | |
Amethyst | |
Diamond | |
Emerald | |
Obsidian | |
Opal | |
Ruby | |
ADJ_CHANGE | |
Falling | |
Rising | |
Brightening | |
Darkening | |
Waning | |
Ascending | |
Gathering | |
ADJ_ATTRIBUTE | |
Ancient | |
Barren | |
Beguiling | |
Blighted | |
Holy | |
Unholy | |
Corrupted | |
Consecrated | |
Sacred | |
Desolated | |
Living | |
Ethereal | |
Accursed | |
Cursed | |
Blessed | |
Royal | |
ADJ_MISC | |
Avenging | |
Burning | |
Coming | |
Endless | |
Unending | |
Encircling | |
Forgotten | |
Magic | |
Magical | |
Lost | |
Lunar | |
Towering | |
Windswept | |
ADJ_ELEMENT | |
Fiery | |
Watery | |
Earthen | |
Stony | |
Ashen | |
Stormy | |
Icy | |
Frozen | |
Bloody | |
Empty | |
Chaos | |
Smoky | |
ADJ_COLOR | |
Black | |
Cerulean | |
Crimson | |
Dark | |
Golden | |
Red | |
Sable | |
Silver | |
White | |
ADJ0 | |
<ADJ_CHANGE|ADJ_ATTRIBUTE|ADJ_MISC|JEWEL|ADJ_ELEMENT|ADJ_COLOR|ELEMENT> | |
OBJECT_PHRASE | |
<PERSON_PHRASE> | |
<ADJ0> <S0> | |
<ADJ_COLOR> <PERSON> | |
<PERSON> 's <ADJ0> <PERSON> | |
BRINGER | |
born | |
bringer | |
cursed | |
marked | |
blade | |
seeker | |
slayer | |
sworn | |
thorn | |
wing | |
COUNT | |
Two | |
Three | |
Four | |
Five | |
Six | |
Seven | |
Eight | |
Nine | |
COUNT2 | |
First | |
Second | |
Third | |
Fourth | |
Fifth | |
COUNT3 | |
One | |
Two | |
Three | |
PLACE_PHRASE | |
<ADJ_ATTRIBUTE|ADJ_MISC|JEWEL|ADJ_ELEMENT|ADJ_COLOR|ELEMENT> <PLACE> | |
the <DIRECTION>ern <PLACE> | |
<ANIMAL|OCCUPATION|RACE>'s <PLACE> | |
SERIES | |
Trilogy | |
Cycle | |
Series | |
Epic | |
SERIES_NAME | |
<PERSON_PHRASE> | |
<PLACE_PHRASE> | |
<S0> of <ADJ0> | |
SERIES_PHRASE | |
(Book <COUNT3> of the <SERIES_NAME> Trilogy) | |
(a Novel of the <SERIES_NAME> <SERIES>) | |
(next in the <SERIES_NAME> <SERIES>) | |
<NULL> | |
<NULL> | |
<NULL> | |
" | |
TitleGenerator.new( templates: templates, lists: lists ).print_titles( (ARGV[0] || 10).to_i ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment