From mipos3!intelca!oliveb!pyramid!decwrl!hplabs!ucbvax!decvax!tektronix!tekgen!tekred!games-request Wed Jul 22 08:07:00 PDT 1987 Article 44 of comp.sources.games: Path: td2cad!mipos3!intelca!oliveb!pyramid!decwrl!hplabs!ucbvax!decvax!tektronix!tekgen!tekred!games-request From: games-request@tekred.TEK.COM Newsgroups: comp.sources.games Subject: v01i097: rnd - random stuff generator (create a dungeon) Message-ID: <1412@tekred.TEK.COM> Date: 20 Jul 87 21:21:51 GMT Sender: billr@tekred.TEK.COM Lines: 907 Approved: billr@tekred.TEK.COM Submitted by: SalyerDE Comp.sources.games: Volume 1, Issue 97 Archive-name: rnd #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh README <<'END_OF_README' XHi all you net-people! X X[I know it's been a long time, but ... ] X X When I saw Marcus Ranum's Random stuff generator, I thought Xit was a novel idea, but limiting. I wanted a way to nest tables Xand subtables in the subtables themselves. So I have done that. XIt was fairly simple to do, (a tribute to Marcus for easily Xmodified code) just change the 'dohack()' call in 'dosubtable' to Xa 'doparse()' call instead (after stripping off the number). X X This greatly extends the possibilities of the system, but Xmakes it much more likely to blow your stack due to recursion. XBut if you are careful, it should work. X X I also added a few 'features' - there is a default of 10 Xiterations if you hit C/R, each roll of the dice uses the 'best Xof three tries, and the stat command has been modified as follows: X X If you put an '*' as the first character of the stat string, Xthe printout will be in an alternate format, i.e. X stat Gold 3 100 would produce=> Gold: 230 X stat *Gold_pieces 3 100 would produce=> 160 Gold_pieces X X Also included is a subdirectory Dungeon, which uses these new Xfeatures to create random sections of dungeon corridors. It is Xbased loosely on a Dungeon Generator article in a magazine I saw Xmany moons ago (I don't remember which). Look it over and see Xwhat you can do with it. X X I've lost the E-Mail address of Marcus, so I hope he sees Xthis and approves of the changes I've made. I also hope everyone Xelse can use this to further your Dungeon endeavors. X X Real Life?: Dale Salyer (Programmer of the Third Level) X Fantasy: Dark Star (Thief of the Fifth Level) X Coyote (Half-Coyote - Ranger of the Sixth Level) X X E-Mail Address: ...!ihnp4!druhi!des X U.S. Mail: AT&T Info. Systems Laboratories X Denver, CO 80229 END_OF_README if test 1762 -ne `wc -c Makefile <<'END_OF_Makefile' X# Makefile for rnd - random stuff generator XCFLAGS = -O X Xrnd: rnd.c X cc $(CFLAGS) -o rnd rnd.c END_OF_Makefile if test 95 -ne `wc -c rnd.6 <<'END_OF_rnd.6' X.TH rnd 6 12/4/86 X.SH NAME Xrnd \- Random stuff generator X.SH SYNOPSIS X.B rnd Xfile1 file2, etc... X.SH DESCRIPTION X.I rnd Xreads a file and writes generated data to the standard output. XIf more than one file is specified, the files are used as descriptions Xin sequence and merged output is written to the standard output. XTwo types of files are recognized: \fBtable\fP files, and \fBsubtable\fP files. XEach file specified on the command line must be a \fBtable\fP file. X XA table file can contain several types of information: X.TP 8 X\(bu \fBText and Text-formats:\fP XThese are simply text that will be Xincluded in the output. There are, however, a few simple rules. XAny text string ending in a ':' (no space afterwards) will NOT Xhave a newline appended after it. This is to allow a user to Xhave the output from a subtable placed after a text string. XOther text that does not have a ':' as the last character before Xa newline is printed as is. X.TP X\(bu \fBTabs and Formfeeds:\fP XTo insert tabs and formfeeds, a \fB\\t\fP (tab) Xor a \fB\\l\fP (formfeed ^L) is placed as the ONLY thing on that line. XThere must be no other blank spaces, etc. This is inelegant, but Xallows the program to run reasonably fast. If a tab is coded, the Xprogram does NOT automatically generate a newline after it. This Xis to allow a user to separate stats with tabs reasonably easily. X.TP X\(bu \fBStat commands:\fP XA stat command goes at the beginning of an Xotherwise empty line, in the format: X.nf X X \fBstat\fP <# of dice> X.fi X X\fIrnd\fP will then roll the appropriate number of N-sided dice, and Xprint the stat on the standard output. XThe must not have any spaces in it. X XNormal output format is: X X.nf X \fB: \fP X.fi X Xhowever if you make the first character of the Xa '*' the format will be: X X.nf X \fB \fP X.fi X Xwithout the '*'. XThis is useful to produce messages like \fB300 gold_pieces\fP or X\fB12 large_diamonds\fP. X.TP X\(bu Subtable commands: a subtable command goes at the beginning of Xan otherwise empty line, in the format X.nf X X \fBsubtable\fP <# of dice> X X.fi X\fIrnd\fP will then roll the appropriate number of N-sided dice, and Xopen the subtable file. A subtable file is constructed as follows: X.nf X X <#upper limit> X <#upper limit> X ... etc. X.fi XWhere can be text, a stat command, a table command, Xor another subtable command. X XSubtable files may not contain any extraneous text (unlike table files) Xand all the entries must be in order, with the highest numerical entry as the Xlast line in the file, and the lowest numerical entry as the first. XEach line must contain a number value and a command, or the results Xmay be unusual. X.TP X\(bu \fBTable commands:\fP Xa table can contain a list of tables as well as Xstats, format commands, text, and subtables. Each table is randomized Xrecursively, until your stack overflows, or it returns, whichever comes Xfirst. Stacking tables deeply is usually not necessary, since several Xtables can be called on the command line, and that is much safer. XWhen a table command is invoked, it must be followed by two numbers Xso that the semi-braindead parser recognizes it as a command. X X.nf X \fBtable\fP X.fi X XEach table can contain its own list of subtables and stats, as well as text Xand format commands. This is lots of fun, since it allows the discerning XDM to make a variety of layered tables and gives excellent control Xover how much is generated. For example if you determine that an encounter Xconsists of monsters, booty, and terrain, you could have a table that Xinvoked the monster table, then the terrain one, then the booty. In Xthis way, you would still have a perfectly usable set of tables for Xany time you wanted just a monster, or just terrain. The possibilities Xas far as maintaining standard libraries are considerable, obviously. XThe primary limitation is your machine's stack and disk space. X.bp X.SH EXAMPLES X X$ \fBrnd Pirates\fR X X.I rnd Xwill read the table named X.B Pirates Xand executes all the stats, subtables, and tables contained therein. X X\fB(The contents of file "Pirates".)\fR X.nf X X-------------------------------------- XA sample pirate ----- Xstat strength 3 6 X\\t Xstat brains 3 6 X Xpossesions: Xsubtable possesions 1 100 X Xtable Anothertable 0 0 X X--------------------------------------- X\\l X X.fi X.ad XThis would generate a pirate with 3d6 strength and brains, and Xwould make a 1d100 roll on the possessions table. XThe table "Anothertable" is then opened and treated as a new list of Xcommands. Note that there are 2 numbers provided with the table command. XThis is MANDATORY, and due only to lazy programming. Live with it. XAppropriate tabs and Xa formfeed are generated. X X\fB(contents of file "possesions")\fP X X.nf X10 Cutlass X20 Fists X30 subtable beverage 1 100 X40 Belaying Pin X90 Just his clothes X100 table armor 0 0 X X.fi XIn this example, when the subtable was called, a 1d100 was rolled (be Xsure to specify that right!) 0-10 yields a Cutlass, 11-20 Fists, etc. X20-30 will roll on the subtable 'beverage' for a suitable liquid refreshment, XAnd 91-100 will recurse into the table 'armor' and print out what types Xof armor the pirate has. X.SH CAVEATS X.TP 2 X\(bu XThis program is machine dependent to the degree that your machine likes Xrecursion. X.TP X\(bu XThis program has run on Gould Powernode 9080, Sun workstations, and Xmy IBM compatible clone (lattice 3.0). On the PCs, usually some provision Xwill have to be made to increase the stack size. On lattice this is Xdone by declaring a global "int _stack = 6000;" or about 6 kilobytes. XYou decide. No guarantees are made for other compilers/machines, but Xanything that is not braindead can handle it. X.TP X\(bu XCapturing the output: This program is designed for systems with I/O Xredirection. If you don't have it, I suggest you frob the code slightly Xby changing all printfs to fprintf, etc. For you with I/O redirection, Xthe initial text, etc, is returned on the standard error, so it will Xnot kluge up your characters as they are generated. Pipe the output Xthrough your favorite formatter, editor, printer, or null device. X.SH AUTHOR X.nf X Marcus J Ranum, Gould Inc. X All rights reserved. X.fi X XDon't use this to make money, or sell. Otherwise it may be freely copied, Xdeleted, and hacked. XGive me credit for the idea, if not the code. END_OF_rnd.6 if test 6425 -ne `wc -c rnd.c <<'END_OF_rnd.c' X#include "stdio.h" X X/* rnd.c : X written by Marcus J Ranum. X All rights reserved. You may freely copy, modify, distribute, X and delete this program, but any use that will cause you to X gain money is prohibited unless we work out a deal, or you X ask me nicely. Any nifty ideas/modifications, please let me X know. X -mjr X X New Features added by: Dale Salyer X E-Mail Address: ihnp4!druhi!des X X CHG1) Default of # of iterations. X CHG2) Rolling of dice takes best of 3 tries. X CHG3) Nesting of subtables. X CHG4) Alternate format of stat command. X X*/ X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X int a; X char seed[100]; X char word[200]; X int iterations; X X /* If you can't do this, change back to old way */ X long t; X time(&t); X srand(getpid() + (int)((t>>16) + t)); X X /* CHG1) Default is 10 iterations if C/R pressed */ X X fprintf(stderr,"How many runs do you want[10]?"); X iterations=atoi(gets(word)); X if( iterations == 0 ) iterations = 10; X X while(iterations--) { X for(a=1 ; a < argc; a++) { X dotable(argv[a]); X } X } X} X X/* roll a sided die times [best of three tries] */ X Xroll(num,sides) Xint num; Xint sides; X{ X int a=0, m=0; X int i, j; X for(i=1,j=num;i<=3;i++) { X while (j-- >0) X a += rnd(sides); X /* CHG2) Rolling of dice takes best of 3 tries. */ X if(a > m) m = a; X } X return(m); X} X X/* roll a sided die once */ X Xrnd(die) Xregister die; X{ X return(((rand()>>3) % die)+1); X} X X/* reads a table. calls doparse for every line in the table. X note, if doparse hits another table name, this will X be recursively called. max recursions seems to be X around 10.... X*/ X Xdotable(table) Xchar table[]; X{ X FILE *file; X char junk[BUFSIZ]; X X if((file = fopen(table,"r")) ==NULL) { X fprintf(stderr,"can't open %s\n",table); X exit(1); X } X X while((fgets(junk,BUFSIZ-3,file)) !=NULL){ X doparse(junk); X } X if(fclose(file) == EOF) { X fprintf(stderr,"unusual file-close error!\n"); X exit(1); X } X} X X/* reads a subtable, and looks for a numbered entry that matches the X given selection criterion. is the table name, is X the number of dice, and is the number of sides on the X die. the subtables must be in the form of X<#> text entry (text string terminated with newline) X<#> table entry ... X<#> subtable entry ... X<#> stat entry ... X*/ X Xdosubtable(stat,num,die) Xchar stat[]; Xint num; Xint die; X{ X X FILE *tbl; X X char junk[BUFSIZ]; X char word[100]; X int old; X int new; X int pick; X int a; X X old=0; X if((tbl=fopen(stat,"r")) == NULL){ X fprintf(stderr,"can't open subtable %s\n",stat); X } else { X pick = roll(num,die); X X while((fgets(junk,BUFSIZ-3,tbl)) != NULL){ X if(sscanf(junk,"%d",&new)) { X if( pick > old && pick <= new) { X if (sscanf(junk,"%s",word)==0) { X fprintf(stderr, "invalid format\n"); X exit(1); X } X junk[strlen(junk)-1] = ' '; X X /* CHG3) Nesting of subtables. X * Skip over number, then call X * doparse() to re-parse the rest. X */ X for(a=0;junk[a] != ' ';a++); X a++; /* skip over leading space */ X doparse(&junk[a]); X break; X } X old = new; X } X } X } X if(fclose(tbl)== EOF) { X fprintf(stderr,"unusual file-close error\n"); X exit(1); X } X} X X/* prints a stat is statname, is number of dice, and X is how many sides the die has. */ X Xdostat(stat,num,die) Xchar stat[]; Xint num; Xint die; X{ X /* CHG4) Alternate format of stat command. */ X X if( stat[0] == '*' ) { X printf("%d %s", roll(num,die), &stat[1]); X } else { X printf("%s: %d",stat,roll(num,die)); X } X} X X/* doparse looks for the magic words "table" "subtable" "\t" or "\l" X and does the appropriate thing if it encounters them. if it X does not, it dumps it as text */ X Xdoparse(junk) Xchar junk[]; X{ X char stat[200]; X char word[200]; X int die; X int num; X X if( junk[0] == '\\') { X switch(junk[1]) { X case 'l': X putchar(12); X break; X case 't': X printf("\t"); X break; X } X } else { X if((sscanf(junk,"%s %s %d %d", X word,stat,&num,&die))!= 4) { X dotext(junk); X } else { X if(!strcmp(word,"subtable")) X dosubtable(stat,num,die); X else if(!strcmp(word,"stat")) X dostat(stat,num,die); X else if(!strcmp(word,"table")) X dotable(stat); X else X dotext(junk); X } X } X} X X/* dohack takes a string and dumps all of it except the FIRST word. X this is basically useful for printing subtable entries. X while there are more elegant ways of doing this in C, X my Lattice compiler dies when I try it. */ X Xdohack(string) Xchar string[BUFSIZ]; X{ X int a = 0; X int b = 0; X X while(b == 0) X if(string[a++] == ' ') X b++; X for(b = a; b < strlen(string); b++) X putchar(string[b]); X} X X/* dotext basically dumps a text string. X if the 2nd to last char (newline is last) is a ":" X it does NOT put a newline on. this is to allow you to X have items from subtables appear after a : */ X Xdotext(string) Xchar string[BUFSIZ]; X{ X int c; X X c = (strlen(string)-2); X if (string[c] == ':') X string[++c] = ' '; X printf("%s",string); X} END_OF_rnd.c if test 4930 -ne `wc -c Dungeon/CAT_ME <<'END_OF_Dungeon/CAT_ME' X In this subdirectory is a sample dungeon creator. The basic Xidea is that a dungeon is made up of Corridors, Doors, Rooms, and XCreatures that inhabit them. A corridor can have creatures, doors, Xand traps, whereas doors can have corridors or closets or rooms Xbehind them. A room can have some exits, a trap, and possibly a Xmonster or treasure. Creatures have attributes, possessions and Xweapons. Creatures sometimes come up with weird combinations of Xweapons, such as a floating eye using a long sword, if this occurs, Xconsider it part of the treasure instead. X X Note that I have adopted a convention that table files start Xwith a capital letter, and subtable files are lower-case. This helps Xto remember which can be used as input files. X XThe tables (input files): X X Corridor - The main table, this will create the next section of X a dungeon corridor. X X Creature - If you need another monster, use this table. X X Door - Use this to describe the doors in a room, to expand X the dungeon that way. X X Room - Use this to describe an extra room thrown in for good X measure. X X Try it - You'll like it. If you make any enhancements to this, Xplease let me know. I hope you enjoy this and take it as an Aid - Xnot the end-all of dungeon creation. Enjoy! X X Real Life?: Dale Salyer (Programmer of the Third Level) X Fantasy: Dark Star (Thief of the Fifth Level) X Coyote (Half-Coyote - Ranger of the Sixth Level) X X E-Mail Address: ...!ihnp4!druhi!des X U.S. Mail: AT&T Info. Systems Laboratories X Denver, CO 80229 X END_OF_Dungeon/CAT_ME if test 1552 -ne `wc -c Dungeon/Corridor <<'END_OF_Dungeon/Corridor' X+-------------------------+ X Xsubtable corr_sect 1 100 X Xsubtable corr_cont 1 100 X END_OF_Dungeon/Corridor if test 81 -ne `wc -c Dungeon/Creature <<'END_OF_Dungeon/Creature' X XA Monster ... X\t Xsubtable monster 1 100 X X\t XAttributes: X\t Xstat Strength 2 20 X\t Xstat Intelligence 3 6 X X\t X\t X\t Xstat Dexterity 3 6 X\t Xstat Constitution 3 12 X X\t XPossessions: X\t Xsubtable treasure 1 100 X X\t XWeapon: X\t Xsubtable weapon 1 100 X END_OF_Dungeon/Creature if test 241 -ne `wc -c Dungeon/Door <<'END_OF_Dungeon/Door' XA door ... X\t XIt is: Xsubtable trap 1 100 X X\t XBehind it will be found: Xsubtable behind_door 1 100 X END_OF_Dungeon/Door if test 98 -ne `wc -c Dungeon/Room <<'END_OF_Dungeon/Room' X XA Room ... X\t XIt's size is: X\t Xstat width 2 10 X\t Xstat length 3 10 X X\t XThe room is: Xsubtable trap 1 100 X X\t XThe room has: Xsubtable exits 1 100 X X\t XIt contains: Xsubtable room_cont 1 100 X END_OF_Dungeon/Room if test 187 -ne `wc -c Dungeon/behind_door <<'END_OF_Dungeon/behind_door' X65 table Room 0 0 X75 an empty closet X100 a corridor like the rest END_OF_Dungeon/behind_door if test 66 -ne `wc -c Dungeon/corr_cont <<'END_OF_Dungeon/corr_cont' X5 There is nothing here. X55 table Creature 0 0 X60 There is a set of stairs here. X63 subtable trap 1 100 X69 There is a trap door here. X100 table Door 0 0 END_OF_Dungeon/corr_cont if test 153 -ne `wc -c Dungeon/corr_sect <<'END_OF_Dungeon/corr_sect' X25 Corridor continues straight X29 Corridor continues straight, narrows X60 Corridor continues straight, widens X68 Corridor turns left X76 Corridor turns right X81 Corridor enters from left X86 Corridor enters from right X92 Corridor creates 'T' intersection X97 Corridor creates '+' intersection X100 Corridor Dead Ends END_OF_Dungeon/corr_sect if test 313 -ne `wc -c Dungeon/exits <<'END_OF_Dungeon/exits' X40 No other exits X51 A door on opposite wall X62 A door on left wall X73 A door on right wall X82 A door on two other walls X100 A door on each wall END_OF_Dungeon/exits if test 145 -ne `wc -c Dungeon/jewel <<'END_OF_Dungeon/jewel' X20 stat *emeralds 3 20 X35 stat *rubies 2 40 X50 A diamond necklace X65 A pair of jeweled bracers X80 stat *diamonds 1 12 X100 A few worthless rocks END_OF_Dungeon/jewel if test 144 -ne `wc -c Dungeon/monster <<'END_OF_Dungeon/monster' X15 Giant Spider X20 Hill Giant X25 Orc X28 Earth Elemental X37 subtable undead 1 100 X43 Werewolf X48 Gremlin X50 Centaur X58 Dragon X63 Goblin X68 Salamander X75 Griffon X83 Shadow X92 Floating Eye X100 Giant Bear END_OF_Dungeon/monster if test 201 -ne `wc -c Dungeon/room_cont <<'END_OF_Dungeon/room_cont' X30 nothing X65 table Creature 0 0 X100 subtable treasure 1 100 END_OF_Dungeon/room_cont if test 61 -ne `wc -c Dungeon/trap <<'END_OF_Dungeon/trap' X24 needle trapped X45 poisonous gas trapped X53 sleep gas trapped X60 crossbow trapped X75 electrically trapped X100 not trapped END_OF_Dungeon/trap if test 124 -ne `wc -c Dungeon/treasure <<'END_OF_Dungeon/treasure' X35 stat *gold_pieces 3 30 X65 subtable jewel 1 100 X90 subtable weapon 1 100 X100 Nothing END_OF_Dungeon/treasure if test 87 -ne `wc -c Dungeon/undead <<'END_OF_Dungeon/undead' X23 Skeleton X37 Ghoul X50 Mind Flayer X60 Generic Undead X70 Ghost X80 Wraith X90 Zombie X100 Vampire END_OF_Dungeon/undead if test 95 -ne `wc -c Dungeon/weapon <<'END_OF_Dungeon/weapon' X15 Broad Sword X25 Long Sword X37 Two-handed Sword X50 Battle Axe X70 Quarter Staff X85 Spear X100 Claws/Bare Hands END_OF_Dungeon/weapon if test 110 -ne `wc -c