Name Generating Lua Script

I was on a car trip a while back and programmed this on my iPod. It took maybe an hour and a half, so it is VERY ugly but it does what I wanted it to do. Generate senseless names that sound like they are from some ancient Inca-related tribe. This was programmed in Lua.


nvowels = {"b","c","d","f","g","h","j","k","l","m","n","p","r","s","t","v","w","
x","y","z"} 
 
vowels = {"a","e","i","o","u"} 
 
cho = math.random(5,7) 
local name = {"b","c","d","f","g","h","j"} 
 
function getBoolBasedLetter() 
if getBool() then 
return getVowel() 
else 
return getNVowel() 
end 
end 
 
function setLetters() 
if getBool() then 
name[1] = getVowel() 
else 
name[1] = getNVowel() 
end 
it = 2 
while it <= cho do 
if isVowel(name[it - 1]) then 
name[it] = getNVowel() 
else 
name[it] = getVowel() 
end 
name[it + 1] = getBoolBasedLetter() 
if name[it] == "u" and name[it + 1] == "i" then 
name[it] = getBoolBasedLetter() 
end 
it = it + 2 
end 
if name[cho] == "q" then 
name[cho] = getMNVowel("q") 
end 
rand = math.random(1,12) 
if rand == 10 then 
name[1] = "c" 
name[2] = "h" 
end 
if isVowel(name[3]) then 
else 
name[3] = getVowel() 
if rand == 9 then 
name[1] = "s" 
name[2] = "t" 
name[3] = getVowel() 
end 
rand = math.random(1,12) 
if rand == 6 then 
name[cho] = "r" 
name[cho - 1] = "o" 
end 
if isNVowel(name[cho]) and isNVowel(name[cho - 1]) then 
name[cho - 1] = getVowel() 
end 
end 
end 
 
 
 
 
function getVowel() 
return vowels[math.random(1,5)] 
end 
 
function isVowel(n) 
if n == "a" or n == "e" or n == "i" or n == "o" or n == "u" then 
return true 
else 
return false 
end 
end 
 
function isNVowel(n) 
if n == "a" or n == "e" or n == "i" or n == "o" or n == "u" then 
return false 
else 
return true 
end 
end 
 
function getMVowel(n) 
re = getVowel() 
while re == n do 
re = getVowel() 
end 
return re 
end 
 
function getMNVowel(n) 
re = getNVowel() 
while re == n do 
re = getNVowel() 
end 
return re 
end 
 
function getNVowel() 
return nvowels[math.random(1,20)] 
end 
 
function getBool() 
if math.random(1,2) == 1 then 
return true 
else 
return false 
end 
end 
 
setLetters() 
 
local endName = name 
 
iter = 1 
while iter <= cho do 
if iter == 1 then 
name[iter] = name[iter].upper(name[iter]) 
end 
io.write(name[iter]) 
iter = iter + 1 
end 
print() 

And here are some of the outputs of:

Foouci
Emozo
Kaemyeu
Umuuj
Veene
Azofiga
Moabwai
Eyepo
Iwior
Chilto
Steua
Emocnoa

I honestly had no idea how other name generating scripts did it so I just kind of hard coded the whole thing into an ugly beast. Maybe other ones somehow use Perlin noise somehow?

It’s rather cool. But it doesn’t really look like Java though :persecutioncomplex:.
I need something like this in the future though, thanks for bringing up the idea.

That’s why the title says it’s in Lua.

It is pretty cool :smiley: :smiley:

Thanks for my first medal! :D. Anyways, yeah it’s lua, I know lua is simple and all that but it holds a special place in my heart because it was my first programming language :). I suggest you look into learning it, it’s easy and you can use it for simple processing scripts. It’s very fast.

I was bored and procrastinating my project
so I may have mucked this up a lot, since I don’t know lua, but I attempted a java port of your code. I hope you don’t mind :clue: ??? :point: :persecutioncomplex: :stuck_out_tongue:

Although I would have preferred a different code style(e.g. for loops instead of while), I tried to stay as true 1:1 to your code as possible.

There are some locations, I just don’t know what is going on, either 0 or 1 array indexing. Or a half dozen other ??? not sure abouts

