{*
 *  Program Title:	Wumpus
 *  Written by:		Gregory Yob
 *			More BASIC Computer Games
 *			Edited by David H. Ahl
 *
 * Translated into Pascal by Paul H. Gilliam from the
 * BASIC programs 'WUMPUS 1' and 'WUMPUS 2'
 *
 * This game will teach you how to play it.
 * Happy wumpus hunting!
 *
 *   29 June 1980	-corrected minor logic bugs.
 *   29 June 1980	-Modified for Pascal/Z v3.0
 *			 Pascal/Z does not allow jumps out
 *			 of Procedures/Functions [A practice
 *			 I fully agree with!]
 *  Donated July, 1980
 *}
Program  Wumpus;
LABEL	99;		{ Fatal error }
CONST
  default = 80;
  {---   define your screen parameters   ---}
  s_beglin =  1;	{ first line }      (* ADM-3A Screen parameters *)
  s_endlin = 23;	{ last  line }
  s_begcol =  1;	{ first column }
  s_endcol = 80;	{ last column  }

TYPE    
  alfa    = STRING 10;		{ just the right size }
  Dstring = STRING default;
  str0    = STRING 0;
  str255  = STRING 255;
  room     = 1 .. 20;
  tunnel   = 1 .. 3;

VAR
 arrowcount	: integer;
 bell		: char;
 cave		: array[room, tunnel] of room;
 cursorhome,		{ cursor controls }
 cursorup,
 cursordown,
 cursorleft,
 cursorright,  
 clearscreen,
 escape		: char;
 fatal_error	: boolean;
 i, j		: integer;	{ global indexers }
 initlocate	: array[1..6] of room;

Procedure KEYIN(VAR cix: char); external;
(*---Direct Keyboard input of a single char---*)

Function RANDOM(limit: integer): real; external;
(*---returns a real number from 0.0 to limit---*)

Function length(x: str255): integer; external;

Procedure gotoxy( col, row : integer );
(*		 X-coord, Y-coord	*)
begin
  WRITE( chr(27), '=', chr(row+32), chr(col+32))
end;

Procedure terminit;
begin 
 bell := chr(7);
 escape	    := chr(27);
 cursorhome := chr(30);
 cursorup   := chr(11);;   
 cursordown := chr(10);
 cursorleft := chr(8);
 cursorright := chr(12);  
 clearscreen := chr(26);	{ ASCII control-Z }
end{of terminit};

Procedure CLEAR;
begin
  Write(clearscreen);
end;

Procedure clearline( row: integer );
begin
  gotoxy( s_begcol, row);
  WRITE( ' ':(s_endcol-s_begcol+1) );
  gotoxy( s_begcol, row);
end;

Function  randroom : room; { 1..20 }
begin
  randroom := trunc(random(20)) + 1
end  { randroom };

Function  randtunnel : tunnel; { 1..3 }
begin
 randtunnel := trunc(random(3)) + 1
end  { randtunnel };

Function  wumpmove : integer; 
var	i : integer;
begin
  i := trunc(random(4)) + 1;
  If i > 3 then
    wumpmove := -1
  Else
    wumpmove := i;
end  { wumpmove };

Function QUIRY(sign: Dstring): boolean;
var	ch: char;
begin
  writeln;
  Repeat
    write(sign);
    KEYIN(ch);writeln(ch);
    writeln;
  Until ch IN ['Y', 'y', 'N', 'n'];
  QUIRY := ch in ['n', 'N'];
end;

Procedure Instruct;
{*
 *   Attempts to read in an external file to instruct the player
 *   as to how to play the game.
 *   Instruct will pause for console input whenever it finds the
 *   string "$pause" in the first position of a line in the line.
 *}
var	line : Dstring;  
	Ifile : text;
	ch : char;

	Procedure ShowInstructions;
	begin
	  CLEAR;
	  Readln(Ifile,line);
	  while  not eof(Ifile) do
	    begin
	      If (line = '$pause') or (line = '$PAUSE') then
		begin
		Clearline(s_endlin);
		write('Press <sp> to continue.');KEYIN(ch);
		CLEAR;
		end
	      Else
		writeln(line);
	      readln(Ifile,line);
	   end;{ While }
	End{ShowInstructions};

begin  { instruct }  
  CLEAR;
  write('Do you want instructions on how to play? ');
  KEYIN(ch);Writeln(ch);
  writeln;
  If (ch='y') or (ch='Y') then
    begin
      RESET('WUMPUS.DOC',Ifile);
      If not EOF(Ifile) then
	ShowInstructions
      Else
	begin
	writeln;
	writeln('Sorry,  instructions not availiable yet.');
	end;
    end
