[alt.sources] townmaze part 02/04

xanthian@zorch.SF-Bay.ORG (Kent Paul Dolan) (04/18/91)

Archive-name: townmaze/part02

#! /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 <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 4)."
# Contents:  connectst.c getargs.c makeunused.c townmaze.h townmaze.lmk
#   townproto.h townuser.doc
# Wrapped by xanthian@zorch on Wed Apr 17 20:34:40 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'connectst.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'connectst.c'\"
else
echo shar: Extracting \"'connectst.c'\" \(4740 characters\)
sed "s/^X//" >'connectst.c' <<'END_OF_FILE'
X/*
X** connectst.c  Copyright 1991 Kent Paul Dolan,
X**              Mountain View, CA, USA 94039-0755
X**
X** Written to satisfy an inquiry on USENet's rec.games.programmer newsgroup.
X** May be freely used or modified in any non-commercial work.  Copyrighted
X** only to prevent patenting by someone else.
X*/
X
X#include <stdio.h>
X#include "townmaze.h"
X#include "townproto.h"
X
X#ifdef __STDC__
Xvoid connectstreets()
X#else
Xint connectstreets()
X#endif
X{
X  int targetcell;
X  int livewalk, i;
X  int deadwalk, lastdead, j, k;
X  int cheapdir;
X  int nhbrid;
X
X/*
X** Sanity check; make sure we don't go spinning off with no work possible.
X*/
X
X  if (streetnumct < 1)
X  {
X    fprintf(stderr,"No streets seeded! Oops!  Logic bomb!\n");
X    freespace();
X    exit(1);
X  }
X
X/*
X** Three phase maneuver; phase 1, try to connect streets by using live
X** cells to turn into street cells.
X*/
X
X#if PROGRESS
X  fprintf(stderr,"Streets 1 ");
X#endif
X
X
X  while((streetnumct > 1) && (livect > 0))
X  {
X    targetcell = RANDOM()%livect;
X    livewalk = live;
X    for (i = 0; i < (targetcell - 1); i++)
X      if (livewalk == NOPOINTER)
X      {
X        fprintf(stderr,"live list too short in connectstreets\n");
X        showdebugmaze();
X        freespace();
X        exit(1);
X      }
X      else
X      {
X        livewalk = statlist[livewalk].next;
X      }
X/*
X** Since this is a live cell, it is supposed to have a street cell
X** neighbor; find one and adopt its street number.  If directions
X** are implemented, then this has to be expanded to pick a random
X** neighbor street fairly and adopt its number and direction.
X** [Done in makestreet(), instead, by the "-1" direction parameter..]
X*/
X    for (nhbrid = 0; nhbrid < 4; nhbrid++)
X      if (nhbrexists(livewalk,nhbrid))
X        if (statlist[nhbris(livewalk,nhbrid)].status == STREET)
X        {
X          makestreet(livewalk,
X            statlist[nhbris(livewalk,nhbrid)].streetnum,-1);
X          break;
X        }
X  }
X
X/*
X** Phase 2: try for a cheap connect by only using dead cells between streets
X** with different streetnums; this should get some more streets connected,
X** without turning the whole map into streets.
X*/
X
X  if ((streetnumct > 1) && (deadct > 0))
X  {
X
X#if PROGRESS
X  fprintf(stderr,"Streets 2 ");
X#endif
X
X    deadwalk = dead;
X    for (i = 0; i < deadct; i++)
X    {
X      if (deadwalk == NOPOINTER) break;
X      if (streetnumct <= 1) break;
X      lastdead = deadwalk;
X      deadwalk = statlist[deadwalk].next;
X      if (interiorcell(lastdead))
X      {
X        for (j = 0; j < 4; j++)
X          if (nhbrexists(lastdead,j))
X            for (k = 0; k < 4; k++)
X            {
X              if (j == k) continue;
X              if (nhbrexists(lastdead,k))
X/*
X** The reason this loop doesn't keep trying to make a street out of the
X** chosen cell after it has done so once is that makestreet() causes
X** all the adjacent streets to be merged and have the same street number,
X** so that the next if always fails after the first success.  It can thus
X** safely be let run to completion.
X*/
X                if (   (statlist[nhbris(lastdead,j)].status == STREET)
X                    && (statlist[nhbris(lastdead,k)].status == STREET)
X                    && (statlist[nhbris(lastdead,j)].streetnum
X                        != statlist[nhbris(lastdead,k)].streetnum)
X                   )
X                {
X                  cheapdir = ((j+2)%4);
X                  if (statlist[nhbris(lastdead,k)].streetdir == ((k+2)%4))
X                    cheapdir = ((k+2)%4); 
X                  makestreet(lastdead,statlist[nhbris(lastdead,j)].streetnum,
X                             cheapdir);
X                }
X            }
X      }
X      
X    }
X  }
X
X/*
X** Phase 3: when out of luck, use brute force (haven't ever seen this
X** required, but can't prove it isn't)..
X*/
X
X#if PROGRESS
X  if (streetnumct > 1) fprintf(stderr,"Streets 3 ");
X#endif
X
X  while((streetnumct > 1) && (deadct > 0))
X  {
X    targetcell = RANDOM()%deadct;
X    deadwalk = dead;
X    for (i = 0; i < (targetcell - 1); i++)
X      if (deadwalk == NOPOINTER)
X      {
X        fprintf(stderr,"dead list too short in connectstreets\n");
X        showdebugmaze();
X        freespace();
X        exit(1);
X      }
X      else
X      {
X        deadwalk = statlist[deadwalk].next;
X      }
X/*
X** Since this is a dead cell, it is necessary to check that it is an interior
X** one.  This should again be enhanced when directions are installed.
X*/
X    if (interiorcell(deadwalk))
X    {
X      for (nhbrid = 0; nhbrid < 4; nhbrid++)
X        if (nhbrexists(deadwalk,nhbrid))
X          if (statlist[nhbris(deadwalk,nhbrid)].status == STREET)
X          {
X            makestreet(deadwalk,
X              statlist[nhbris(deadwalk,nhbrid)].streetnum,-1);
X            break;
X          }
X    }
X  }
X
X}
END_OF_FILE
if test 4740 -ne `wc -c <'connectst.c'`; then
    echo shar: \"'connectst.c'\" unpacked with wrong size!
