Author Topic: name generator  (Read 8951 times)

Offline Nulvect

  • BFF
  • ***
  • Posts: 127
    • View Profile
name generator
« on: September 21, 2007, 04:31:27 pm »
I'm thinking of coding a name generator in LPC that people can use when connecting. After finding and reading the history of http://www.rinkworks.com/namegen/, I think I have a good idea of where to start: syllables and rules for putting them together.

Perhaps like this:

Code: [Select]
string *vowels = ({
  "a", "e", "i", "o", "u", "y",
});

string *consonants = ({
  "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
  "n", "p", "q", "r", "s", "t", "v", "w", "x", "z",
  "ya", "ye", "yi", "yo", "yu",
});

mapping syllable_rules = ([
  "bla" : ([
    "before" : ({ "C", "i", "u" }),
    "after" : ({ "V", "b", "l", "r" })
  ]),
  "hor" : ([
    "before" : ({ "a", "d", "k", "n" }),
    "after" : ({ "V", "s", "t", "c" }),
  ]),
]);

int is_vowel(string c) { return member_array(c, consonants) == -1; }

Basically, grab a random syllable, then grab another random one to add to the end, check if the new one fits the "after" rule of the first one, and if the first one fits the "before" rule of the new one, and if so, put them together. Random name length, biased toward medium, with a maximum. C stands for any consonant and V for any vowel.

What I'm looking for is anyone who's done this sort of thing before and any advice they might have. This basically falls into the realm of language parsing, with which I have very little experience. Will I be fiddling with the rules and adding new syllables until the day I die?? Or is it possible to get a fairly good generator without devoting months of my life??

Offline silenus

  • BFF
  • ***
  • Posts: 199
    • View Profile
Re: name generator
« Reply #1 on: September 21, 2007, 05:59:34 pm »
I really don't know much about this area either but here is my 2 cents. I recently saw a pretty realistic natural language generator which relies on stochastic models by fitting against existing papers. It produces complete gibberish but it's grammatically correct and sounds reasonable. My suggestion might be to consider looking at a name set of reasonable names (this might be hard to come by since they are fantasy names after all) and use them to fit a Markov model of some sort to the data.

Offline quixadhal

  • BFF
  • ***
  • Posts: 642
    • View Profile
    • WileyMUD
Re: name generator
« Reply #2 on: September 21, 2007, 11:09:17 pm »
A good place to look for ideas in this area is the language code that's in several mudlibs.  I believe discworld and nightmare both had language support so if you didn't know a language, speech in it would be generated via some grammer rules that tended to make particular languages "feel" different.  So dwarven speech would have more consonants and choppy syllables, elven speech would be more soft combinations and vowels, etc.

That's a plus if you're using an account system, or just allowing people to log in as "new" and not pick their name until after their race/ancestry/nationality is set.

Offline Tricky

  • BFF
  • ***
  • Posts: 189
  • I like what I code and I code what I like!
    • View Profile
Re: name generator
« Reply #3 on: September 22, 2007, 01:16:15 am »
I put this quick hack together to show what can be done with a simple string of seemingly random letters.

Just call genNames() to get a list of names. The observant (and older) lot amongst you may recognise 1 or 2 names. :)

Code: [Select]
string digrams =
  "ABOUSEITILETSTONLONUTHNO" +
  "..LEXEGEZACEBISO" +
  "USESARMAINDIREA." +
  "ERATENBERALAVETI" +
  "EDORQUANTEISRION";

mapping seed = ([
  "w0": 0,
  "w1": 0,
  "w2": 0,
]);

int rotatel(int x)
{
    int tmp = (x & 255) * 2;

    if(tmp > 255) tmp -= 255;

    return tmp;
}

int twist(int x)
{
    return (256 * rotatel(x / 256)) + rotatel(x & 255);
}

void next()
{
    seed["w0"] = twist(seed["w0"]);
    seed["w1"] = twist(seed["w1"]);
    seed["w2"] = twist(seed["w2"]);
}

void tweakseed()
{
    int tmp;

    tmp = seed["w0"] + seed["w1"] + seed["w2"];
    tmp &= 65535;

    seed["w0"] = seed["w1"];
    seed["w1"] = seed["w2"];
    seed["w2"] = tmp;
}

