Contributor: MATTHEW MASTRACCI

{
From: Matthew.Mastracci@matrix.cambo.cuug.ab.ca (Matthew Mastracci)

 l> This is just cool enough that I'm going to post publicly too.

 l> LOU'S MAZE ALGORITHM

    Great algorithm!  I read your posting, pondered it, sat down for an hour
and wrote this:

}
{$r-} { Increases speed a marginal amount }
{
  Maze Generator - PD 1995, by Matthew Mastracci
                   rayban@matrix.cambo.cuug.ab.ca

  This program generates a maze using a plant-like approach.  It starts by
  sowing "seeds" about every four units around the edge, and two in the
  middle.  These then grow out in a random order in three directions.  This
  prevents seeds from sprouting if they would grow into another.

  The original algorithm for generating mazes was written by Lou Duchez and
  posted in comp.lang.pascal.  Here's a small excerpt from the part which
  describes how to work with the seeds:

 ---
Keep executing this loop until you run out of seeds:

  - Randomly select a seed.  Extend the wall in some valid direction from
    this seed point, by turning into walls the grid locations one unit and
    two units away from the seed.  To prevent the maze from closing off at
    any point, DO NOT EXTEND A WALL TO ANY POINT THAT IS ALREADY MARKED AS A
    WALL!  (With this rule, you never close off the maze; you simply
    complicate the path from beginning to end.)

  - Remove this seed.  It's done its job.

  - Add three seed points at this new location.  (The assumption is that the
    wall could grow in three directions from this new point; if you want to
    be more exacting, you can add as many seeds as there are directions that
    the wall could extend from that point.  It really doesn't matter much,
    except for the possibility of running out of seed point array elements if
    you always add 3.)

  - Seed maintenance: go through your list of seeds and eliminate any
    seeds that cannot extend in any valid direction.
 ---

  Feel free to use this source anywhere you want in anyway you want.  I
  recommend you use it to generate mazes for games, however...  :)

}
program
  MazeGenerator;

uses
  Crt;

const
  xMax = 79;
  yMax = 49;
  sMax = (xMax - 3) * (yMax - 3) div 2;

type
  tMap = record
    Data : array[1..xMax, 1..yMax] of Boolean;
    xEntrance, yEntrance : Byte;
    xExit, yExit : Byte;
  end;
  tSeed = record
    x, y, Dir : Byte;
    Valid : Boolean;
  end;

var
  Map : tMap;

{ Draws the map }
procedure DrawMap(Map : tMap);
var
  x, y : Byte;
begin
  for x := 1 to xMax do begin
    for y := 1 to yMax do begin
      if Map.Data[x, y] then Mem[$b800 : y * 160 + x * 2] := 219;
    end;
  end;
end;

{ Generates the map }
procedure GenerateMap(var Map : tMap);
var
  Seeds : array[1..sMax] of tSeed;

{ Reports TRUE if any seeds are "unsprouted" }
function NoSeeds : Boolean;
var
  i : Word;
  FoundSeeds : Boolean;
begin
  FoundSeeds := False;
  for i := 1 to sMax do begin
    if Seeds[i].Valid then FoundSeeds := True;
  end;
  NoSeeds := not FoundSeeds;
end;

{ "Plant" a seed }
procedure AddSeed(x, y, Dir : Byte);
var
  i : Word;
begin
  i := 0;
  repeat
    Inc(i);
  until (i = sMax) or not Seeds[i].Valid;
  if Seeds[i].Valid then begin
    WriteLn('Error: Out of seed space!');
    Halt;
  end else begin
    Seeds[i].x := x;
    Seeds[i].y := y;
    Seeds[i].Dir := Dir;
    Seeds[i].Valid := True;
  end;
end;

{ "Sprout" a seed }
procedure Sprout;
var
  i : Word;
begin
  repeat
    i := Random(sMax) + 1;
  until Seeds[i].Valid;
  with Seeds[i] do begin
    case Dir of
      0: begin { up }
        if not Map.Data[x, y - 2] then begin
          AddSeed(x, y - 2, 1);
          AddSeed(x, y - 2, 2);
          AddSeed(x, y - 2, 3);
          Map.Data[x, y - 1] := True;
          Map.Data[x, y - 2] := True;
        end;
      end;
      1: begin { down }
        if not Map.Data[x, y + 2] then begin
          AddSeed(x, y + 2, 0);
          AddSeed(x, y + 2, 2);
          AddSeed(x, y + 2, 3);
          Map.Data[x, y + 1] := True;
          Map.Data[x, y + 2] := True;
        end;
      end;
      2: begin { left }
        if not Map.Data[x - 2, y] then begin
          AddSeed(x - 2, y, 0);
          AddSeed(x - 2, y, 1);
          AddSeed(x - 2, y, 3);
          Map.Data[x - 1, y] := True;
          Map.Data[x - 2, y] := True;
        end;
      end;
      3: begin { right }
        if not Map.Data[x + 2, y] then begin
          AddSeed(x + 2, y, 0);
          AddSeed(x + 2, y, 1);
          AddSeed(x + 2, y, 2);
          Map.Data[x + 1, y] := True;
          Map.Data[x + 2, y] := True;
        end;
      end;
    end;
  end;
  Seeds[i].Valid := False;
end;

var
  x, y : Byte;
  DrawCount : Byte;

begin
  FillChar(Map, SizeOf(Map), 0); { Zero out map }
  FillChar(Seeds, SizeOf(Seeds), 0); { Erase seeds }
  { Draw border }
  with Map do begin
    for x := 1 to xMax do begin
      Data[x, 1] := True;
      Data[x, yMax] := True;
    end;
    for y := 1 to yMax do begin
      Data[1, y] := True;
      Data[xMax, y] := True;
    end;
    { Map entrance }
    yEntrance := 1;
    xEntrance := (Random(yMax div 2) + 1) * 2;
    Data[xEntrance, yEntrance] := False;
    { Map exit }
    yExit := yMax;
    xExit := (Random(yMax div 2) + 1) * 2;
    Data[xExit, yExit] := False;
    { Add a couple of seeds in the middle (islands) }
    AddSeed((Random(xMax div 2) + 1) * 2 + 1, (Random(yMax div 2) + 1) * 2 + 1,
Random(4));
    AddSeed((Random(xMax div 2) + 1) * 2 + 1, (Random(yMax div 2) + 1) * 2 + 1,
Random(4));
    { Add seeds around the edges, about every 4 units }
    for DrawCount := 1 to (2 * xMax + 2 * yMax) div 4 do begin
      case Random(4) of
        0: AddSeed((Random(xMax div 2) + 1) * 2 + 1, 1, 1); { top, going down }
        1: AddSeed((Random(xMax div 2) + 1) * 2 + 1, yMax, 0); { bottom, going
up }
        2: AddSeed(1, (Random(yMax div 2) + 1) * 2 + 1, 3); { left, going right
}
        3: AddSeed(xMax, (Random(yMax div 2) + 1) * 2 + 1, 2); { right, going
left }
      end;
    end;
  end;
  DrawCount := 0;
  repeat
    Inc(DrawCount);
    if DrawCount = 100 then begin
      DrawCount := 0;
      DrawMap(Map);
    end;
    if KeyPressed then begin
      while KeyPressed do ReadKey;
      DrawMap(Map);
    end;
    Sprout;
  until NoSeeds;
  DrawMap(Map);
end;

begin
  Randomize;
  TextMode(CO80 + Font8x8);
  GenerateMap(Map);
end.