fi
# end of 'connectst.c'
fi
if test -f 'getargs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getargs.c'\"
else
echo shar: Extracting \"'getargs.c'\" \(2964 characters\)
sed "s/^X//" >'getargs.c' <<'END_OF_FILE'
X/*
X** getargs.c  Copyright 1991 Kent Paul Dolan,
X**            Mountain View, CA, USA 94039-0755
X**
X** Written to satisfy an inquiry on USENet's rec.games.programmer newsgroup.
X** May be freely used or modified in any non-commercial work.  Copyrighted
X** only to prevent patenting by someone else.
X*/
X
X
X#include <stdio.h>
X#include "townmaze.h"
X#include "townproto.h"
X
X
X#ifdef __STDC__
Xvoid getargs(int argc,char *argv[])
X#else
Xint getargs(argc,argv)
X  int argc;
X  char *argv[];
X#endif
X{
X  int i;
X
X#if PROGRESS
X  fprintf(stderr,"Get arguments ");
X#endif
X
X  if (argc > 1)
X  {
X    if ((argc%2) != 1)
X    {
X      usage();
X      exit(1);
X    }
X    i = 1;
X    while (i < argc)
X    {
X      if ((*argv[i]) != '-')
X      {
X        usage();
X        exit(1);
X      }
X      switch (*(argv[i]+1))
X      {
X      case 'h':
X        if (sscanf(argv[i+1],"%d",&mazeheight) == EOF)
X        {
X          usage();
X          exit(1);
X        }
X        break;
X
X      case 'w':
X        if (sscanf(argv[i+1],"%d",&mazewidth) == EOF)
X        {
X          usage();
X          exit(1);
X        }
X        break;
X
X      case 'r':
X        if (sscanf(argv[i+1],"%ld",&randomseed) == EOF)
X        {
X          usage();
X          exit(1);
X        }
X        SEEDRANDOM(randomseed); /* override clock seed in main() */
X        break;
X
X      case 'g':
X        if (sscanf(argv[i+1],"%d",&mazegates) == EOF)
X        {
X          usage();
X          exit(1);
X        }
X        break;
X
X      case 'l':
X        if (sscanf(argv[i+1],"%d",&leftgates) == EOF)
X        {
X          usage();
X          exit(1);
X        }
X        break;
X
X      case 'c':
X        if (sscanf(argv[i+1],"%d",&mazecourts) == EOF)
X        {
X          usage();
X          exit(1);
X        }
X        break;
X
X      case 'u':
X        if (sscanf(argv[i+1],"%d",&mazeunused) == EOF)
X        {
X          usage();
X          exit(1);
X        }
X        break;
X
X      case 's':
X        if (sscanf(argv[i+1],"%d",&mazestraightness) == EOF)
X        {
X          usage();
X          exit(1);
X        }
X        break;
X
X      default:
X        usage();
X        exit(1);
X      }
X      i += 2;
X    }
X  }
X  if (   ((mazewidth%2) != 1)
X      || ((mazeheight%2) != 1)
X      || (mazewidth < 11)
X      || (mazeheight < 11)
X      || (mazegates < 0)
X      || (mazegates > (2*((mazeheight - 6)/7) + 2*((mazewidth- 6)/7)))
X      || (leftgates < 0)
X      || (leftgates > mazegates)
X      || (mazecourts < 0)
X      || (mazecourts > (((mazeheight - 5)/6)*((mazewidth - 5)/6)))
X      || ((mazecourts + mazegates) < 1)
X      || (mazeunused > (((mazeheight - 1)/14)*((mazewidth - 1)/14)))
X      || (mazestraightness < 0)
X      || (mazestraightness > 998)
X     )
X  {
X    usage();
X    exit(1);
X  }
X  listsize = ((mazeheight - 1)/2)*((mazewidth - 1)/2);
X
X#if HEADER
X  fprintf(stdout,
X   "ht %d wd %d gates %d left %d courts %d unused %d straight %d seed %ld\n",
X   mazeheight,mazewidth,mazegates,leftgates,mazecourts,mazeunused,
X   mazestraightness,randomseed);
X#endif
X  return;
X}
END_OF_FILE
if test 2964 -ne `wc -c <'getargs.c'`; then
    echo shar: \"'getargs.c'\" unpacked with wrong size!