string makename(string pairs)
{
    string name = "";
    int pair1, pair2, pair3, pair4;
    int longname;

    longname = seed["w0"] & 64;

    pair1 = 2 * ((seed["w2"] / 256) & 31); tweakseed();
    pair2 = 2 * ((seed["w2"] / 256) & 31); tweakseed();
    pair3 = 2 * ((seed["w2"] / 256) & 31); tweakseed();
    pair4 = 2 * ((seed["w2"] / 256) & 31); tweakseed();

    name += sprintf("%c", pairs[pair1]);
    name += sprintf("%c", pairs[pair1 + 1]);
    name += sprintf("%c", pairs[pair2]);
    name += sprintf("%c", pairs[pair2 + 1]);
    name += sprintf("%c", pairs[pair3]);
    name += sprintf("%c", pairs[pair3 + 1]);

    if(longname)
    {
        name += sprintf("%c", pairs[pair4]);
        name += sprintf("%c", pairs[pair4 + 1]);
    }

    name = implode(explode(name, "."), "");

    return capitalize(name);
}

string genNames()
{
    string array names = ({ });
    string pairs;
    int num = 1;

    digrams = lower_case(digrams);
    pairs = digrams[24..<1];

    seed["w0"] = 0x5a4a;
    seed["w1"] = 0x0248;
    seed["w2"] = 0xb753;

    for(int i = 1; i < num; ++i) next();

    for(int i = 0; i < 256; ++i)
        names += ({ makename(pairs) });

    return implode(names[0..<2], ", ") + " and " + names[<1] + "\n";
}

Tricky

Offline Tricky

  • BFF
  • ***
  • Posts: 189
  • I like what I code and I code what I like!
    • View Profile
Re: name generator
« Reply #4 on: September 22, 2007, 01:20:00 am »
Forgot to quote the original...

http://www.iancgbell.clara.net/elite/text/index.htm

Tricky

Offline detah

  • BFF
  • ***
  • Posts: 190
  • Ruler of 2D
    • View Profile
Re: name generator
« Reply #5 on: September 24, 2007, 01:10:44 pm »
I dont know how 'hard' you want this coded, but I have ripped open some strategy games and looked at how they generate names for cities and npcs. I found that they typically use a 3 syllable approach to naming. So you start with three arrays. Array one is a listing of First syllables. Array two is a listing of the middle syllable, etc. Then those are put together to form a word. They have multiple lists for each race. So for example, the Dwarven names would have their own 3 arrays, the Elven names would have their own 3 arrays, etc.

Here is one of the selections. I think this one is for the humans. This one is taken from Warlords 3 Darklords Rising, I believe.

[NAME]
Silver
Gold
Skul
Crown
Jewel
Death
Red
Black
Green
King
Queen
Prince
Duke
Lord
Gem
Dark
Ring
Mage
Light
Sword
Mace
Shield
Maiden

[SYL1]
Aern
Gal
Fer
Bel
Mel
Bal
Xal
Xaj
Zan
Zar
Zhul
Yor
Nar
Gon
Trel
Grul
Pret
Ban
Mor
Tan
Ten
Tul
Gol
Ghul
Dar
Dran
Drak
Dral
Del
Drel
Dul
Drul
Dhul
Dhol
Eral
Elan
Hrul
Han
Hol
Hop
Jus
Kil
Khaz
Khur
Zen

[SYL2]
an
a
or
o
in
il
al

[SYL3]
ton
rak
rik
thas
thor
gor
dros
heim
dor
doria
kith
kin
ing
gon
tor
torin
berg
ville
side
shand
lay
thas
dalf
drak
nakh
rol
gnol
myr
lock
loch
moon
gate
leaf
bow
ice
rim
tooth

The NAME array is only used for naming Heroes in the game. It generates some pretty cool names.

Offline Nulvect

  • BFF
  • ***
  • Posts: 127
    • View Profile
Re: name generator
« Reply #6 on: September 24, 2007, 07:33:00 pm »
Thanks for the info, everyone.

I have to admit that I don't understand the logic or terms used in the code that Tricky posted, nor do I know about stochastic models. I'll have to study this stuff a bit more, I guess.

I'll also check out that language code in nightmare lib.

Offline daelaskai

  • BFF
  • ***
  • Posts: 174
    • View Profile