Just an example of what my port produced
Fayeiy
Tecookk
Keoual
Istater
Niauel
Kuaiuk
Ebbiuh
Achimag
Jakato
Seuumo


import java.util.Random;

public class Test {

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			NameBuilder nm = new NameBuilder();

			String tempName = "";
			char[] name = nm.setLetters();
			int cho = nm.len();

			int iter = 0;
			while (iter <= cho) {
				if (iter == 0) {
					name[iter] = Character.toUpperCase(name[iter]);
				}
				tempName += name[iter];
				iter = iter + 1;
			}

			System.out.println(tempName);// print()
		}
	}
}

class NameBuilder {
	private char[] nvowels = { 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z' };
	private char[] vowels = { 'a', 'e', 'i', 'o', 'u' };
	private char[] name = { 'b', 'c', 'd', 'f', 'g', 'h', 'j' };

	private Random r = new Random();
	private int rand;
	private int cho;

	private char getBoolBasedLetter() {
		if (getBool()) {
			return getVowel();
		} else {
			return getNVowel();
		}
	}

	private char getVowel() {
		return vowels[r.nextInt(5)];
	}

	private boolean isVowel(char n) {
		if (n == 'a' || n == 'e' || n == 'i' || n == 'o' || n == 'u') {
			return true;
		} else {
			return false;
		}
	}

	private boolean isNVowel(char n) {
		if (n == 'a' || n == 'e' || n == 'i' || n == 'o' || n == 'u') {
			return false;
		} else {
			return true;
		}
	}

	// unused?
	private char getMVowel(char n) {
		char re = getVowel();
		while (re == n) {
			re = getVowel();
		}
		return re;
	}

	private char getMNVowel(char n) {
		char re = getNVowel();
		while (re == n) {
			re = getNVowel();
		}
		return re;
	}

	private char getNVowel() {
		return nvowels[r.nextInt(20)];
	}

	private boolean getBool() {
		return r.nextBoolean();
	}

	public int len() {
		return cho;
	}

	public char[] setLetters() {
		cho = 5 + r.nextInt(2); // math.random(5,7)

		if (getBool()) {
			name[0] = getVowel();
		} else {
			name[0] = getNVowel();
		}

		int it = 1;
		while (it <= cho) {
			if (isVowel(name[it - 1])) {
				name[it] = getNVowel();
			} else {
				name[it] = getVowel();
			}
			name[it + 1] = getBoolBasedLetter();

			if (name[it] == 'u' && name[it + 1] == 'i') {
				name[it] = getBoolBasedLetter();
			}
			it = it + 2;
		}

		if (name[cho] == 'q') {
			name[cho] = getMNVowel('q');
		}
		rand = r.nextInt(12);// math.random(1,12)
		if (rand == 10) {
			name[1] = 'c';
			name[2] = 'h';
		}
		if (isVowel(name[3])) {
		} else {
			name[3] = getVowel();
			if (rand == 9) {
				name[1] = 's';
				name[2] = 't';
				name[3] = getVowel();
			}
			rand = r.nextInt(12);// math.random(1,12)
			if (rand == 6) {
				name[cho] = 'r';
				name[cho - 1] = 'o';
			}
			if (isNVowel(name[cho]) && isNVowel(name[cho - 1])) {
				name[cho - 1] = getVowel();
			}
		}
		return name;
	}
}

Very cool :slight_smile: I don’t mind at all. As you could probably tell, it does need some work, but at least it’s not completely random letters ;). The getMVowel function I created was just for if I needed it later. I think you may have mixed up some of the order in the set letter method, it is imperative that they go in the original order. I may be mistaken though. Good job!

That’s a pretty cool little script. I may have to make use of it for NPC naming in something I’m working on. ;D As a side note, isn’t the isNVowel function somewhat superfluous? Surely the same result can be obtained by negating the results from isVowel. If you wanted to keep it as a convenience function, you could cut down on some code duplication like so (I also cut out the unneeded else statement in the isVowel function):


private boolean isVowel(char n) {
    if (n == 'a' || n == 'e' || n == 'i' || n == 'o' || n == 'u') {
        return true;
    }
    return false;
}