fi
# end of 'getargs.c'
fi
if test -f 'makeunused.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makeunused.c'\"
else
echo shar: Extracting \"'makeunused.c'\" \(8699 characters\)
sed "s/^X//" >'makeunused.c' <<'END_OF_FILE'
X/*
X** makeunused.c  Copyright 1991 Kent Paul Dolan,
X**               Mountain View, CA, USA 94039-0755
X**
X** Written to satisfy an inquiry on USENet's rec.games.programmer newsgroup.
X** May be freely used or modified in any non-commercial work.  Copyrighted
X** only to prevent patenting by someone else.
X*/
X
X#include <stdio.h>
X#include "townmaze.h"
X#include "townproto.h"
X
X#ifdef __STDC__
Xvoid makeunused()
X#else
Xint makeunused()
X#endif
X{
X
X  int totalcells;
X  int chosencell;
X  int tries;
X  int i;
X  int mazei, mazej;
X/*
X** Pepper unused cells around the city interior; keep them apart for
X** algorithmic robustness and connectivity reasons.
X*/
X
X#if PROGRESS
X  fprintf(stderr,"Unused ");
X#endif
X
X  totalcells = ((mazeheight-1)/2 * (mazewidth-1)/2);
X
X  for (i = 0; i < mazeunused; i++)
X  {
X
X/*  fprintf(stderr,"Unused %d\n",i); */
X
X/*
X** Set up to prevent infinite loop from unforseen geometry problems.
X*/
X
X   tries = 0;
X
X/*
X** Keep looking until a candidate cell is found for this ith unused cell.
X*/
X    do
X    {
X      /* not perfectly fair, but good enough for moderate sized mazes. */
X
X      chosencell = RANDOM()%totalcells;
X
X/*    fprintf(stderr,"  chosencell %d\n",chosencell); */
X
X      tries++;
X
X    } while
X      (   (tries <= MAXTRIES)
X       && (   /* To avoid locking in an isolated room, check all 49 cells */
X              /* centered on this cell, the hard way: no loop, no direct  */
X              /* index to the cells. Goal is to be able to run a street  */
X              /* on all four sides of the 3x3 cell array centered at this */
X              /* cell. */
X
X              /* First verify cell and immediate neighbors two out all */
X              /* have all their neighbors; if not, we're too close to a */
X              /* border. */
X
X              (interiorcell(chosencell) != (1==1))
X
X           || (interiorcell(nhbris(chosencell,0)) != (1==1))
X           || (interiorcell(nhbris(nhbris(chosencell,0),0)) != (1==1))
X           || (interiorcell(nhbris(chosencell,1)) != (1==1))
X           || (interiorcell(nhbris(nhbris(chosencell,1),1)) != (1==1))
X           || (interiorcell(nhbris(chosencell,2)) != (1==1))
X           || (interiorcell(nhbris(nhbris(chosencell,2),2)) != (1==1))
X           || (interiorcell(nhbris(chosencell,3)) != (1==1))
X           || (interiorcell(nhbris(nhbris(chosencell,3),3)) != (1==1))
X
X              /* Now check all 49 cells for ISOLATED status -- yeech! */
X
X              /* check the chosen cell */
X           || (statlist[chosencell].status != ISOLATED)
X
X              /* check to the north/up */
X           || (statlist[nhbris(chosencell,0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(chosencell,0),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               0),0),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               0),0),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               0),0),3),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               0),0),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               0),0),0),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               0),0),0),1),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               0),0),0),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               0),0),0),3),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               0),0),0),3),3),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(chosencell,0),3)].status != ISOLATED)
X
X              /* check to the east/right */
X           || (statlist[nhbris(chosencell,1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(chosencell,1),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(chosencell,1),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               1),1),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               1),1),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               1),1),0),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               1),1),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               1),1),1),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               1),1),1),2),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               1),1),1),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               1),1),1),0),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               1),1),1),0),0),0)].status != ISOLATED)
X
X              /* check to the south/down */
X           || (statlist[nhbris(chosencell,2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(chosencell,2),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(chosencell,2),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               2),2),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               2),2),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               2),2),1),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               2),2),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               2),2),2),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               2),2),2),3),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               2),2),2),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               2),2),2),1),1)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               2),2),2),1),1),1)].status != ISOLATED)
X
X              /* check to the west/left */
X           || (statlist[nhbris(chosencell,3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(chosencell,3),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(chosencell,3),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               3),3),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               3),3),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               3),3),2),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(chosencell,
X                               3),3),3)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               3),3),3),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               3),3),3),0),0)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(chosencell,
X                               3),3),3),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               3),3),3),2),2)].status != ISOLATED)
X           || (statlist[nhbris(nhbris(nhbris(nhbris(nhbris(nhbris(chosencell,
X                               3),3),3),2),2),2)].status != ISOLATED)
X          )
X      );
X
X    if (tries <= MAXTRIES)
X    {
X      movefromto(&isolated,&isolatedct,&unused,&unusedct,UNUSED,chosencell);
X      mazei = (chosencell/((mazewidth-1)/2));
X      mazej = (chosencell%((mazewidth-1)/2));
X      mazei = (2*mazei) + 1;
X      mazej = (2*mazej) + 1;
X      cmaze[mazei][mazej] = WALL;
X/*
X** Tried moving neighbors of unused cells to the DEAD list here; big mistake;
X** ended up with isolated dead cells; let the later routines do it.
X*/
X    }
X  }
X  return;
X}
END_OF_FILE
if test 8699 -ne `wc -c <'makeunused.c'`; then
    echo shar: \"'makeunused.c'\" unpacked with wrong size!