Re: name generator
« Reply #7 on: September 24, 2007, 08:54:15 pm »
I don't understand Tricky's code either, but it works.  Tricky, any chance you could post some comments on the code for newbies like me to learn?  One question I had was: "What does '0x5a4a' mean?"  I see that it is a number when I do "eval return 0x5a4a" it returns 23114 though I don't know why it returns that.  The rest of the code is just as confusing to me.  Any tutoring would be appreciated :).

Daelas@Neverland

Offline JohnOC

  • Acquaintance
  • *
  • Posts: 10
    • View Profile
Re: name generator
« Reply #8 on: September 24, 2007, 09:20:36 pm »
One question I had was: "What does '0x5a4a' mean?"  I see that it is a number when I do "eval return 0x5a4a" it returns 23114 though I don't know why it returns that.

Well I can't answer for a lot of the rest of it, but for this I can.

The 0x prefix on any integer value tells the compiler or interpreter that the value is in hexadecimal (base 16) format.  So, each digit of the following number can have values from 0-9 and A-F.  This is useful for compactly representing numbers of up to a certain size for a computer to use.  Since 16 = 2^4, two hexadecimal digits can represent any 32-bit value.   So 0x5a4a could also be representing the binary value '101101001001010'




Offline JohnOC

  • Acquaintance
  • *
  • Posts: 10
    • View Profile
Re: name generator
« Reply #9 on: September 24, 2007, 09:25:39 pm »
Since 16 = 2^4, two hexadecimal digits can represent any 32-bit value.

Wow, I done butchered that one.  Got too far ahead of myself.

2^4 is 16.  So any one hexadecimal (or hex) digit is 4 bits.
Two hex digits is thus 8 bits, and 4 is 16 bits.


Offline Tricky

  • BFF
  • ***
  • Posts: 189
  • I like what I code and I code what I like!
    • View Profile
Re: name generator
« Reply #10 on: September 24, 2007, 10:01:52 pm »
I don't understand Tricky's code either, but it works.  Tricky, any chance you could post some comments on the code for newbies like me to learn?  One question I had was: "What does '0x5a4a' mean?"  I see that it is a number when I do "eval return 0x5a4a" it returns 23114 though I don't know why it returns that.  The rest of the code is just as confusing to me.  Any tutoring would be appreciated :).

Daelas@Neverland
:D

The original was written by a far better man than me. http://www.iancgbell.clara.net/elite/text/index.htm

rotatel is just the same as the 6502 opcode ROL - shifts the 8 bit value to the left once and places what was in the MSB (most significant bit) into the LSB (least significant bit) position.
twist does some manipulation on a 16 bit value - think of it as two 8 bit values split up, rotated left once and then joined back together. (I visualize it as 2 carosels side by side)
next just twists each of the three 16 bit values in the seed mapping. This is called once for each of the 8 galaxies. So for galaxy 3, next is called 3 times. Side-note: There are a maximum of 8 galaxies, if you try to call next more than 8 times the seed values will wrap. Obvious really as there are only 8 bits in a byte.

makename is called 256 times for each of the planet names. 4 pairs of letters are picked from the digram (literally meaning letter pair) array (from position 24 onwards). Each time a pair is picked, the seed is 'tweaked'. A '.' in the array means don't print it.

Code: (tweakseed) [Select]
void tweakseed()
{
    int tmp;

    tmp = seed["w0"] + seed["w1"] + seed["w2"];

    /* 65535 == 0xffff */
    /* So this just preserves the bottom 16 bits of tmp */
    tmp &= 65535;

    seed["w0"] = seed["w1"];
    seed["w1"] = seed["w2"];
    seed["w2"] = tmp;
}

Tricky

Offline chaos

  • BFF
  • ***
  • Posts: 291
  • Job, school, social life, sleep. Pick 2.5.
    • View Profile
    • Lost Souls
Re: name generator
« Reply #11 on: September 24, 2007, 10:22:16 pm »
I use a set of language/culture based name style definitions that work from a typical basis of combining two or three syllables, with various settings for tweaking the behavior.  Explaining everything that goes into this would take forever, so I'll just paste a random sample; this is my file /def/name_style/sperethiel_masculine.c.  (Sperethiel == "Elvish".)

Code: [Select]
#include <case_pattern.h>
#include <names.h>
#include <rarity.h>

inherit "/std/def/name_style";

