[net.sources] Random Stuff Generator

mjranum@gouldsd.UUCP (Captain Coredump) (10/28/86)

	Here it is, since I am tired of sending individual copies around.
Take it or leave it....   So far it runs on BSD UNIX and on IBM PCs under
Lattice 3.0 (with stack modifications).

Live Free
mjr


the following files are in this bundle: (Use them wisely)
Makefile README food nationality pirate possesions rnd.c rnd.nro skill weapon
cut here --------------------------------------------------
# To unbundle - sh this file
echo unbundling Makefile
if [ -f Makefile ] ; then
	echo cannot unbundle Makefile - file exists already
	exit 1
fi
cat >Makefile <<'End of Makefile'
CFLAGS=	-O
CC=	/bin/cc

STD=	rnd 
DOC=	README

all:	${STD} ${DOC} 

${STD}: rnd.c
	${CC} ${CFLAGS} -o $@ $@.c

${DOC}: rnd.nro
	nroff -man rnd.nro >> README
End of Makefile
echo unbundling README
if [ -f README ] ; then
	echo cannot unbundle README - file exists already
	exit 1
fi
cat >README <<'End of README'



rrrrnnnndddd((((1111))))                                                     rrrrnnnndddd((((1111))))



rrrrnnnndddd ---- RRRRaaaannnnddddoooommmm ssssttttuuuuffffffff ggggeeeennnneeeerrrraaaattttoooorrrr ((((MMMMJJJJRRRR))))




SSSSYYYYNNNNTTTTAAAAXXXX

     rrrrnnnndddd file1 file2, etc...



OOOOPPPPTTTTIIIIOOOONNNNSSSS

   Each file must be a 'table' file.


DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN

   _R_n_d reads a file and writes generated  data  to  the  standard
   output.   If  more  than  one file is specified, the files are
   used as descriptions in sequence and merged output is  written
   to  the  standard  output.  Two types of files are recognized;
   table files, and subtable files.  A  table  file  can  contain
   several types of information:

     o+ Text-formats and text: these are simply text that will be
             included in the output. There are,  however,  a  few
             simple  rules.   Any text string ending in a ':' (no
             space afterwards) will NOT have a  newline  appended
             after it. This is to allow a user to have the output
             from a subtable placed after a text  string.   Other
             text  that does not have a ':' as the last character
             before a newline is printed as is.

     o+ Tabs and Formfeeds: to insert tabs and formfeeds, a "\t" (tab)
             or  a "\l" (formfeed ^L) is placed as the ONLY thing
             on that line. There must be no other  blank  spaces,
             etc.  This  is  inelegant, but allows the program to
             run reasonably fast. If a tab is coded, the  program
             does  NOT automatically generate a newline after it.
             This is to allow a user to separate stats with  tabs
             reasonably easily.

     o+ Stat commands: a stat command goes at the beginning of an
             otherwise empty line, in the format
             "stat <char string WITHOUT spaces> <# of dice> <type of dice>"
             Rnd will then roll the appropriate number of N-sided
             dice, and print the stat on the standard output.


     o+ Subtable commands: a subtable command goes at the beginning of
             an otherwise empty line, in the format

UTX/32 User's
Reference Manual               -1-                       Commands







rrrrnnnndddd((((1111))))                                                     rrrrnnnndddd((((1111))))



             "subtable <filename> <# of dice> <type of dice>"
             Rnd will then roll the appropriate number of N-sided
             dice, and open the subtable file. A subtable file is
             constructed as follows:
             <#upper limit> <text string>
             <#upper limit> <text string>
             Subtable files may not contain any  extraneous  text
             (unlike  table files) and all the entries must be in
             order, with the highest numerical entry as the  last
             line  in the file, and the lowest numerical entry as
             the first.  Each line must contain  a  number  value
             and a text string, or the results may be unusual.

     o+ Table commands: a table can contain a list of tables as well as
             stats,  format  commands,  text, and subtables. Each
             table is randomized recursively,  until  your  stack
             overflows,  or  it  returns,  whichever comes first.
             Stacking tables deeply  is  usually  not  necessary,
             since  several  tables  can be called on the command
             line, and that is much safer.  When a table  command
             is  invoked,  it  must be followed by two numbers so
             that the semi-braindead parser recognizes  it  as  a
             command.
             table <filename> <bogus #><bogus #>
             Each table can contain its own list of subtables and
             stats,  as well as text and format commands. This is
             lots of fun, since it allows the  discerning  DM  to
             make a variety of layered tables and gives excellent
             control over how much is generated. For  example  if
             you   determine   that   an  encounter  consists  of
             monsters, booty, and terrain, you could have a table
             that  invoked  the  monster  table, then the terrain
             one, then the booty. In this way,  you  would  still
             have  a  perfectly usable set of tables for any time
             you wanted just a  monster,  or  just  terrain.  The
             possibilities   as   far   as  maintaining  standard
             libraries are considerable, obviously.  The  primary
             limitation is your machine's stack and disk space.