fi
# end of 'makeunused.c'
fi
if test -f 'townmaze.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'townmaze.h'\"
else
echo shar: Extracting \"'townmaze.h'\" \(4580 characters\)
sed "s/^X//" >'townmaze.h' <<'END_OF_FILE'
X/*
X** townmaze.h  Copyright 1991 Kent Paul Dolan,
X**             Mountain View, CA, USA 94039-0755
X**
X** Written to satisfy an inquiry on USENet's rec.games.programmer newsgroup.
X** May be freely used or modified in any non-commercial work.  Copyrighted
X** only to prevent patenting by someone else.
X*/
X
X/*
X** Avoid infinite loops in several routines.
X*/
X
X#define MAXTRIES 5000
X
X/*
X** Define PROGRESS as 1 to see a "walking progress" line of routines
X** announcing themselves as they run, 0 otherwise.
X**
X** Define SHOWSTART as 1 to see a debug maze that shows the situation
X** after the gates and courts are added but before the streets are 
X** otherwise built; 0 to skip it.
X**
X** Define SHOWEND as 1 to see a debug maze that shows the situation
X** after the maze is completed; 0 to skip it;
X**
X** Define HEADER as 1 to see the parameter values echoed back just above
X** the maze, 0 to suppress the header.
X**
X** Define RAND48 in the makefile  to use the srand48() and lrand48()
X** functions instead of the srandom() and random() functions.
X*/
X
X#define PROGRESS 0
X#define SHOWSTART 0
X#define SHOWEND 0
X#define HEADER 1
X
X#ifdef RAND48
X#include <math.h>
X#define RANDOM lrand48
X#define SEEDRANDOM srand48
X#else
X#define RANDOM random
X#define SEEDRANDOM srandom
X#endif
X
X/*
X** Control for defintion (allocation)  of globals in main only,
X** declaration only elsewhere.
X*/
X
X#ifdef MAINLINE
X#define SIDETRACK
X#else
X#define SIDETRACK extern
X#endif
X
X/*
X**
X** The maze is sized in terms of X cells by Y cells, and of X charcters
X** by Y characters; there are 2*cells+1 characters in each maze
X** direction.  For purposes of screen display, the horizontal direction
X** is X and the vertical direction is Y, and the origin is at the top
X** left, with Y positive going down and X positive going right. 
X**
X** For various reasons in the code, a maze must have at least 5 cells
X** per side (which will be a very boring maze indeed.  Corner cells are
X** not used, to ease the design problem.
X**
X** The primary maze determinant is its size in characters; this
X** must be odd in both dimensions.
X**
X*/
X
X/*
X** P A R A M E T E R S
X**
X** -h mazeheight         -- Y size in characters
X** -w mazewidth          -- X size in characters
X**    listsize           -- number of cells in the cell array; computed global
X** -r randomseed         -- use to get the same maze again; replaces clock
X**                          seed; seed used is reported in the header for use
X**                          in a repeat run.
X** -g mazegates          -- number of streets to start at the city wall
X** -l leftgates          -- number of them to leave doors leading outside on
X** -c mazecourts         -- number of streets to start away from the city wall
X** -u mazeunused         -- number of cells to leave unused in the maze
X** -s mazestraightness   -- times per thousand to retry on average before
X**                          accepting a random cell that makes a street turn
X**                          rather than go straight
X**
X** All parameters have default values.  Listsize is computed when mazeheight
X** and mazewidth are known.
X**
X*/
X
X#ifdef MAINLINE
Xint mazeheight = 23;
Xint mazewidth  = 77;
Xint listsize;
Xlong randomseed;
Xint mazegates  =  4;
Xint leftgates  =  2;
Xint mazecourts =  0;
Xint mazeunused = 0;
Xint mazestraightness = 750;
X#else
Xextern int mazeheight;
Xextern int mazewidth;
Xextern int listsize;
Xextern long randomseed;
Xextern int mazegates;
Xextern int leftgates;
Xextern int mazecourts;
Xextern int mazeunused;
Xextern int mazestraightness;
X#endif
X
X/*
X** T Y P E D E F S
X*/
X
Xtypedef struct dlelem
X{
X   int next;
X   int prev;
X   int status;
X   int streetnum;
X   int streetdir;
X} DLENODE;
X
X/*
X** M A Z E  A R R A Y S
X*/
X
X/*
X** The cell list used for storing cell status information.  Although the
X** maze is 2D, this list is kept 1D to make it simple to use list array
X** indices as link list "pointers".
X*/
X
XSIDETRACK DLENODE *statlist;
XSIDETRACK int isolated,  street,  live,  dead,   unused;
XSIDETRACK int isolatedct,streetct,livect,deadct, unusedct;
XSIDETRACK int streetnumct;
X
X/*
X** The maze used for display.
X*/
X
XSIDETRACK char **cmaze;
X
X/*
X** P R I N T I N G  S Y M B O L S
X*/
X
X/*
X** maze drawing elements for ascii screen presentation
X*/
X
X#define WALL  '#'
X#define VDOOR '|'
X#define HDOOR '-'
X#define BLANK ' '
X
X/*
X** D E F I N E D  C O N S T A N T S
X*/
X
X/*
X** status of maze locations
X*/
X
X#define ISOLATED  1
X#define LIVE      2
X#define DEAD      3
X#define STREET    4
X#define UNUSED    5
X
X/*
X** Bogus null pointer for indices used as pointers, since zero is valid.
X*/
X
X#define NOPOINTER -1
END_OF_FILE
if test 4580 -ne `wc -c <'townmaze.h'`; then
    echo shar: \"'townmaze.h'\" unpacked with wrong size!
