[alt.sources] Generating Random Names -- source included!

lee@sq.sq.com (Liam R. E. Quin) (09/08/90)

After I posted my ancient, horrible, gunky Rolemaster Non-Player-Character
gnerating program (for fantasy rolepaying games), I got mail asking for
the program it references to generate names for characters.

I have several programs.  I'm posting two here.  The first one I wrote
in 1983 or 1984 (I forget the exact date) at Warwick University, and Kay
Dekker hacked it mercilessly to make it read a file.

The second one I wrote a couple of weeks ago in awk in an hour or so...

If I find more, I'll post them.

These programs want data files, so I'll include two of those.  I'll
also post an article (to rec.games.frp only) talking a little more about
the philosophy behind these, and about strategies for choosing names.
Unless I forget.
These programs both make a name out of
	A start syllable
	0 or more middle syllable [the awk program uses 1 here I think]
	An ending syllable

Here are some sample names made by the C program:
Valayngar      Jestuny        Darorn         Klabere        Ostdy
Ryleyn         Chiesartsene   Jastgethen     Valcheanastar  Caiam
Tocydeas       Jararyr        Kolain         Geyne          Laracy
Lararyr        Harar          Klabain        Moldyddney     Harlyarn
Starluth       Elazer         Darzane        Drassa         Queranayn
Cracy          Calorn         Perisant       Kaern          Tydor

Here are some names made by the awk program:
[Plag-art-ar]     [Jar-carth-bin]   [Klab-art-a]      [Cal-len-ayne]
[Kor-is-ydd]      [El-quent-arne]   [Jar-reth-yne]    [Ob-quent-el]
[Kor-aver-sume]   [Cal-erry-an]     [Vart-lin-yne]    [Chies-arne-ane]
[Win-ime-y]       [Odas-zane-un]    [Est-end-aryn]    [Nor-mel-art]
[Shot-cath-art]   [Pas-aldy-azer]   [Ob-yd-tyne]      [Pan-yne-oller]
[Wearn-arth-yn]   [In-asc-gayn]     [Zol-valer-varn]  [Bae-y-art]

Here are some names made by the C program using the "dragon-oe" list
of syllables (see _Dragon_ No. 72, p.56):
aldwini        healfberct     theodgeofu     hrethwealch    aldsige
beomonfled     sexwuduwiv     quicwict       aelfweald      dryhtbrand
erconwic       beohard        earcongisllid  earngyd        ceonhelm
leofgyd        ceonflaed      hrothmon       tunward        ealdhathu
cuthgifu       grimfrid       cenebrand      healffled      cwicraed
maegengeld     selefridcyn    breguthryd     bregubeald     fordlindwiw

The C ones seem the best.  The awk ones retain the - so you can see how
they're made --- this lets you work out meanings easily if each syllable
is a real word root.  A fairly simple modification would be to have the
"meaning" for each syllable in the file listing syllables.  Then you
could generate (for example)
	Cuth-brand	[Known, famous -- sword]
	Aelf-wic	[Elf -- dwelling-place]
but I'll leave this to enthusiasts.

The rolemaster "npc" program uses "names -i" to generate an "infinite"
stream of names.

Have fun...

Lee


: To unbundle, sh this file
echo x - names.c 1>&2
sed 's/^X//' >names.c <<'@@@End of names.c'
X#include <stdio.h>
X#define MAXSYLLNUM 1024
X#define GETSYLL()	fscanf (dfile, "%s", linebuf);
X#define USAGE "Usage: names [-f file] [-n number-of-names] [-i]\n"
Xchar *progname;
X
X#ifndef NAMEFILE
X# define NAMEFILE "/userpk/etc/req/lib/names/default"
X				/* default file for names */
X#endif
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X
X{
X    extern char *optarg;
X    extern int optind, getopt();
X
X    int  how_many,	/* number of names to generate		*/
X         num_mids,	/*   "     " middle syllables (0-2)	*/
X	 seed = getpid ();	/* for random number generator		*/
X    int  max_start = 0,	/* maximum numbers of start, middle and	*/
X         max_mid = 0,	/* end syllables read  (subject to	*/
X         max_end = 0;	/* ceiling of MAXSYLLNUM) 	 	*/
X
X    char *name_start[MAXSYLLNUM];
X    char *name_middle[MAXSYLLNUM];
X    char *name_end[MAXSYLLNUM];
X    char linebuf[BUFSIZ];
X
X    char *name_file = NAMEFILE;
X    FILE *dfile = NULL;	/* file pointer for data file */
X    int infinity = 0;	/* give an infinite number of names if set */
X    int option;
X
X    progname = argv[0];
X
X    /* parse args */
X
X    while ( (option = getopt(argc, argv, "f:in:") )  != EOF) {
X	switch(option) {
X	    case '?' :	/* no-such-option, getopt has printed a message */
X		fprintf(stderr, "%s", USAGE);
X		exit(1);
X	    case 'f': {	/* -f file-of-syllables */
X		name_file = optarg;
X		break;
X	    }
X	    case 'n': {	/* how many names to do */
X		if ( (how_many = atoi(optarg)) < 0 ) {
X		    err("number of names invalid (-n %d)\n", optarg);
X		    exit(2);
X		}
X		break;
X	    }
X	    case 'i': {	/* infinite number of names */
X		infinity = 1;
X		break;
X	    }
X
X	    default: {
X		fprintf(stderr, USAGE);
X		exit(2);
X	    }
X	}
X    }
X    if (optind != argc) {
X	fprintf(stderr, USAGE);
X	exit(2);
X    }
X    if ((dfile = fopen (name_file, "r")) == NULL) {
X	err("cannot read database %s\n", name_file);
X	exit (2);
X    }
X
X    /* read the data file */
X
X    GETSYLL();
X    while (strcmp(linebuf, "%") != NULL && *linebuf) {
X	name_start[max_start] = (char *) malloc (strlen (linebuf) + 1);
X	strcpy (name_start[max_start++], linebuf);
X	if (max_start == MAXSYLLNUM) {
X	    err("too many initial syllables - but continuing\n");
X	    while (strcmp (linebuf, "%")) GETSYLL();
X	    break;
X	} else GETSYLL();
X    }
X
X    max_mid = 0;		/* initialise medial syllables */
X    GETSYLL();
X    while (strcmp (linebuf, "%") != NULL) {
X	name_middle[max_mid] = (char *) malloc (strlen (linebuf) + 1);
X	strcpy (name_middle[max_mid++], linebuf);
X	if (max_mid == MAXSYLLNUM) {
X	    err("too many medial syllables - but continuing\n");
X	    while (strcmp (linebuf, "%"))
X		GETSYLL();
X	    break;
X	} else GETSYLL();
X    }
X
X    max_end = 0;
X    GETSYLL();
X    while (strcmp (linebuf, "%") != NULL) {
X	name_end[max_end] = (char *) malloc (strlen (linebuf) + 1);
X	strcpy (name_end[max_end++], linebuf);
X	if (max_end == MAXSYLLNUM) {
X	    err("too many final syllables - but continuing\n");
X	    while (strcmp (linebuf, "%"))
X		GETSYLL();
X	    break;
X	} else GETSYLL();
X    }
X
X    /* now print the names out */
X
X    if (!(max_start && max_mid && max_end)) {
X	err("zero-length section in datafile\n");
X	exit (3);
X    }
X
X    srand (seed);
X
X    while (infinity || how_many-- > 0) {
X	printf ("%s", name_start[rand_int () % max_start]);
X	for (num_mids = (rand_int () % 3) / 2; num_mids > 0; num_mids--)
X	    printf ("%s", name_middle[rand_int () % max_mid]);
X	printf ("%s\n", name_end[rand_int () % max_end]);
X    }
X}
X
Xrand_int() {
X	return ((int) (rand() / 37));
X}
X
Xerr(s, a1, a2, a3)
X    char *s;
X{
X	fputs(progname, stderr);
X	fputs(": ", stderr);
X	fprintf (stderr, s, a1, a2, a3);
X}
@@@End of names.c
echo x - default "(put this in $HOME/lib/names, for example)" 1>&2
sed 's/^X//' >default <<'@@@End of default'
XRal
XNa
XArd
XVald
XCal
XHy
XPan
XChies
XPer
XEr
XHob
XHarg
XWin
XMar
XQuarne
XBa
XEr
XOdas
XKa
XMold
XSyn
XRo
XJast
XYal
XNap
XVard
XAs
XBinthe
XZald
XDez
XLas
XUld
XNev
XHaur
XBar
XDas
XTy
XDar
XOst
XTral
XGrave
XEth
XFlar
XYal
XKlab
XHarab
XJar
XNor
XDain
XToc
XBay
XHaith
XCal
XLar
XNaut
XDruc
XBar
XArt
XFor
XMart
XYar
XHa
XNy
XYar
XVerd
XWy
XPlag
XTer
XHaur
XVar
XAr
XDar
XVal
XMar
XCar
XLoc
XWearn
XDras
XBel
XHar
XJar
XFor
XKil
XOc
XAl
XSkal
XNun
XAz
XKop
XHoul
XLab
XJar
XVast
XClaune
XTes
XOb
XNist
XEl
XEst
XZol
XBrow
XPulg
XStar
XKren
XCrac
XScaun
XWal
XQuer
XRy
XCyn
XRusk
XDel
XLab
XMel
XSep
XLor
XRos
XJar
XDaf
XHal
XKol
XIn
XAel
XSald
XKuv
XYm
XCa
XKeld
XBar
XTarl
XShot
XPes
XQuer
XLor
XGeld
XAr
XHar
XBae
XVad
XPas
XUr
XNor
XKir
XVar
XMel
XAr
XShy
XI
XRald
XCor
XSar
XKor
XRol
XHar
XAsh
XDir
XLas
XVab
XAld
XPar
XOb
XHor
XChy
XJar
XRyle
XChar
XHab
XSar
XVart
XNist
XObr
XJar
XGe
XYas
XPav
XJes
XShot
XMar
XHor
XEr
XKi
XHar
XCal
XAnd
X%
Xgur
Xcarn
Xaz
Xacy
Xayn
Xasc
Xgary
Xhen
Xtan
Xarny
Xalen
Xcarth
Xgant
Xrath
Xcam
Xart
Xron
Xarth
Xarth
Xcarad
Xere
Xgeth
Xaldy
Xyn
Xvaler
Xarne
Xaller
Xvarn
Xar
Xan
Xnal
Xtyne
Xar
Xart
Xont
Xaur
Xaver
Xlyn
Xas
Xgar
Xcuth
Xarry
Xor
Xquine
Xastar
Xmel
Xaryn
Xart
Xwar
Xasty
Xzane
Xarik
Xayne
Xloc
Xoller
Xwarty
Xaryne
Xchean
Xquin
Xtar
Xdar
Xreth
Xant
Xan
Xyne
Xax
Xtuny
Xwat
Xjuin
Xa
Xgayn
Xon
Xan
Xcar
Xgine
Xcodd
Xquent
Xeas
Xew
Xazer
Xam
Xly
Xstead
Xorn
Xar
Xcath
Xiera
Xque
Xair
Xla
Xart
Xerry
Xend
Xom
Xast
Xet
Xarty
Xdoth
Xcath
Xert
Xdy
Xorn
Xont
Xtak
Xar
Xart
Xwarne
Xarn
Xin
Xian
Xel
Xak
Xil
Xydd
Xime
Xyn
Xen
Xin
Xim
Xel
Xar
Xro
Xis
Xis
Xro
Xera
Xene
Xin
Xane
Xiam
Xain
Xir
Xun
Xil
Xbin
Xlin
Xis
Xsene
Xbin
Xlir
Xame
Xa
Xfyn
Xy
Xin
Xyd
Xien
Xain
Xyn
Xar
Xer
Xin
Xsume
Xras
Xid
Xmel
Xluth
Xance
Xer
Xyn
Xan
Xar
Xayne
Xeth
Xlen
Xter
Xrik
Xer
Xro
Xtin
Xmel
Xyn
Xris
Xlene
Xane
Xas
X%
Xty
Xcarn
Xar
Xacy
Xer
Xal
Xgary
Xy
Xar
Xarny
Xalen
Xcarth
Xgant
Xy
Xber
Xart
Xdal
Xarth
Xarth
Xan
Xere
Xgeth
Xaldy
Xyn
Xvaler
Xarne
Xaller
Xvarn
Xayne
Xan
Xnal
Xtyne
Xayne
Xart
Xont
Xney
Xaver
Xlyn
Xiel
Xgar
Xy
Xarry
Xor
Xquine
Xastar
Xer
Xaryn
Xart
Xwar
Xasty
Xzane
Xarik
Xayne
Xan
Xoller
Xwarty
Xaryne
Xchean
Xta
Xun
Xtha
Xreth
Xant
Xel
Xyne
Xel
Xtuny
Xwat
Xjuin
Xdor
Xgayn
Xtyn
Xdar
Xcar
Xgine
Xcodd
Xquent
Xeas
Xew
Xazer
Xont
Xly
Xstead
Xorn
Xen
Xcath
Xiera
Xque
Xair
Xla
Xart
Xerry
Xsa
Xar
Xer
Xern
Xarty
Xdoth
Xy
Xert
Xdy
Xorn
Xont
Xern
Xayn
Xart
Xwarne
Xarn
Xin
Xian
Xel
Xak
Xil
Xydd
Xime
Xyn
Xen
Xin
Xim
Xel
Xar
Xro
Xis
Xis
Xro
Xera
Xene
Xin
Xane
Xiam
Xain
Xir
Xun
Xil
Xbin
Xlin
Xis
Xsene
Xbin
Xlir
Xame
Xa
Xfyn
Xse
Xin
Xyd
Xien
Xain
Xyn
Xar
Xer
Xin
Xsume
Xras
Xon
Xmel
Xluth
Xance
Xer
Xyn
Xan
Xar
Xayne
Xeth
Xnyd
Xter
Xrik
Xnik
Xro
Xa
Xmel
Xyn
Xris
Xlene
Xane
Xyr
X%
@@@End of default
echo x - newnames.awk 1>&2
sed 's/^X//' >newnames.awk <<'@@@End of newnames.awk'
X#! /usr/local/bin/nawk -f
X
XBEGIN {
X    srand()
X    Count[0] = 0
X    Section = 0
X}
X
X/^%[ 	]*$/ {
X    Section++;
X    Count[Section] = 0
X    next
X}
X
X/^#.*$/ { next }  # ignore comments
X
X/^[ 	]*$/ { next } # ignore blank lines
X
X{
X    Syllables[Section, Count[Section] ] = $0
X    Count[Section]++
X}
X
XEND {
X    for (;;) {
X	# random from first section
X	printf "[%s", Syllables[0, Rand(0, Count[0] - 1)]
X
X	for (i = 1; i <= Section && i in Count; i++) {
X	    if (Count[i]) {
X		printf "-%s", Syllables[i, Rand(0, Count[i] - 1)]
X	    }
X	}
X
X	printf "]\n"
X    }
X}
X
Xfunction Rand(min, max)
X{
X    return int((max + 1 - min) * rand()) + min
X}
@@@End of newnames.awk
echo x - dragonOEnames 1>&2
sed 's/^X//' >dragonOEnames <<'@@@End of dragonOEnames'
Xaelf
Xaelb
Xaethel
Xaedil
Xbadu
Xbeado
Xbeo
Xblith
Xbregu
Xceol
Xceon
Xcoin
Xcene
Xcuth
Xcud
Xcwic
Xcuic
Xquic
Xdryct
Xdryht
Xead
Xed
Xaead
Xeald
Xald
Xealh
Xalh
Xearcon
Xercon
Xearn
Xecg
Xec
Xeofor
Xeorcon
Xeormen
Xyrmen
Xfolc
Xford
Xfri
Xgold
Xgrim
Xhaem
Xhaeth
Xheah
Xhealf
Xhreth
Xhroth
Xhuaet
Xhyg
Xhugu
Xiaru
Xleof
Xmaegen
Xoidil
Xongen
Xos
Xrath
Xsaex
Xsax
Xsex
Xsele
Xtat
Xtheod
Xtil
Xtorct
Xtrum
Xtun
Xwaeg
Xwig
Xwil
X%
Xbald
Xbeald
Xbalt
Xbalth
Xbeorht
Xberct
Xberict
Xbeorn
Xbern
Xbrand
Xbroad
Xburg
Xburh
Xcyni
Xcyn
Xdegn
Xferth
Xflaed
Xfled
Xfor
Xfrith
Xfrit
Xfrid
Xgar
Xgeld
Xgifu
Xgeofu
Xgisl
Xgund
Xgunn
Xgyth
Xgyd
Xhaed
Xhathu
Xheard
Xhard
Xhere
Xheri
Xhelm
Xhild
Xhun
Xlac
Xlaf
Xlid
Xlind
Xlinda
Xmaer
Xman
Xmon
Xmund
Xnoth
Xraed
Xred
Xrefu
Xric
Xsig
Xsige
Xstan
Xswith
Xswid
Xtheof
Xtheow
Xthryth
Xthryd
Xwealch
Xwalh
Xweald
Xwald
Xweard
Xward
Xwic
Xwict
Xwiht
Xwine
Xwini
Xwiw
Xwiv
Xwuda
Xwida
Xwudu
Xwulf
Xulf
Xwyn
Xwynn
X%
X%
Xbald
Xbeald
Xbalt
Xbalth
Xbeorht
Xberct
Xberict
Xbeorn
Xbern
Xbrand
Xbroad
Xburg
Xburh
Xcyni
Xcyn
Xdegn
Xferth
Xflaed
Xfled
Xfor
Xfrith
Xfrit
Xfrid
Xgar
Xgeld
Xgifu
Xgeofu
Xgisl
Xgund
Xgunn
Xgyth
Xgyd
Xhaed
Xhathu
Xheard
Xhard
Xhere
Xheri
Xhelm
Xhild
Xhun
Xlac
Xlaf
Xlid
Xlind
Xlinda
Xmaer
Xman
Xmon
Xmund
Xnoth
Xraed
Xred
Xrefu
Xric
Xsig
Xsige
Xstan
Xswith
Xswid
Xtheof
Xtheow
Xthryth
Xthryd
Xwealch
Xwalh
Xweald
Xwald
Xweard
Xward
Xwic
Xwict
Xwiht
Xwine
Xwini
Xwiw
Xwiv
Xwuda
Xwida
Xwudu
Xwulf
Xulf
Xwyn
Xwynn
X%
@@@dragonOEnames
echo "Congratulations.  You've unpacked the entire archive." 1>&2
exit 0


-- 
Liam R. E. Quin,  lee@sq.com, SoftQuad Inc., Toronto, +1 (416) 963-8337