private boolean isNVowel(char n) {
    return !isVowel(n);
}

Thank’s for sharing. 8)

For my code anyways, I had just kept it original to his. I didn’t try optimizing it.
However you can reduce your code even further :point:

Fair enough. My comment was directed at the original script as well. I just wrote the example in Java as my Lua knowledge is enough to follow along with the original script, but not enough for me to reliably write an example in it. :persecutioncomplex:

Touché, LOL. ;D

@namrog
What NVowel and MNVowel mean?

Nvowel = Not vowel = Consonant.

While I am not sure what the M in MVowel means.
Its intending to fetch a different vowel then currently inserted

same for MNVowel, get a new not vowel or consonant. (the idea behind this function appears to be so that you don’t have something like “adaaam” multiple a’s in a row or something

Just like I have no idea why the wordlength variable was “cho” instead of “wordLength” or something. Maybe its another language or maybe its a lua thing? I have no idea. Same with using iter (For iterator) instead of just i in several cases.

The reason for nvowel was because I couldn’t remember consonant at the time :P. MNVowel, at least the M part of it, is my own little way in code for remembering things that produce that opposite/other of the input variable. One of my many quirks. Cho is for choice, the word length, which somehow made sense in my head as I programmed this at 3 in the morning in the car. That stuff happens when you do that.

That’s a neat little way to optimize it, but given the fact that I was thinking it would be used on runtime to generate maybe 100 names and that took about 0.4 seconds on my old, emphasis on OLD, ipod touch I think it’s fine. But hey, if you are using it to generate thousands that is definitely a good optimization.

However I do suggest that you use the actual Lua script instead of the java port because lua is much more light weight and fast.

That’s not an optimization. It’s just a more readable alternative. (In my opinion, maybe other people disagree.) It should be compiled to the same code. (At some point hopefully.) 0.4 seconds seems like a very long time, though, but it probably includes how long it takes for the program/emulator/interpreter to load/start up.

Haha. Well at a minimum the insight into how a person with confusing variable names chooses them and why is interesting.

From a quick look at this code, it seems to represent the probability of certain letters following others using hard coded rules. hat you could do is extend this to derive the rules from actual names used in different languages and/or cultures. You can then generate names that follow these cultural patterns but still appear random. I could be wrong, but I think this is based on the theory of Markov Chains, which can be used to represent rules for generating character sequences using probabilities.

What you need to do is find out for each letter of the alphabet (which doesn’t necessarily have to be latin), what is the probability that every other letter will follow it. To do this, just record a count of how many of each consecutive paring of characters appears in a list of names you provide. You end up with a list of pairings, each with the number of times it appears recorded against it (including spaces or the end of the name - we’ll record this as a terminator character). I store this in a 2D array, with the first letter in the pairing as one dimension and each letter that has been known to follow is as the second. The value in each cell of this matrix is the number of times the pairing appears, ie. the frequency.

To generate a name, start with a random letter. For the each subsequent letter, look up the matrix using the previous letter. You’ll get back a list of letters that have been known to follow it. The frequency of each possible letter will be used as a weighting when randomly choosing one. Keep going until you roll a terminator as the next character.

The end result will be random names of varying lengths that never have character sequences that you don’t see in real life. You don’t need to do any special handling for vowels or consonants, they will just follow the patterns used by the language of the names you have chose.

You can however get instances of the same letter occurring three or more times in a row. This is not too hard to write some extra conditions in your code for, but another option is to make your “chains” longer. What I’ve described above is using letter chains with a length of two. This can be extended to a length of three (or more), however it gets a little complicated. Extending the above solutions to a 3D array might be one way, I took a slightly different approach but it’ll require a bit of thought to explain it properly. I’ll dig up the code and post it somewhere if anyone is interested. The end result is even more natural, however it does seem to lose a bit of randomness - in fact it will often generate regular boring old names such as “John” and “Fred” (no offense to anyone called John or Fred).

I’ve got an old project that does this using variable length chains, but it’s implemented using the Java Application Framework so won’t compile using newer NetBeans versions. If anyone is interested I’ll put the code up somewhere along with an old build.