#include <stdio.h>

#include <string.h>

#include <stdlib.h>


#define MAXSIZE 40

#define SIZE 12

#define SHAPEMAGIC 8  /* Tweak this between 2 and 14 for longer corridors */


#define NORTH 1

#define EAST 2

#define SOUTH 4

#define WEST 8

/* Some of the N/E/W/S logic could be done more neatly with sparse 16-element tables */

/* If we add an overhead maze view, these two tags can be stored in the
   maze array to denote the viewer's position and perhaps an optional
   bad guy, or treasure?  Just remember to update them when they move. */
#define ME 16

#define TARGET 32

#define VISITED 64 /* breadcrumb to show how much of the maze you have seen */


static int maze[MAXSIZE+1][MAXSIZE+1]; /* Initialsed to all zeroes */
static int locx, locy;   /* viewer location, uninitialsed */
static int facing;       /* what way am I facing? */

/* Add a wall during building phase */
void wall(int x, int y, int dirn) { maze[x][y] |= dirn; };

/* This shows you where to get the info from, to draw the perspective maze
   ahead of you. Very easy to adapt to line graphics.  Use 'dist' to
   determine scale and position. Whether you allow a view through open
   passageways or shroud them in darkness is up to you, just extend
   the logic to look at cells within your cone of vision rather than
   just directly ahead. */
void draw_forward_view(int x, int y, int facing, int dist) {
  int lx = x, ly = y, leftwall, rightwall;
  if (dist == 0) printf("\n\nAt %d,%d facing %s\n\n\n", locx, locy,
             (facing == NORTH ? "North" :
             (facing == EAST  ? "East" :
             (facing == SOUTH ? "South" :
                                "West"))));
  if (maze[x][y] & facing) printf("     ._.\n"); else {
      switch (facing) {
      case NORTH: ly--; break;
      case EAST:  lx++; break;
      case SOUTH: ly++; break;
      case WEST:  lx--; break;
      }
      draw_forward_view(lx, ly, facing, dist+1); /* Next square moving away from us */
    }
    facing = ((facing << 3) | (facing >> 1))&15; /* look left */
    leftwall = maze[x][y]&facing;
    facing ^= ((facing & (NORTH|SOUTH)) ? (NORTH|SOUTH) : (EAST|WEST)); /* look right */
    rightwall = maze[x][y]&facing;
    printf("     %c%c%c\n", leftwall ? '|' : ' ', dist==0 ? '*' : ' ', rightwall ? '|' : ' ');
}

int luck(void) {return (rand()&15) < SHAPEMAGIC;} /* determines corridor length */

/* Wonderful maze builder taken from http://homepages.cwi.nl/~tromp/maze.html
   and modified to produce a square maze, plus a maze array where
   every square is tagged with which of the 4 exits is blocked by
   a wall. This data structure is now suitable for use in creating
   an isometic maze exploration game, FPS, etc */

int main(int argc, char **argv)
{
  int H,		/* height of the maze */
      C,		/* current cell */
      E,		/* temporary pointer used in the updating */
      L[MAXSIZE],
      R[MAXSIZE];       /* left and right pointers */

  if (argc > 1)
      for (H = 0; H < strlen(argv[1]); H++)
          for (C = 0; C < argv[1][H]; C++)
              (void)luck(); /* give a parameter and get a different maze! */

  L[0] = 1; H = SIZE;	/* sets height - could be a command line parameter if you prefer */
  if ((H < 5) || (H > MAXSIZE)) exit(1); /* Size sanity check */

  for (E = SIZE+1; --E; L[E] = R[E] = E) wall(E-1, 0, NORTH); /* top of maze */
  wall(0, SIZE-H, WEST);

  /* Create a fully-connected spanning tree that fills the square - no inaccessible cells */
  while (--H) {                         /* more rows to do */
    for (C = SIZE+1; --C; ) {    	/* visit cells from right to left */
      if (C != (E=L[C-1]) && luck()) {	/* no wall to the right */
        R[E] = R[C];			/* link E */
        L[R[C]] = E;			/* to R[C] */
        R[C] = C-1;			/* link C */
        L[C-1] = C;			/* to C-1 */
      } else { /* My right wall is my right-hand neighbor's left wall */
        wall(SIZE-C, SIZE-H-1, EAST); wall(SIZE-C+1, SIZE-H-1, WEST); /* wall to the right */
      }
      if (C != (E=L[C]) && luck()) { 	/* wall downward? */
        R[E] = R[C];			/* link E */
        L[R[C]] = E;			/* to R[C] */
        L[C] = C;			/* link C */
        R[C] = C;			/* to C */
        wall(SIZE-C, SIZE-H-1, SOUTH); wall(SIZE-C, SIZE-H, NORTH);
      } /* else no wall downward */
    }
    wall(0, SIZE-H, WEST); /* Think there's an extra one but we have a tombstone wall around the maze */
  }

  for (C = SIZE+1; --C; ) {  	        /* bottom of maze */
    wall(C-1, SIZE-1, SOUTH);
    if (C != (E=L[C-1]) && (C == R[C] || luck())) {
      L[R[E]=R[C]]=E; L[R[C]=C-1]=C;
    } else wall(SIZE-C, SIZE-1, EAST);
    E = L[C]; R[E] = R[C]; L[R[C]] = E; L[C] = C; R[C] = C;
  }

  /* Place the player at a random position */
  locx = (rand()&65535)%SIZE; locy = (rand()&65535)%SIZE; facing = 1 << (rand()&3);

  for (;;) { /* Now play */

      maze[locx][locy] |= VISITED; /* Drop a breadcrumb (not yet used) */
      
      draw_forward_view(locx, locy, facing, 0); /* You can only ever see in front of you. */

      /* Get a movement command.  Replace this code with mouse, cursor keys, whatever... */
      fprintf(stderr, "\n\nMove: "); C = getchar(); if (C == EOF) break; if (C <= 32) continue;

      /* You have to turn to look to the side or change direction: */
      if (C == 'L' || C == 'l') facing = ((facing << 3) | (facing >> 1))&15; else
      if (C == 'R' || C == 'r') facing = ((facing << 1) | (facing >> 3))&15; else

      /* You can only move forwards */
      if (C == 'M' || C == 'm') {
          if (maze[locx][locy] & facing) {
              printf("Can't move, the way forward is blocked.  Turn first, then move!\n");
              while ((C != '\n') && (C != EOF)) C = getchar(); /* drain excess chars on line */
          } else { /* Move forward */
            switch (facing) {
              case NORTH: locy--; break;
              case EAST:  locx++; break;
              case SOUTH: locy++; break;
              case WEST:  locx--; break;
            }
          }
      } else { /* unknown input */
          printf("Enter L (left), R (right) or M (move).  (stack multiple moves using MMR etc)\n");
          while ((C != '\n') && (C != EOF)) C = getchar(); /* drain excess chars on line */
      }
  }
  
  exit(0);
  return(0);
}