fi
# end of 'townmaze.h'
fi
if test -f 'townmaze.lmk' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'townmaze.lmk'\"
else
echo shar: Extracting \"'townmaze.lmk'\" \(3734 characters\)
sed "s/^X//" >'townmaze.lmk' <<'END_OF_FILE'
X# townmake.lmk  Copyright 1991 by Kent Paul Dolan,
X#                                 Mountain View, CA, USA, 94039-0755
X#               May be freely used in any non-commercial venture.
X#               Copyrighted only to prevent patenting of the ideas
X#               contained herein by others.
X#
X# BSD 4.3 Unix make file for use with "make -f townmaze.lmk" command
X# Amiga Lattice C 5.05 make file for use with "lmk -f townmzae.lmk" command;
X# (after minor edits) -- needs townmaze.with for blink command.
X#
X# C files:
X#
X# cleanupmap.c
X# closedoors.c
X# closegates.c
X# connectst.c
X# filllist.c
X# fillmaze.c
X# finishalleys.c
X# freespace.c
X# getargs.c
X# interiorcell.c
X# makecourts.c
X# makegates.c
X# makestreet.c
X# makespace.c
X# makeunused.c
X# movefromto.c
X# nhbrexists.c
X# nhbris.c
X# showdbmaze.c
X# showlistdet.c
X# showlistsum.c
X# showmaze.c
X# townmain.c
X# usage.c
X#
X# townmaze.h
X# townproto.h
X#
X# pick a compile command
X#
X# Amiga Lattice C 5.05:
X#
X#CC =			lc -dRAND48
X#
X# or BSD 4.3 Unix oldstyle cc:
X#
XCC =			cc -c
X#
X# The link command is too complex to just define up here; it will need
X# changes in the body of the makefile where "townmaze" is created.
X#
X
XHEADERS =		townmaze.h \
X			townproto.h
X
XSOURCES =		cleanupmap.c \
X			closedoors.c \
X			closegates.c \
X			connectst.c \
X			filllist.c \
X			fillmaze.c \
X			finishalleys.c \
X			freespace.c \
X			getargs.c \
X			interiorcell.c \
X			makecourts.c \
X			makegates.c \
X			makestreet.c \
X			makespace.c \
X                        makeunused.c \
X			movefromto.c \
X			nhbrexists.c \
X			nhbris.c \
X			showdbmaze.c \
X			showlistdet.c \
X			showlistsum.c \
X			showmaze.c \
X			townmain.c \
X                        usage.c
X
XOBJECTS =		cleanupmap.o \
X			closedoors.o \
X			closegates.o \
X			connectst.o \
X			filllist.o \
X			fillmaze.o \
X			finishalleys.o \
X			freespace.o \
X			getargs.o \
X			interiorcell.o \
X			makegates.o \
X			makecourts.o \
X			makespace.o \
X			makestreet.o \
X                        makeunused.o \
X			movefromto.o \
X			nhbrexists.o \
X			nhbris.o \
X			showdbmaze.o \
X			showlistdet.o \
X			showlistsum.o \
X			showmaze.o \
X			townmain.o \
X			usage.o
X
X#
X# Pick a link command
X#
X# Amiga Lattice C 5.05:
X#
X
X#townmaze:		$(OBJECTS)
X#			blink with townmaze.with
X
X#
X# or BSD 4.3 Unix old style cc:
X#
X
Xtownmaze:		$(OBJECTS)
X			cc -o townmaze ${OBJECTS}
X
Xcleanupmap.o:		$(HEADERS) cleanupmap.c
X			$(CC) cleanupmap.c
X
Xclosedoors.o:		$(HEADERS) closedoors.c
X			$(CC) closedoors.c
X
Xclosegates.o:		$(HEADERS) closegates.c
X			$(CC) closegates.c
X
Xconnectst.o:		$(HEADERS) connectst.c
X			$(CC) connectst.c
X
Xfilllist.o:		$(HEADERS) filllist.c
X			$(CC) filllist.c
X
Xfillmaze.o:		$(HEADERS) fillmaze.c
X			$(CC) fillmaze.c
X
Xfinishalleys.o:		$(HEADERS) finishalleys.c
X			$(CC) finishalleys.c
X
Xfreespace.o:		$(HEADERS) freespace.c
X			$(CC) freespace.c
X
Xgetargs.o:		$(HEADERS) getargs.c
X			$(CC) getargs.c
X
Xinteriorcell.o:		$(HEADERS) interiorcell.c
X			$(CC) interiorcell.c
X
Xmakecourts.o:		$(HEADERS) makecourts.c
X			$(CC) makecourts.c
X
Xmakegates.o:		$(HEADERS) makegates.c
X			$(CC) makegates.c
X
Xmakespace.o:		$(HEADERS) makespace.c
X			$(CC) makespace.c
X
Xmakestreet.o:		$(HEADERS) makestreet.c
X			$(CC) makestreet.c
X
Xmakeunused.o:		$(HEADERS) makeunused.c
X			$(CC) makeunused.c
X
Xmovefromto.o:		$(HEADERS) movefromto.c
X			$(CC) movefromto.c
X
Xnhbrexists.o:		$(HEADERS) nhbrexists.c
X			$(CC) nhbrexists.c
X
Xnhbris.o:		$(HEADERS) nhbris.c
X			$(CC) nhbris.c
X
Xshowdbmaze.o:		$(HEADERS) showdbmaze.c
X			$(CC) showdbmaze.c
X
Xshowlistdet.o:		$(HEADERS) showlistdet.c
X			$(CC) showlistdet.c
X
Xshowlistsum.o:		$(HEADERS) showlistsum.c
X			$(CC) showlistsum.c
X
Xshowmaze.o:		$(HEADERS) showmaze.c
X			$(CC) showmaze.c
X
Xtownmain.o:		$(HEADERS) townmain.c
X			$(CC) townmain.c
X
Xusage.o:		$(HEADERS) usage.c
X			$(CC) usage.c
END_OF_FILE
if test 3734 -ne `wc -c <'townmaze.lmk'`; then
    echo shar: \"'townmaze.lmk'\" unpacked with wrong size!