EEEEXXXXAAAAMMMMPPPPLLLLEEEESSSS

     $ rrrrnnnndddd ppppiiiirrrraaaatttteeeessss

   rrrrnnnndddd reads the table named ppppiiiirrrraaaatttteeeessss and executes all the  stats,
   subtables, and tables contained therein.






UTX/32 User's
Reference Manual               -2-                       Commands







rrrrnnnndddd((((1111))))                                                     rrrrnnnndddd((((1111))))



     ((((TTTThhhheeee ccccoooonnnntttteeeennnnttttssss ooooffff ffffiiiilllleeee """"ppppiiiirrrraaaatttteeeessss""""....))))

   --------------------------------------
   A sample pirate -----
   stat strength 3 6
   \t
   stat brains 3 6

   possesions:
   subtable possesions 1 100

   table anothertable 0 0

   ---------------------------------------
   \l

        This would  generate  a  pirate  with  3d6  strength  and
   brains,  and would make a 1d100 roll on the possessions table.
   The table "anothertable" is then opened and treated as  a  new
   list  of commands. Note that there are 2 numbers provided with
   the table command.  This is MANDATORY, and due  only  to  lazy
   programming.  Live  with  it.  Appropriate tabs and a formfeed
   are generated.


     ((((ccccoooonnnntttteeeennnnttttssss ooooffff ffffiiiilllleeee """"ppppoooosssssssseeeessssiiiioooonnnnssss""""))))

   10 Cutlass
   20 Fists
   30 Bucket of Grog
   40 Belaying Pin
   90 Just his clothes
   100 Fancy clothes


   In this example, when the subtable was  called,  a  1d100  was
   rolled (be sure to specify that right!) 0-10 yields a Cutlass,
   11-20 Fists, etc.



SSSSPPPPEEEECCCCIIIIAAAALLLL CCCCOOOONNNNSSSSIIIIDDDDEEEERRRRAAAATTTTIIIIOOOONNNNSSSS

     o+ This program is machine dependent to the degree that  your
       machine likes recursion.

     o+ A random seed is requested for portability's  sake.  Those
       of you on UNIX systems are urged to use something like:
       srand((unsigned)getpid());
       instead. Any random stirrer is appropriate.



UTX/32 User's
Reference Manual               -3-                       Commands







rrrrnnnndddd((((1111))))                                                     rrrrnnnndddd((((1111))))



     o+ This  program  has  run  on  Gould  Powernode  9080,   Sun
       workstations,  and  my IBM compatible clone (lattice 3.0).
       On the PCs, usually some provision will have to be made to
       increase  the  stack  size.  On  lattice  this  is done by
       declaring a  global  "int  _stack  =  6000;"  or  about  6
       kilobytes.   You  decide. No guarantees are made for other
       compilers/machines, but anything that is not braindead can
       handle it.

     o+ Capturing  the  output::  This  program  is  designed  for
       systems  with  I/O  redirection.  If  you don't have it, I
       suggest you frob the code slightly by changing all printfs
       to fprintf, etc. For you with I/O redirection, the initial
       text, etc, is returned on the standard error, so  it  will
       not  kluge  up your characters as they are generated. Pipe
       the  output  through  your  favorite  formatter,   editor,
       printer, or null device.



SSSSEEEEEEEE AAAALLLLSSSSOOOO

   no references

AAAAuuuutttthhhhoooorrrr:::: Marcus J Ranum, Gould Inc. All rights reserved. Don't use
this  to  make money, or sell. Otherwise it may be freely copied,
deleted, and hacked.  Give me credit for the  idea,  if  not  the
code.

























UTX/32 User's
Reference Manual               -4-                       Commands




End of README
echo unbundling food
if [ -f food ] ; then
	echo cannot unbundle food - file exists already
	exit 1
fi
cat >food <<'End of food'
10 Sushi
20 Salad
30 Wiskey
40 Sandwitches
50 Port
60 Scotch
70 Vodka
80 Caviar
90 Gum
100 Beer
End of food
echo unbundling nationality
if [ -f nationality ] ; then
	echo cannot unbundle nationality - file exists already
	exit 1
fi
cat >nationality <<'End of nationality'
10 Spanish
20 French
30 English
40 Japanese
50 Dutch
60 Portugese
70 Italian
80 Russian
90 German
100 Other
End of nationality
echo unbundling pirate
if [ -f pirate ] ; then
	echo cannot unbundle pirate - file exists already
	exit 1
fi
cat >pirate <<'End of pirate'

--------------------------------------------------------------
Sample Pirate
-------------
stat strength 3 6
\t
\t
stat intelligence 3 6
\t
\t
stat seamanship 3 6

stat swordplay 3 6
\t
\t
stat looks 3 6
\t
\t
stat education 1 10

nationality:
subtable nationality 1 100

Possessions:
table possesions 0 0
End of pirate
echo unbundling possesions
if [ -f possesions ] ; then
	echo cannot unbundle possesions - file exists already
	exit 1
fi
cat >possesions <<'End of possesions'

Weapons:
subtable weapon 1 100

Food:
subtable food 1 100
subtable food 1 100

stat cash 3 30

Skills:
subtable skill 1 100
\t
subtable skill 1 100
\t
subtable skill 1 100

End of possesions
echo unbundling rnd.c
if [ -f rnd.c ] ; then
	echo cannot unbundle rnd.c - file exists already
	exit 1
fi
cat >rnd.c <<'End of rnd.c'
#include "stdio.h"

/* rnd.c :
	written by Marcus J Ranum.
	All rights reserved. You may freely copy, modify, distribute,
	and delete this program, but any use that will cause you to 
	gain money is prohibited unless we work out a deal, or you
	ask me nicely. Any nifty ideas/modifications, please let me
	know.
		-mjr
*/

main(argc,argv)
int argc;
char *argv[];
{
	int a;
	char seed[100];
	char word[200];
	int	iterations;

	fprintf(stderr,"Please enter a random # and press <return>: ");
	srand((unsigned)atoi(gets(seed)));

	fprintf(stderr,"How many runs do you want and press <return>: ");
	iterations=atoi(gets(word));

	while(iterations--) {
		for(a=1 ; a < argc; a++) {
			dotable(argv[a]);
		}
	}
}

/* roll a <sides> sided die <num> times */

roll(num,sides)
int num;
int sides;
{
	int a=0;
	while (num-- >0)
		a += rnd(sides);
	return(a);
}

/* roll a <die> sided die once */

rnd(die)
register die;
{
	return(((rand()>>3) % die)+1);
}

/* reads a table. calls doparse for every line in the table.
	note, if doparse hits another table name, this will
	be recursively called. max recursions seems to be
	around 10....
*/

dotable(table)
char table[];
{
	FILE *file;
	char junk[BUFSIZ];

	if((file = fopen(table,"r")) ==NULL) {
		fprintf(stderr,"can't open %s\n",table);
		exit(1);
	}

	while((fgets(junk,BUFSIZ-3,file)) !=NULL){
		doparse(junk);
	}
	if(fclose(file) == EOF) {
		fprintf(stderr,"unusual file-close error!\n");
		exit(1);
	}
}

/* reads a subtable, and looks for a numbered entry that matches the
	given selection criterion. <stat> is the table name, <num> is
	the number of dice, and <die> is the number of sides on the
	die. the subtables must be in the form of 
<#> entry (text string terminated with newline)
*/

dosubtable(stat,num,die)
char stat[];
int	num;
int	die;
{

	FILE *tbl;

	char junk[BUFSIZ];
	char word[100];
	int old;
	int new;
	int pick;

	old=0;
	if((tbl=fopen(stat,"r")) == NULL){
		fprintf(stderr,"can't open subtable %s\n",stat);
	} else {
		pick = roll(num,die);

		while((fgets(junk,BUFSIZ-3,tbl)) != NULL){
			if(sscanf(junk,"%d",&new)) {
				if( pick > old &&  pick <= new) {
					if (sscanf(junk,"%s",word)==0) {
						fprintf(stderr, "invalid format\n");
						exit(1);
					}
					junk[strlen(junk)-1] = ' ';
					dohack(junk);
					break;
				}
				old = new;
			}
		}
	}
	if(fclose(tbl)== EOF) {
		fprintf(stderr,"unusual file-close error\n");
		exit(1);
	}
}

/* prints a stat <stat> is statname, <num> is number of dice, and 
	<die> is how many sides the die has. */

dostat(stat,num,die)
char stat[];
int	num;
int	die;
{
	printf("%s:_______ \b\b\b\b\b\b\b%d",stat,roll(num,die));
}

/* doparse looks for the magic words "table" "subtable" "\t" or "\l"
	and does the appropriate thing if it encounters them. if it
	does not, it dumps it as text */

doparse(junk)
char junk[];
{
	char stat[200];
	char word[200];
	int die;
	int num;

	if( junk[0] == '\\') {
		switch(junk[1]) {
		case 'l':
			putchar(12);
			break;
		case 't':
			printf("\t");
			break;
		}
	} else {
		if((sscanf(junk,"%s %s %d %d",
		    word,stat,&num,&die))!= 4) {
			dotext(junk);
		} else {
			if(!strcmp(word,"subtable"))
				dosubtable(stat,num,die);
			else if(!strcmp(word,"stat"))
				dostat(stat,num,die);
			else if(!strcmp(word,"table"))
				dotable(stat);
			else
				dotext(junk);
		}
	}
}

/* dohack takes a string and dumps all of it except the FIRST word.
	this is basically useful for printing subtable entries.
	while there are more elegant ways of doing this in C,
	my Lattice compiler dies when I try it. */

dohack(string)
char string[BUFSIZ];
{
	int a = 0;
	int b = 0;

	while(b == 0)
		if(string[a++] == ' ')
			b++;
	for(b = a; b < strlen(string); b++)
		putchar(string[b]);
}

/* dotext basically dumps a text string.
	if the 2nd to last char (newline is last) is a ":" 
	it does NOT put a newline on. this is to allow you to 
	have items from subtables appear after a :	*/

dotext(string)
char string[BUFSIZ];
{
	int c;
	
	c = (strlen(string)-2);
	if (string[c] == ':')
		string[++c] = ' ';
	printf("%s",string);
}
End of rnd.c
echo unbundling rnd.nro
if [ -f rnd.nro ] ; then
	echo cannot unbundle rnd.nro - file exists already
	exit 1
fi
cat >rnd.nro <<'End of rnd.nro'
.EV
.T1 rnd 1  2/14/84
.SH "rnd \- Random stuff generator" (MJR)
.SY
.B rnd
file1 file2, etc...
.OP
Each file must be a 'table' file.
.DS
.I Rnd 
reads a file and writes generated data to the standard output.
If more than one file is specified, the files are used as descriptions
in sequence and merged output is written to the standard output.
Two types of files are recognized; table files, and subtable files.
A table file can contain several types of information:
.TP 8
\(bu Text-formats and text: these are simply text that will be
included in the output. There are, however, a few simple rules.
Any text string ending in a ':' (no space afterwards) will NOT
have a newline appended after it. This is to allow a user to
have the output from a subtable placed after a text string.
Other text that does not have a ':' as the last character before
a newline is printed as is.
.TP
\(bu Tabs and Formfeeds: to insert tabs and formfeeds, a "\\t" (tab)
or a "\\l" (formfeed ^L) is placed as the ONLY thing on that line. 
There must be no other blank spaces, etc. This is inelegant, but
allows the program to run reasonably fast. If a tab is coded, the
program does NOT automatically generate a newline after it. This
is to allow a user to separate stats with tabs reasonably easily.
.TP
\(bu Stat commands: a stat command goes at the beginning of an 
otherwise empty line, in the format 
.nf
.na
"stat <char string WITHOUT spaces> <# of dice> <type of dice>"
.fi
.ad
Rnd will then roll the appropriate number of N-sided dice, and
print the stat on the standard output.

.TP
\(bu Subtable commands: a subtable command goes at the beginning of
an otherwise empty line, in the format
.nf
.na
"subtable <filename> <# of dice> <type of dice>"
.fi
.ad
Rnd will then roll the appropriate number of N-sided dice, and
open the subtable file. A subtable file is constructed as follows:
.nf
.na
<#upper limit> <text string>
<#upper limit> <text string>
... etc.
.fi
.ad
Subtable files may not contain any extraneous text (unlike table files)
and all the entries must be in order, with the highest numerical entry as the
last line in the file, and the lowest numerical entry as the first.
Each line must contain a number value and a text string, or the results
may be unusual. 
.TP
\(bu Table commands: a table can contain a list of tables as well as
stats, format commands, text, and subtables. Each table is randomized
recursively, until your stack overflows, or it returns, whichever comes
first. Stacking tables deeply is usually not necessary, since several
tables can be called on the command line, and that is much safer.
When a table command is invoked, it must be followed by two numbers
so that the semi-braindead parser recognizes it as a command.
.nf
.na
table <filename> <bogus #><bogus #>
.fi
.ad
Each table can contain its own list of subtables and stats, as well as text
and format commands. This is lots of fun, since it allows the discerning
DM to make a variety of layered tables and gives excellent control
over how much is generated. For example if you determine that an encounter
consists of monsters, booty, and terrain, you could have a table that
invoked the monster table, then the terrain one, then the booty. In 
this way, you would still have a perfectly usable set of tables for
any time you wanted just a monster, or just terrain. The possibilities
as far as maintaining standard libraries are considerable, obviously.
The primary limitation is your machine's stack and disk space.
.EX
$ \fBrnd pirates\fR
.EE
.B rnd 
reads the table named
.B pirates
and executes all the stats, subtables, and tables contained therein.
.E2
.sp
.sp
.sp
.sp
\fB(The contents of file "pirates".)\fR
.EE
.nf
.na
--------------------------------------
A sample pirate -----
stat strength 3 6
\\t
stat brains 3 6

possesions:
subtable possesions 1 100

table anothertable 0 0

---------------------------------------
\\l

.fi
.ad
	This would generate a pirate with 3d6 strength and brains, and
would make a 1d100 roll on the possessions table. 
The table "anothertable" is then opened and treated as a new list of
commands. Note that there are 2 numbers provided with the table command.
This is MANDATORY, and due only to lazy programming. Live with it.
Appropriate tabs and
a formfeed are generated. 
.E2
\fB(contents of file "possesions")\fP
.EE
.nf
.na
10 Cutlass
20 Fists
30 Bucket of Grog
40 Belaying Pin
90 Just his clothes
100 Fancy clothes


.fi
.ad
In this example, when the subtable was called, a 1d100 was rolled (be
sure to specify that right!) 0-10 yields a Cutlass, 11-20 Fists, etc.

.SC
.TP 2
\(bu
This program is machine dependent to the degree that your machine likes
recursion.
.TP
\(bu
A random seed is requested for portability's sake. Those of you on
UNIX systems are urged to use something like:
.nf
.na
srand((unsigned)getpid());
.fi
.ad
instead. Any random stirrer is appropriate.
.TP
\(bu
This program has run on Gould Powernode 9080, Sun workstations, and
my IBM compatible clone (lattice 3.0). On the PCs, usually some provision
will have to be made to increase the stack size. On lattice this is
done by declaring a global "int _stack = 6000;" or about 6 kilobytes.
You decide. No guarantees are made for other compilers/machines, but
anything that is not braindead can handle it. 
.TP
\(bu
Capturing the output:: This program is designed for systems with I/O
redirection. If you don't have it, I suggest you frob the code slightly
by changing all printfs to fprintf, etc. For you with I/O redirection,
the initial text, etc, is returned on the standard error, so it will
not kluge up your characters as they are generated. Pipe the output
through your favorite formatter, editor, printer, or null device.
.IN
no references
.I0
Author:
Marcus J Ranum, Gould Inc. All rights reserved. Don't use this to make
money, or sell. Otherwise it may be freely copied, deleted, and hacked.
Give me credit for the idea, if not the code.
End of rnd.nro
echo unbundling skill
if [ -f skill ] ; then
	echo cannot unbundle skill - file exists already
	exit 1
fi
cat >skill <<'End of skill'
5 pickpocketing
10 torture
30 basic seamanship
35 zymurgy
40 gunnery
45 rope-tying
50 liguistics
55 reading/writing
60 bookeeping
65 farming
70 gunsmithing
72 metalsmithing
80 woodworking
85 whaling
90 shipbuilding
95 navigation
97 storytelling/music
100 cartography
End of skill
echo unbundling weapon
if [ -f weapon ] ; then
	echo cannot unbundle weapon - file exists already
	exit 1
fi
cat >weapon <<'End of weapon'
10 Bad Breath
20 Musket
30 Cutlass
40 Belaying Pin.
50 Cane
60 Bottle
100 Fists
End of weapon