void configure() {
    ::configure();
    set_name_style_name("Sperethiel masculine");
    set_name_style_rarity(Rarity_Common);
    set_name_style_typical_element(Name_Element_Personal);
    set_name_style_backup(({
        "Aelvalie masculine",
        "Cilaghai masculine",
    }));
    add_name_style_naming(([
        Naming_Begin                : ({
            "eae",
            "ae",
            "ara",
            "bina",
            "cala",
            "cele",
            "celi",
            "cora",
            "dai",
            "ele",
            "elro",
            "fara",
            "ga",
            "gala",
            "gi",
            "glo",
            "lhe",
            "li",
            "lle",
            "lo",
            "mi",
            "ni",
            "que",
            "sae",
            "sha",
            "ta",
            "tara",
            "tere",
            "tie",
            "vala",
            "vali",
            "y",
        }),
        Naming_Middle               : ({
            "b",
            "band",
            "brimb",
            "dand",
            "del",
            "dr",
            "l",
            "lthon",
            "lyr",
            "mitr",
            "ml",
            "n",
            "nd",
            "nt",
            "rand",
            "rd",
            "rfind",
            "rid",
            "rim",
            "rn",
            "ryll",
            "str",
            "tal",
            "th",
            "thon",
            "tir",
            "tor",
            "thr",
        }),
        Naming_End                  : ({
            "al",
            "an",
            "and",
            "ar",
            "are",
            "arn",
            "andir",
            "e",
            "el",
            "elis",
            "ern",
            "iel",
            "ien",
            "iend",
            "ond",
            "or",
            "orn",
            "uial",
            "uan",
            "yl",
            "yn",
            "yr",
            "ys",
        }),
        Naming_Count                : ([
            2                       : 5,
            3                       : 95,
        ]),
        Naming_Rarity               : Rarity_Very_Common,
        Naming_Flags                : Naming_Flag_Middle_As_End,
    ]));
}

I just now had this name style generate some random names for me; it gave me Terelthon, Calarimor, Glodelar, Celebandond, and Liban.

Offline Tricky

  • BFF
  • ***
  • Posts: 189
  • I like what I code and I code what I like!
    • View Profile
Re: name generator
« Reply #12 on: September 24, 2007, 10:34:28 pm »
I've got a Shakesperian insult generator that works a bit like that. Hidden somewhere on one of my old hard-drives.

Tricky

Offline chaos

  • BFF
  • ***
  • Posts: 291
  • Job, school, social life, sleep. Pick 2.5.
    • View Profile
    • Lost Souls
Re: name generator
« Reply #13 on: September 25, 2007, 09:09:09 am »
/daemon/insults.c:

Code: [Select]
// Insult generation daemon
//
// Drawing on the various "Shakespearean Insult Generators" flying around
// the net as text files, programs, Web applications, and God knows what
// else, as well as the inestimable _Ruled Britannia_ by Harry Turtledove.
//
// by Chaos, Fri May 20 14:36:49 EDT 2005

inherit "/std/daemon";
inherit "/mod/daemon/control";

#include <daemon.h>

internal string array adjectives;
internal string array nouns;

