-- Strain name generator - Northern Cosmos 2020
--
-- The dictionary holds the words that will be used.
--
-- Fill in the strain dictionary as you please, add/change/remove words and/or groups.
-- The rule at the start of each group can be used to steer the name composition towards "some style"...
--
-- Each strain name picks words from different groups, thus a word will never appear twice in a name,
-- unless it's repeated in more than one group and the rules allow it. There's no support for probabilities
-- or anything fancy, but some basics can be realized by repeating words within a group.
-- Note that it's possible to construct the dictionary with its rules so that it's impossible,
-- or takes too long, to make the requested name(s) - a timeout occurs if this happens.
--
-- The rules are:
-- rules.ANY => word can be anywhere in the name
-- rules.FIRST => word must be first
-- ...
-- rules.FIFTH => word must be fifth (no symbols provided for later positions, use integers for that!)
-- positive integer N => word must be Nth
-- rules.LAST => word must be last
-- rules.MIDDLE => word must be in the middle (not first or last)
-- rules.NOT_FIRST => word must not be first
-- rules.NOT_LAST => word must not be ast
local rules =
{ ANY = 0,
FIRST = 1,
SECOND = 2,
THIRD = 3,
FOURTH = 4,
FIFTH = 5,
LAST = -1,
MIDDLE = -2,
NOT_FIRST = -3,
NOT_LAST = -4
}
local strain_dictionary =
{
-- origins
{ rules.FIRST, "Jamaica", "Jamaican", "Acapulco", "Cali", "Afghan", "Nepal", "Nepalese", "Thai", "Space", "Moon",
"African", "Colombian", "Mexican", "Hawaiian", "Panama", "Cambodia", "Panama"
},
-- directions
{ rules.FIRST, "Northern", "Southern", "Western", "Eastern" },
-- occasions
{ rules.FIRST,
"Birthday", "Wedding", "Burial", "Christmas", "Easter", "Weekend", "Sunday" },
-- misc prefixes
{ rules.FIRST, "OG", "AK" },
-- dope terms
{ rules.ANY, "LSD", "Crack", "Meth" },
-- misc
{ rules.LAST, "Shot", "Kick", "Smash", "Hit" },
-- first names
{ rules.FIRST, "Jack", "Bruce", "Willy", "Carl", "Bob" },
-- last names
{ rules.FIRST, "Herer", "Banner", "Nelson", "Sagan", "Marley" },
-- last names
{ rules.SECOND, "Herer", "Banner", "Nelson", "Sagan", "Marley" },
-- places
{ rules.ANY, "Pyramid", "Cave", "Dungeon", "Mountain", "Desert", "Savannah", "Cosmos", "Cosmic", "Space" },
{ rules.ANY, "Fruit", "Pineapple", "Banana", "Apple", "Pear", "Orange", "Blackberry", "Blueberry",
"Lingonberry", "Lemon", "Lime"
},
-- fruits/berrys/nuts
{ rules.LAST, "Fruit", "Berry","Strawberry", "Blackberry", "Blueberry", "Peanut", "Lingonberry", "Cherry",
"Pineapple", "Banana", "Apple", "Pear", "Tangerine", "Orange", "Lemon", "Lime"
},
-- other general edibles
{ rules.LAST, "Cookies", "Cake", "Bread", "Pudding", "Gelato", "Ice Cream", "Cream", "Candy", "Fudge", "Chocolate",
"Punch", "Pie", "Cereal", "Bubble", "Gum", "Cheese", "Coffe", "Mimosa", "Zkittlez", "Snack", "Munchie", "Slush"
},
-- other edibles
{ rules.LAST, "Milk", "Juice", "Coffe", "Moonshine", "Poison", "Potion", "Medicine" },
-- tastes/smells
{ rules.ANY, "Sour", "Sweet", "Bitter", "Pungent", "Vanilla", "Sugar" },
-- animals
{ rules.ANY, "Animal", "Ape", "Gorillla", "Monkey", "Rhino", "Cow", "Bull", "Tiger", "Lion", "Snake", "Dawg", "Cat",
"Hog", "Bird", "Shark", "Turtle", "Reptile", "Gator", "Chicken", "Lamb", "Spider", "Scorpion", "Octopus",
"Mosquito", "Bee", "Wasp", "Gnat"
},
-- cryptozoology'ish
{ rules.ANY, "Alien", "Vampire", "Ghost", "Kraken", "Cthulu", "Zombie", "Monster" },
-- plant terms
{ rules.LAST, "Kush", "Skunk", "Bud", "Tree", "Bush", "Shrubbery", "Stick", "Leaf" },
{ rules.NOT_FIRST, "Haze", "Fog", "Ice", "Snow", "Rain", "Lava" },
-- colours
{ rules.NOT_LAST, "Red", "Green", "Blue","White", "Black", "Purple", "Yellow" },
-- metals
{ rules.ANY, "Silver", "Gold", "Golden", "Platinum", "Lead", "Iron", "Steel"},
-- chemistry
{ rules.ANY, "Diesel", "Gasoline", "Fuel", "Grease", "Oil", "Fire", "Smoke" },
-- accidents
{ rules.LAST, "Explosion", "Implosion", "Crash", "Death", "Dead" },
-- relations
{ rules.ANY, "Widow", "Girl", "Sister", "Granddaddy", "Grandma", "Daddy", "Mama", "Babushka" },
-- spiritual
{ rules.ANY, "Spirit", "Soul", "Brain", "Dream", "Trip", "Thought" },
-- other attributions
{ rules.ANY, "Hindu", "Shiva", "Priest", "Shaman" },
-- occupations, activities
{ rules.ANY, "Driver", "Ryder", "Pilot", "Diver" },
-- body parts
{ rules.ANY, "Head", "Foot", "Hand", "Finger", "Spine" },
-- bodily functions
{ rules.ANY, "Cough", "Sneeze", "Breath", "Vomit", "Fuck", "Bite" },
-- misc
{ rules.ANY, "Amnesia", "Flashback", "Clearvoyant" },
-- music
{ rules.ANY, "Rock", "Reggeae", "Pop", "Riff", "Fuzz" },
-- numbers
{ rules.NOT_FIRST, "1", "47", "42", "303", "420", "707", "666" },
-- numbers
{ rules.FIRST, "One", "Single", "Double", "Triple", "Quadruple" },
-- expediency, size
{ rules.ANY, "Express", "Fast", "Big", "XL", "XXL", "Super", "Extra", "Good", "Great", "Ultra", "Plus" },
-- vehicles
{ rules.LAST, "Motor", "Airplane", "Jet", "Rocket", "Submarine", "Train" },
-- positions
{ rules.ANY, "Queen", "King", "Emperor", "Joker", "Devil", "Belzebub", "Saint", "Sinner", "Baby" },
}
-- Default min/max words per name and number of names to generate
local min_words_in_name = 2
local max_words_in_name = 4
local n_names = 10
-- Give up after MAX_ATTEMPTS, could be garbage dictionary data or a really unlucky RNG sequence.
local MAX_ATTEMPTS = 1000000
-- Tests if an element exists in a table.
function find_element( element, table )
local k,v
for k,v in pairs(table) do
if v == element then
return true
end
end
return false
end
function make_name( n_words, dictionary )
if n_words > #dictionary then
error "More words than dictionary word groups"
end
local groups, name = {}, ""
local group_i, word_i, group_attempts, name_attempts = 1, 1, 0, 0
repeat
name_attempts = name_attempts + 1;
if name_attempts > MAX_ATTEMPTS then
error "Can't make name - Timeout due to impossible dictionary or bad luck"
end
repeat
group_attempts = group_attempts + 1;
if group_attempts > MAX_ATTEMPTS then
error "Can't find unique groups - Timeout"
end
group_i = math.random( #dictionary )
if #dictionary[group_i] < 2 then
error "Empty dictionary word group"
end
until find_element( group_i, groups ) == false
groups[word_i] = group_i
if dictionary[group_i][1] == rules.ANY
or dictionary[group_i][1] == rules.FIRST and word_i == 1
or dictionary[group_i][1] > 0 and dictionary[group_i][1] == word_i
or dictionary[group_i][1] == rules.LAST and word_i == n_words
or dictionary[group_i][1] == rules.MIDDLE and word_i > 1 and word_i < n_words
or dictionary[group_i][1] == rules.NOT_FIRST and word_i ~= 1
or dictionary[group_i][1] == rules.NOT_LAST and word_i ~= n_words
then
name = name .. dictionary[group_i][ 1 + math.random( 2, #dictionary[group_i] - 1 )]
if word_i < n_words then
name = name .. " "
end
word_i = word_i + 1
end
until word_i > n_words
return name
end
function print_table( t )
local k,v
for k,v in pairs(t) do
print(v)
end
end
-- Make a list of N unique names and print them out
function make_strain_names( min_words, max_words, n_names )
if min_words < 1 or max_words < min_words or n_names < 1 then
error "Your arguments are rubish!"
end
math.randomseed(os.time())
local names, attempts = {}, 0
repeat
attempts = attempts + 1
if attempts > MAX_ATTEMPTS then
error "Can't makes list of names - Timeout or impossible dictionary"
end
local name = make_name( math.random(min_words, max_words), strain_dictionary )
if find_element( name, names ) == false then
table.insert(names, name)
end
until #names == n_names
print_table( names )
end
-- Command line arguments overrides defaults
function get_arg( t, n, default )
if arg ~= nil and #arg >= n then
return arg[n]
end
return default
end
-- Make it happen!
make_strain_names( tonumber( get_arg( arg, 1, min_words_in_name ) ),
tonumber( get_arg( arg, 2, max_words_in_name ) ),
tonumber( get_arg( arg, 3, n_names ) )
)
-- EOF
To embed this program on your website, copy the following code and paste it into your website's HTML: