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