fi
# end of 'townmaze.lmk'
fi
if test -f 'townproto.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'townproto.h'\"
else
echo shar: Extracting \"'townproto.h'\" \(3198 characters\)
sed "s/^X//" >'townproto.h' <<'END_OF_FILE'
X/*
X** townproto.h  Copyright 1991 Kent Paul Dolan,
X**              Mountain View, CA, USA 94039-0755
X**
X** Written to satisfy an inquiry on USENet's rec.games.programmer newsgroup.
X** May be freely used or modified in any non-commercial work.  Copyrighted
X** only to prevent patenting by someone else.
X*/
X
X/*
X** This file contains the prototypes for the functions in townmaze;
X** it is quite a mess since the program was developed in both an
X** ANSI C prototyping environment (Lattice C 5.05) on the Amiga
X** under AmigaOS, and in a BSD 4.3 Unix pre-ANSI C "cc" environment
X** without prototypes. 
X*/
X
X/* functions in cleanupmap.c */
X
X#ifdef __STDC__
Xvoid cleanupmap();
X#else
Xint cleanupmap();
X#endif
X
X/* functions in closedoors.c */
X
X#ifdef __STDC__
Xvoid closedoors();
X#else
Xint closedoors();
X#endif
X
X/* functions in closegates.c */
X
X#ifdef __STDC__
Xvoid closegates();
X#else
Xint closegates();
X#endif
X
X/* functions in connectst.c */
X
X#ifdef __STDC__
Xvoid connectstreets();
X#else
Xint connectstreets();
X#endif
X
X/* functions in filllist.c */
X
X#ifdef __STDC__
Xvoid filllist();
X#else
Xint filllist();
X#endif
X
X/* functions in fillmaze.c */
X
X#ifdef __STDC__
Xvoid fillmaze();
X#else
Xint fillmaze();
X#endif
X
X/* functions in finishalleys.c */
X
X#ifdef __STDC__
Xvoid finishalleys();
X#else
Xint finishalleys();
X#endif
X
X/* functions in freespace.c */
X
X#ifdef __STDC__
Xvoid freespace();
X#else
Xint freespace();
X#endif
X
X/* functions in getargs.c */
X
X#ifdef __STDC__
Xvoid getargs(int argc,char *argv[]);
X#else
Xint getargs();
X#endif
X
X/* functions in interiorcell.c */
X
X#ifdef __STDC__
Xint interiorcell(int cellid);
X#else
Xint interiorcell();
X#endif
X
X/* functions in makecourts.c */
X
X#ifdef __STDC__
Xvoid makecourts();
X#else
Xint makecourts();
X#endif
X
X/* functions in makegates.c */
X
X#ifdef __STDC__
Xvoid makegates();
X#else
Xint makegates();
X#endif
X
X/* functions in makespace.c */
X
X#ifdef __STDC__
Xvoid makespace();
X#else
Xint makespace();
X#endif
X
X/* functions in makestreet.c */
X
X#ifdef __STDC__
Xint makestreet(int chosencell,int streetid,int streetpoints);
X#else
Xint makestreet();
X#endif
X
X/* functions in makeunused.c */
X
X#ifdef __STDC__
Xvoid makeunused();
X#else
Xint makeunused();
X#endif
X
X/* functions in movefromto.c */
X
X#ifdef __STDC__
Xvoid movefromto(int *fromlist,int *fromcount,int *tolist,int *tocount,
X                int newstat,int cellnum);
X#else
Xint movefromto();
X#endif
X
X/* functions in nhbrexists.c */
X
X#ifdef __STDC__
Xint nhbrexists(int cellid,int nhbrid);
X#else
Xint nhbrexists();
X#endif
X
X/* functions in nhbris.c */
X
X#ifdef __STDC__
Xint nhbris(int cellid,int nhbrid);
X#else
Xint nhbris();
X#endif
X
X/* functions in showdbmaze.c */
X
X#ifdef __STDC__
Xvoid showdebugmaze();
X#else
Xint showdebugmaze();
X#endif
X
X/* functions in showlistdet.c */
X
X#ifdef __STDC__
Xvoid showlistdetail();
X#else
Xint showlistdetail();
X#endif
X
X/* functions in showlistsum.c */
X
X#ifdef __STDC__
Xvoid showlistsummary();
X#else
Xint showlistsummary();
X#endif
X
X/* functions in showmaze.c */
X
X#ifdef __STDC__
Xvoid showmaze();
X#else
Xint showmaze();
X#endif
X
X/* functions in townmain.c */
X
X#ifdef __STDC__
Xvoid main(int argc,char *argv[]);
X#else
Xint main();
X#endif
X
X/* functions in usage.c */
X
X#ifdef __STDC__
Xvoid usage();
X#else
Xint usage();
X#endif
X
END_OF_FILE
if test 3198 -ne `wc -c <'townproto.h'`; then
    echo shar: \"'townproto.h'\" unpacked with wrong size!