void configure() {
    ::configure();
    set_creator("chaos");
    set_credits("Harry Turtledove");
    adjectives = ({
        "agate-ring",
        "artless",
        "base-court",
        "bat-fowling",
        "bawdy",
        "beef-witted",
        "beetle-headed",
        "beslubbering",
        "boil-brained",
        "boot-licking",
        "bootless",
        "caddis-garter",
        "cheap",
        "churlish",
        "clapper-clawed",
        "clay-brained",
        "clouted",
        "cockered",
        "common-kissing",
        "craven",
        "crook-pated",
        "crystal-button",
        "currish",
        "damned",
        "dankish",
        "dismal-dreaming",
        "dissembling",
        "dizzy-eyed",
        "doghearted",
        "dread-bolted",
        "droning",
        "earth-vexing",
        "elf-skinned",
        "errant",
        "fashion-mongering",
        "fat-kidneyed",
        "fawning",
        "fen-sucked",
        "flap-eared",
        "flap-mouthed",
        "fly-bitten",
        "fobbing",
        "folly-fallen",
        "fool-born",
        "frothy",
        "froward",
        "full-gorged",
        "gleeking",
        "goatish",
        "goblin-bred",
        "gorbellied",
        "guts-griping",
        "half-faced",
        "hasty-witted",
        "hedge-born",
        "hell-hated",
        "idle-headed",
        "ill-breeding",
        "ill-nurtured",
        "impertinent",
        "infectious",
        "jarring",
        "knot-pated",
        "knotty-pated",
        "leather-jerkin",
        "loggerheaded",
        "lumpish",
        "mad-mustachio",
        "mammering",
        "mangled",
        "mechanical",
        "mewling",
        "milk-livered",
        "motley-minded",
        "onion-eyed",
        "outfacing",
        "paunchy",
        "peevish",
        "plume-plucked",
        "pottle-deep",
        "pox-marked",
        "pox-ridden",
        "poxy",
        "pribbling",
        "puke-stocking",
        "puking",
        "puny",
        "purple-hued",
        "qualling",
        "ragged",
        "rank",
        "recreant",
        "reeky",
        "reeling-ripe",
        "rough-hewn",
        "rude-growing",
        "rump-fed",
        "ruttish",
        "salt-butter",
        "saucy",
        "scambling",
        "shard-borne",
        "sheep-biting",
        "smooth-tongue",
        "spleeny",
        "spongy",
        "spur-galled",
        "stinking",
        "surly",
        "swag-bellied",
        "tardy-gaited",
        "thrice-damned",
        "tickle-brained",
        "toad-spotted",
        "tottering",
        "tripe-visaged",
        "unchin-snouted",
        "unmuzzled",
        "vain",
        "venomed",
        "vile-minded",
        "villainous",
        "warped",
        "wayward",
        "weather-bitten",
        "weedy",
        "whoreson",
        "yeasty",
    });
    nouns = ({
        "ape",
        "apple-john",
        "baggage",
        "barnacle",
        "blackguard",
        "bladder",
        "boar-pig",
        "braggart",
        "bugbear",
        "bugger",
        "bum-bailey",
        "callet",
        "canker-blossom",
        "catchpole",
        "clack-dish",
        "clotpoll",
        "codpiece",
        "cod-walloper",
        "coxcomb",
        "cur-dog",
        "dastard",
        "death-token",
        "dewberry",
        "flap-dragon",
        "flax-wench",
        "flibbertigibbet",
        "flirt-gill",
        "foot-licker",
        "fustilarian",
        "giglet",
        "gudgeon",
        "haggard",
        "harpy",
        "hedge-pig",
        "horn-beast",
        "hugger-mugger",
        "Jack",
        "joithead",
        "knave",
        "lecher",
        "lewdster",
        "lout",
        "lown",
        "maggot",
        "maggot-pie",
        "malt-worm",
        "mammet",
        "measle",
        "milksop",
        "minnow",
        "miscreant",
        "moldwarp",
        "mumble-news",
        "nut-hook",
        "pigeon-egg",
        "pignut",
        "pouch",
        "pumpion",
        "puttock",
        "rampallian",
        "rascal",
        "ratsbane",
        "recreant",
        "rogue",
        "scullion",
        "scut",
        "skainsmate",
        "strumpet",
        "varlet",
        "vassal",
        "villain",
        "wagtail",
        "wart",
        "whey-face",
        "wool-sack",
    });
}

string array query_insult_adjectives() {
    return adjectives;
}

string array query_insult_nouns() {
    return nouns;
}

string query_random_insult_adjective() {
    return random_element(adjectives);
}

string query_random_insult_noun() {
    return random_element(nouns);
}

varargs string query_random_insult(int num_adjectives) {
    if(num_adjectives)
        num_adjectives = max(num_adjectives, 0);
    else
        num_adjectives = 1 + random(3) + (!random(10) ? 3 : 0);
    string noun = query_random_insult_noun();
    unless(num_adjectives)
        return noun;
    string array adjectives = ({});
    while(sizeof(adjectives) < num_adjectives)
        array_specify(&adjectives, query_random_insult_adjective());
    switch(num_adjectives) {
    case 1  :
        return adjectives[0] + " " + noun;
    case 2  :
        return implode(adjectives, " ") + " " + noun;
    default :
        return implode(adjectives[0..<2], ", ") + " " +
            adjectives[<1] + " " + noun;
    }
}

varargs string query_random_insult_sentence(int num_adjectives) {
    return "Thou " + query_random_insult(num_adjectives) + "!";
}

Very useful, especially for /obj/mental_disorders/tourette's_syndrome.c.