End{of instruct};
   
Procedure  getacave;
LABEL	9;{ABORT}
var
  i : room;	{ 1..20 }
  j : tunnel;	{ 1..3 }
  k : integer;
  CAVENAME : STRING 5;
  LINE : Dstring;
  cavein : text;
  ch : char;
begin 
 cavename := 'CAVE ';
 Repeat
   Writeln;
   write(bell, 'Enter cave #(0-5) ');
   KEYIN(ch);Writeln(ch);
 Until  ch in ['0'..'5'];
 cavename[5] := ch;
 (* OPEN file "cavename" for Read assign cavein *)
 RESET(cavename,cavein);
 fatal_error := EOF(cavein);
 If fatal_error then {ABORT}
   begin
     writeln;
     writeln('Fatal error - file not found');
     {ABORT}goto 9;
   end;
 writeln('reading ',cavename); 
 readln(cavein, line);
 for  i := 1 to 20 do
   for j := 1 to 3 do read(cavein,cave[i,j]);
  writeln;
  writeln('You are in ',line);
  writeln;
9:{ABORT}
End{ of getacave }{ CLOSE(cavein) };
 
Procedure  initsetup; 
var	locatesunique : boolean;
	i, j : integer;  
begin
 Repeat
   for  i := 1 to 6 do initlocate[i] := randroom;  
   locatesunique := true;
   i := 1;
   while  locatesunique and (i <= 6) do
     begin
     j := 1;
     while  locatesunique and (j <= 6) do
       begin
       If (initlocate[i] = initlocate[j]) and (j <> i) then
	 locatesunique := false
       Else
	 j := j + 1;
       end;
     i := i + 1
     end
 Until  locatesunique
End  { initsetup };
 
Procedure  HuntTheWumpus;
CONST	Title = 'Hunt the Wumpus'; 
TYPE	long = real;
VAR	i	: integer;
	game	: (inprogress, youlost, youwon);
	locate	: array[1..6] of room;
 
	Procedure  warnings;  
	var	location, i, j: integer;
	begin
	  writeln;
	  location := locate[1];   
	  for  i := 2 to 6 do
	    begin
	    for  j := 1 to 3 do
	      begin
	      If cave[location,j] = locate[i] then
		 case  i  of
		      2:  writeln('I smell a Wumpus!');
		   3, 4:  writeln('I feel a draft!');
		   5, 6:  writeln('Bats nearby!');
		 End{case};
	      end{ for j };
	    end{ for i };
	  writeln('You are in Room ',location:2);
	  write('Tunnels lead to ');
	  for  i := 1 to 3 do write(cave[location,i]:3); 
	  writeln;
	End  { warnings };
 
	Function  WantToShoot : boolean;   
	LABEL 4;{EXIT}
	var	ch : char;      
	begin
	  Repeat
	    writeln;
	    write('Shoot or move (s-m) <esc>');     
	    KEYIN(ch);writeln;
	    If ch = escape then
	      begin   
	      game := youlost;
	      { EXIT(HuntTheWumpus) } goto 4;
	      end;
	    If ch = 'l' then
	      begin  
	      write('you = ',locate[1]:3, ' ':8);
	      write(' wumpus = ',locate[2]:3);
	      writeln(' pits = ',locate[3]:3,',',locate[4]:3);
	      writeln(' bats = ',locate[5]:3,',',locate[6]:3);
	      writeln
	      end;
	  Until  ch in ['m', 'M', 's', 'S'];   
	  WantToShoot := ch in ['S', 's'];
	4:{EXIT}
	End  { WantToShoot }; 
                    
	Procedure  movewumpus; 
	var	i : integer;
	begin
	  i := wumpmove;
	  If i > 0 then  locate[2] := cave[locate[2],i];
	  If locate[1] = locate[2] then
	    begin      
	    writeln('Tsk Tsk Tsk - Wumpus got you!');
	    game := youlost
	    end;
	End  { movewumpus };
 
	Function  lint(    s : alfa;
		       var l : long) : integer;  
	LABEL 3;{EXIT}
	var
	  i, j : integer;
	  negitive : boolean;
	  ch : char;
	begin 
	  j := 0;
	  l := 0;
	  lint := -1;
	  negitive := false;
	  for  i := 1 to length(s) do
	    begin 
	    ch := s[i];
	    If ch in ['0'..'9'] then
	      begin
	      j := j + 1;
	      If j > 36 then
	        begin lint := -2; {EXIT(lint)}goto 3 end;
	      l := l * 10 + (ord(ch) - ord('0'))
	      end 
	    Else
	      If ch = '-' then
		begin If negitive then {EXIT(lint)}goto 3 end
	      Else  {EXIT(lint)}goto 3;
	    end;{ FOR }
	  If l > maxint then
	    lint := j
	  Else
	    lint := 0;
	  If negitive then  l := -l;
	  3:{EXIT}
	end{lint};
 