fi
# end of 'townproto.h'
fi
if test -f 'townuser.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'townuser.doc'\"
else
echo shar: Extracting \"'townuser.doc'\" \(8428 characters\)
sed "s/^X//" >'townuser.doc' <<'END_OF_FILE'
XWelcome, as it were, to townmaze.
X
XJust typing "townmaze" at the command line will do an example maze for
Xyou, once the program has been compiled and installed (see README).
X
XTyping "townmaze help" at the command line will get you a "usage"
Xdisplay about 22 lines high, describing each of the parameters and
Xtheir limits.
X
XYou might want to try both of these before reading the rest of this
Xdocument.
X
XWhat you see in a townmaze:
X
XIt should be obvious, but the symbols in the townmaze output are "#"
Xfor walls or solid rock, "-" or "|" for horizontal or vertical doors,
Xand " " (blank), for streets and room interiors.
X
XThis program is not, in essence, a game, but instead a tool to use in
Xmaking games.  What it does is to lay out a city after the fashion of
Xthe top level of Bard's Tale I.  You can then use this city layout in
Xeither a paper and pencil dungeon crawling game, or as part the design
Xof a 3D computer dungeon crawling game. You can even (see
Xtownpgmr.doc) install this code into such a game to create city maze
Xlayouts on demand. 
X
XTownmaze has a rich variety of parameters, to let you vary your city
Xin size, shape, building density, and complexity.  The parameters are
Xentered at the command line in standard Unix fashion, as "dash, flag
Xletter, space, value" sets, like
X
X	townmaze -h 23 -w 77 -g 4 -l 2 -c 0 -u 0 -s 750 -r 123456789
X
XExcept for the "r" parameter, the values shown are the defaults, and
Xif the parameter and its value are omitted from the command line, the
Xabove defaults are used.  The exception "r", the random number seed,
Xis read off the system clock unless it is overridden by a command line
Xparameter. 
X
XHere is what the parameters mean.
X
X
X P A R A M E T E R S
X
X -h mazeheight         -- vertical size of the maze, in characters
X
X -w mazewidth          -- horizontal size of the maze, in characters
X
X -g mazegates          -- number of streets to start at the city wall
X
X -l leftgates          -- number of the gates to leave doors leading
X                          outside from the city (the rest are turned
X                          back into city wall)
X
X -c mazecourts         -- number of streets to start away from the city
X                          wall in the city interior
X
X -u mazeunused         -- number of cells to leave unused (solid rock)
X                          in the maze; these cells will be inaccessible
X
X -s mazestraightness   -- times per thousand to retry on average before
X                          accepting a random cell that makes a street
X                          turn rather than go straight
X
X -r randomseed         -- use to get the same maze again; replaces clock
X                          seed; seed used is reported in the header for use
X                          in a repeat run.
X
XThe maze is normally written to the screen (more exactly, to "standard
Xoutput"), and benefits a lot from screens that can show more than 25
Xlines and more than 80 characters; the more room townmaze has to work,
Xthe better job it can do.  The maze is sized in characters, but it
Xreally exists in cells, it takes several characters to draw a single
Xcell.  In the Bard's Tale I game, the main level was 32 by 32 cells.
XIn townmaze, that is a maze 65 by 65 characters, since it takes two
Xrows of characters to make each cell, plus one extra row for the last
Xwall, each way. 
X
XThus, a really effective way to use townmaze is to capture and print
Xits output on paper, to get bigger, more interesting mazes.
X
XTownmaze will make a maze as big as your computer will provide the
Xmemory needed for storing the maze and a corresponding list of cells,
Xand should fail gracefully if you ask for a maze bigger than your
Xcomputer can hold.
X
XAs mentioned, townmaze has a rich variety of parameters, and the best
Xway to understand what they do is to try them; they interact with one
Xanother, so that there is no simple description of what results in the
Xmaze as you vary each one, but here's an approximation. 
X
XThe -h and -w parameters just make the maze bigger; that's easy enough.
X
XTownmaze works by building a maze that is solid room cells with no
Xdoors, then changing a few room cells to street cells, then knocking
Xout more room cells to make street cells until all the streets are
Xconnected and all the room cells have a door. Room cells get a door
Xwhen they are adjacent to at least one street cell.
X
XThe -g parameter tells how many of the starting street cells will be
Xon the border of the maze, next to the city wall.  To start with, each
Xof these cells is given a door which is a gate through the city wall.
XLots of these "gate" cells help break up the street that otherwise runs
Xall around the perimeter of the city to provide doors to the border
Xcells.
X
XWhen the straightness parameter is very strict, rows of room cells
Xoften run from each side of a gate toward the middle of town, like the
Xones in Bard's Tale I did.  There need not be any gate cells at all,
Xin which case your town will have a street that runs all around the
Xoutside.  Presumably then you would put the biggest treasures and
Xnastiest monsters in the middle of town, unlike Bard's tale, which put
Xthem near the corners.
X
XThe -l parameter tells how many of the gates will survive; the rest get
Xturned back into city wall.  This lets you enjoy the complex border of
Xa city with lots of gate cells, without having lots of, or even any,
Xreal gates leaving town.
X
XA town maze must start with at least one street somewhere, so the -c
X("courtyard") parameter lets you start streets in the interior of the
Xtown, instead of the border.  Interesting things happen if you do 1
Xcourtyard and no gates, and also if you do maximums of courtyards and
Xgates, and all other combinations.  In general, lots of courtyards
Xmakes for a less dense, more "gangly" city structure with lots of
Xcul de sacs.
X
XTownmaze with just those parameters tends to create long runs of rooms
Xsnaking about the town.  To give more elaborate structures, and still
Xhave all rooms accessible, it is necessary to leave some cells unused.
XThe -u ("unused") parameter tells how many unused cells to allow.  The
Xlimit is low, because the rule is that a street cannot be adjacent to
Xan unused cell, which means unused cells make it hard to connect all
Xthe streets together.  By forcing at least three cells between unused
Xcells, this connectivity is assured, but doing this means only about
X2% of the interior cells can be unused cells.  Unused cells tend to
Xallow room structures with three or four "arms", rather than just
Xlinear snaking structures, and give the town a more interesting
Xappearance.
X
XThe most important parameter affecting the appearance of the towns
Xdesigned by townmaze is -s, the straightness parameter.  It can take
Xvalues from 0 (don't worry about straightness) to 998 (try as hard
Xas possible to make streets run straight).  Townmaze implements this
Xstraightness by repeatedly trying a random room to see if it will let
Xa street run straight, so straightness has a profound effect on the
Xtime it takes townmaze to design a town.  A set of parameters that
Xallows a town to be drawn in a few seconds with a straightness of 0,
Xwill take about the same number of minutes with a straightness of 996,
Xfor example.
X
XNonetheless, the city streets wander around a lot, and knock out lots
Xof extra rooms, leading to sparse looking towns, until the highest
Xlevels of straightness are used.  This isn't all bad, as the empty
Xspace is like some of the big open courtyards in Bard's Tale I; you
Xcan play with this parameter until you get the appearance you like.
X
XThe -r parameter allows you to generate the same town maze repeatedly.
X
XIf the townmaze program is installed with the HEADER definition turned
Xon, then above the maze output you will see a line showing the
Xparameters for this run, including "seed", typically eight or nine
Xdigits.  If you feed this seed value back in as the value of the -r
Xparameter, you will get the identical maze again.  Even without the
XHEADER turned on, you can enter your own choices of seed until you
Xfind the town maze that suits your needs, then run townmaze again with
Xthe same parameters to save the maze to a file, to print it, or, if
Xyou install the software in a computer game, to be able to use a level
Xover and over without having to store its design explicitly in the
Xgame. 
X
XI hope you have fun with my toy.
X
XCopyright 1991, Kent Paul Dolan,
XPO Box 390755, Mountain View, CA, USA, 94039-0755
END_OF_FILE
if test 8428 -ne `wc -c <'townuser.doc'`; then
    echo shar: \"'townuser.doc'\" unpacked with wrong size!
fi
# end of 'townuser.doc'
fi
echo shar: End of archive 2 \(of 4\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0