Procedure doshot;
var
  path : array[1..5] of integer;
  rooms, i, j, arrow : integer;
  roomok, targethit : boolean;
  l : long;
  ans : alfa;
begin
 { program the arrow }
  Repeat
    write('No. of rooms (1-5) ');
    readln(ans);   
    i := lint(ans, l);
    rooms := trunc(l);
  Until  (i = 0) and (rooms >= 1) and (rooms <= 5);              
  for  i := 1 to rooms do
    begin
    Repeat
      roomok := true;
      write('Room # ');
      readln(ans);  
      j := lint(ans, l);
      roomok := (j = 0) and (l > 0) and (l < 21);
      path[i] := trunc(l);
      If i > 2 then
	If path[i] = path[i-2] then
	  begin
	  writeln('Arrows aren''t that crooked - try another room');
	  roomok := false;
	  end;
      If not roomok then  write(bell);  
    Until  roomok;
    end;
    { shoot the arrow }
  arrowcount := arrowcount - 1;
  I := 1;
  arrow := locate[1];
  Repeat 
    roomok := false; 
    for  j := 1 to 3 do
      If cave[arrow,j] = path[i] then  roomok := true;
    If roomok then
      arrow := path[i]
    Else
      arrow := randroom;
    If arrow = locate[1] then
	begin    
	writeln('OUCH! Arrow got YOU!');
	game := youlost
	end 
    Else
      If arrow = locate[2] then
	begin
	writeln('Aha! You got the Wumpus!');
	game := youwon
	end;
    i := i + 1;
  Until  (i > rooms) or (game <> inprogress);
  Case game of
    inprogress:	begin
		If arrowcount=0 then
		  begin
		  writeln('Out of arrows!!');
		  game := youlost;
		  end
		Else
		  writeln('missed');
		MoveWumpus;
		end;
   youwon:	{dummy};
   youlost:	MoveWumpus
   end{of Case};
end  { doshot };
 
Procedure domove; 
var
  room, i, location : integer;
  roomok, movefinished : boolean;
  l : long;
  ans : alfa;
begin
  location := locate[1];
  Repeat
    write('Where to? '); 
    readln(ans); 
    roomok := false;
    i := lint(ans, l);
    room := trunc(l);
    If i = 0 then
      begin
      for  i := 1 to 3 do
	If room = cave[location,i] then  roomok := true;
      If room = location then  roomok := true;
      end;{ If i=0 }
    If not roomok then  writeln('Not possible');
  Until  roomok;
  location := room;
  Repeat  
    locate[1] := location;
    movefinished := true; 
    If location = locate[2] then
      begin
      writeln('... OOPS!  Bumped a Wumpus');
      movewumpus
      end;
    If game = inprogress then
      If (location = locate[3]) or (location = locate[4]) then
	begin
	writeln('YYYIIEEEE . . . Fell in a pit!');
	game := youlost
	end
      Else
	If (location = locate[5]) or (location = locate[6]) then
	  begin
	  writeln('ZAP -- Super bat snatch! Elsewhereville for you!'); 
	  movefinished := false;
	  location := randroom
	  end;
  Until  movefinished;
end  { do move }; 
 
begin { HuntTheWumpus } 
  arrowcount := 5; 
  for  i := 1 to 6 do locate[i] := initlocate[i];       
  game := inprogress;
  writeln;
  writeln(Title);
  writeln;
{}  REPEAT
      warnings;
      Case WantToShoot of
	TRUE:	If game<>youlost then Doshot;
	FALSE:	If game<>youlost then DoMove
      End{of case};
{}  Until game<>inprogress;
  If game = youwon then
    writeln('Hee Hee Hee - The Wumpus''ll getcha next time.')
  Else
    writeln('Ha Ha Ha - You lose!');
end{ huntthewumpus };
 
Function newsetup: boolean;
begin
  newsetup := QUIRY('Same setup (y-n) ');
end;

Function newcave: boolean;
begin
  newcave := QUIRY('Same cave (y-n) ');
end;

Function  alldone : boolean; 
begin
  alldone := Quiry('Play again (y-n) ');
end;
 
begin{ Main Program Wumpus }
  terminit; 
  Instruct;
  Repeat
     getacave;
     If fatal_error then{ABORT}goto 99;
     Repeat 
       initsetup;
       Repeat
	 HuntTheWumpus;
       Until  newsetup;
     Until  newcave
  Until  alldone;
99:{ABORT}
End{of Wumpus}.

