home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / simtel / sigm / vols000 / vol069 / legame.pas < prev    next >
Pascal/Delphi Source File  |  1984-04-29  |  12KB  |  473 lines

  1. PROGRAM LeGame;
  2. {+
  3. ++ PROGRAM TITLE:    THE GAME
  4. ++ WRITTEN BY:        RAYMOND E. PENLEY
  5. ++
  6. ++ DATE WRITTEN:    AUGUST 7, 1981
  7. ++
  8. ++  COPYRIGHT (c) AUGUST 1981 by Raymond E. Penley
  9. ++  Permission to copy, modify and distribute, except for profit,
  10. ++  is hereby granted.
  11. ++
  12. ++ SUMMARY:
  13. ++    LeGame is a real time simulation game with a very simple
  14. ++   objective: to move into the other player thus giving the
  15. ++   mover an increased score.
  16. ++    LeGame is a simple game that moves two players around on a
  17. ++   game board.  The game is enhanced by the presence of a third
  18. ++   player, the Ghost, on the board.    The ghost player always moves
  19. ++   into a player and thereby causes that player to lose all his score.
  20. ++    The game is over in 1000 rounds or may be terminated by a
  21. ++   control-a key press.
  22. ++    The keys that control players movements are:
  23. ++
  24. ++         PLAYER "+"  PLAYER "*"
  25. ++         ----------  ----------
  26. ++           Q W E       I O P
  27. ++        \!/        \!/
  28. ++          A--S--D      J--K--L
  29. ++        /!\        /!\
  30. ++           Z X C       N M ,
  31. ++
  32. ++ NOTES:
  33. ++    The file 'TERMIO.PAS' contains terminal IO routines.  To use
  34. ++  TERMIO.PAS in your program, study the file TERMIO.PAS and
  35. ++  include those routines necessary into your source program.    There
  36. ++  are a couple of routines in TERMIO.PAS that may be included in
  37. ++  your Pascal source program at compile time: writes() and INITTERM.
  38. ++  Edit TERMIO.PAS and create a new file called TERMIO.LIB, then include
  39. ++  TERMIO.LIB in your Pascal program.    The Pascal/Z compiler will include
  40. ++  the source text as it compiles the main program.
  41. ++    All external modules may be found in the Pascal/Z Users' Group's
  42. ++  very useful library: ASL.REL [A Small Library].
  43. ++    The module 'gotoxy(x,y)' is included as source text in 'TERMIO.PAS'.
  44. ++
  45. +}
  46. CONST
  47.   { DEFINE THE OUTER EDGE MARGINS }
  48.   LM = 10;        { left margin }
  49.   RM = 70;        { right margin }
  50.   TM =    1;        { top margin - remember: the top row is row 0 }
  51.   BM = 17;        { bottom margin }
  52.  
  53.   { DEFINE THE LIMITS OF THE PLAYING FIELD <THE GAME BOARD> }
  54.   BLM = LM+2;         { board left margin }
  55.   BRM = RM-2;         { board right margin }
  56.   BTM = TM+2;         { board top margin }
  57.   BBM = BM-2;         { board bottom margin }
  58.  
  59.   { DEFINE THE PLACEMENT FOR THE SCORE CARD }
  60.   CardRow = BM+2;
  61.   CardColumn = LM;
  62.   alphalen = 10;        {<<< terminal IO routines >>>}
  63.  
  64. TYPE
  65.   ACTION = ( NULL, EA, NE, NO, NW, WE, SW, SO, SE );
  66.   BYTE = 0..255;
  67.   alpha = array [0..alphalen] of byte;    {<<< terminal IO routines >>>}
  68.   PLAYERTYPE = RECORD
  69.          X,             { X-coordinates }
  70.          Y      : BYTE;    { Y-coordinates }
  71.          CH      : CHAR;    { Players identification }
  72.          STATE      : ACTION;  { STATED ACTION }
  73.          SCORE      : INTEGER;
  74.            END;
  75.   STRING3 = PACKED ARRAY [1..3] OF CHAR;   { FOR TERMINAL STRINGS }
  76.   STRING34 = PACKED ARRAY [1..34] OF CHAR;
  77.  
  78. VAR
  79.   ASET,         { first set of keyboard control keys }
  80.   BSET : SET OF CHAR;    { second set of keyboard control keys }
  81.   COUNT : INTEGER;    { count number of rounds played }
  82.   EXT : BYTE;        { direct console character }
  83.   FACTOR : INTEGER;    { DELAY FACTOR }
  84.   GAMEOVER : BOOLEAN;
  85.   INCHAR : CHAR;    { global input character }
  86.   LASTMOVE : INTEGER;
  87.   mover : byte;     { players turn to move }
  88.   PLAYER_ONE,
  89.   PLAYER_TWO,
  90.   GHOST       : PLAYERTYPE;
  91.   SEED : REAL;        { for random numbers }
  92.  
  93. {$iTERMIO.VAR <<<terminal specific variables>>>}
  94.  
  95.  
  96.  
  97. FUNCTION CONCHAR: BYTE; EXTERNAL;
  98. { RETURNS A CONSOLE CHARACTER DIRECTLY WITH NO ECHO }
  99.  
  100.  
  101. PROCEDURE DREAD( VAR CH: CHAR );
  102. { BY USING CONCHAR WE CREATE A READ ROUTINE THAT WAITS
  103.   FOR A SINGLE KEYBOARD INPUT }
  104.  VAR     EXT: BYTE;
  105. BEGIN
  106.   REPEAT
  107.     EXT := CONCHAR;
  108.   UNTIL EXT<>0;
  109.   CH := CHR( EXT );
  110. END{ of DREAD };
  111.  
  112.  
  113. FUNCTION TOUPPER(CH: CHAR): CHAR; EXTERNAL;
  114. { RETURNS THE CHARACTER IN UPPERCASE }
  115.  
  116.  
  117. {$iTERMIO.LIB <<<procedures writes() and initterm>>>}
  118.  
  119.  
  120. FUNCTION RANDOM( VAR SEED: REAL ): REAL;
  121. { RETURNS RANDOM NUMBERS IN RANGE 0 - 1 }
  122. { GLOBAL:
  123.    SEED: REAL;
  124. }
  125. CONST PI = 3.14159;
  126. VAR    X: REAL;
  127. BEGIN
  128.   X := SEED + PI;
  129.   X := EXP(5.0 * LN(X));
  130.   SEED := X - TRUNC(X);
  131.   RANDOM := SEED
  132. END{ of RANDOM };
  133.  
  134.  
  135. PROCEDURE ERASE( VAR PLAYER: PLAYERTYPE );
  136. BEGIN
  137.   GOTOXY( PLAYER.X, PLAYER.Y );
  138.   WRITE(' ')
  139. END{ of ERASE };
  140.  
  141.  
  142. FUNCTION DIRECTION( A: INTEGER ): ACTION;
  143. {  RETURNS AN ACTION FROM A NUMERIC DIRECTION
  144.    ACCORDING TO THE FOLLOWING CONVENTION:
  145.       NO=3
  146.      NW=4 \ ! /NE=2
  147.        \!/
  148.    WEST=5<--+-->EAST=1
  149.        /!\
  150.     SW=6  / ! \ SE=8
  151.      SOUTH=7
  152. }
  153. BEGIN
  154.   CASE A OF
  155.     1: DIRECTION := EA;
  156.     2: DIRECTION := NE;
  157.     3: DIRECTION := NO;
  158.     4: DIRECTION := NW;
  159.     5: DIRECTION := WE;
  160.     6: DIRECTION := SW;
  161.     7: DIRECTION := SO;
  162.     8: DIRECTION := SE
  163.   END
  164. END{ of Direction };
  165.  
  166.  
  167. PROCEDURE Wappo( VAR PLAYER: PLAYERTYPE );
  168. { CHANGES PLAYERS DIRECTION AND COORDINATES }
  169. BEGIN
  170.   WITH PLAYER DO BEGIN
  171.     ERASE( PLAYER );
  172.     STATE := DIRECTION( TRUNC(8.0*RANDOM(SEED))+1 );
  173.     { ESTABLISH NEW COORDINATES }
  174.     X := TRUNC(BM*RANDOM(SEED)) + TM;
  175.     Y := TRUNC(RM*RANDOM(SEED)) + LM
  176.   END
  177. END{ of Wappo };
  178.  
  179.  
  180. Procedure ScoreCard;
  181. const    sp = '    ';
  182.  
  183.    procedure sc_a;
  184.    begin
  185.      writes(INVON);write(sp);writes(INVOFF);
  186.    end{ of sc_a };
  187.  
  188. begin
  189.   { write player two's score first }
  190.   gotoxy( (CardColumn+35),(CardRow+2) );
  191.   sc_a; write( PLAYER_TWO.SCORE:5, '000' ); sc_a;
  192.  
  193.   { now write score for player one leaving cursor in center of screen }
  194.   gotoxy( (CardColumn+5),(CardRow+2) );
  195.   sc_a; write( PLAYER_ONE.SCORE:5, '000' ); sc_a;
  196.  
  197.   gotoxy(40,(TM+5)); { pull cursor up out of the way }
  198. end{ of ScoreCard };
  199.  
  200.  
  201. PROCEDURE InitScoreBoard;
  202. const    blanks = '                  ';
  203. var    CC1, CC2 : byte;
  204.  
  205.    procedure init_b( x,y: byte );
  206.    begin
  207.      gotoxy( x,y );
  208.      writes(invon);write(blanks);writes(invoff);
  209.    end{ of init_b };
  210.  
  211. BEGIN
  212.   CC1 := CardColumn + 5;
  213.   CC2 := CardColumn + 35;
  214.   init_b( CC1, CardRow );
  215.   init_b( CC2, CardRow );
  216.  
  217.   gotoxy( CC1,(CardRow+1) );
  218.   writes(invon);write('    PLAYER (+)    ');writes(invoff);
  219.  
  220.   gotoxy( CC2,(CardRow+1) );
  221.   writes(invon);write('    PLAYER (*)    ');writes(invoff);
  222.  
  223.   ScoreCard;
  224.   init_b( CC1,(CardRow+3) );
  225.   init_b( CC2,(CardRow+3) );
  226. END{ of InitScoreBoard };
  227.  
  228.  
  229. PROCEDURE GENSCORE( VAR PLAYER: PLAYERTYPE );
  230.  
  231.    function hit( var a,b: playertype ): boolean;
  232.    { RETURNS TRUE IF BOTH PLAYERS HAVE THE SAME COORDINATES }
  233.    begin
  234.      hit := ((a.x=b.x) and (a.y=b.y))
  235.    end;
  236.  
  237. BEGIN
  238.   {  ARE ANY SQUARES OCCUPIED BY TWO PLAYERS    }
  239.   IF HIT( PLAYER_ONE, PLAYER_TWO ) THEN BEGIN
  240.      { GIVE PLAYER ON THE MOVE A BONUS SCORE }
  241.      PLAYER.SCORE := PLAYER.SCORE + 500;
  242.      Wappo( PLAYER_ONE );
  243.      Wappo( PLAYER_TWO );
  244.      ScoreCard;
  245.   END
  246.   ELSE BEGIN  { ARE ANY SQUARES OCCUPIED BY THE GHOST AND PLAYER 1 OR }
  247.     { THE GHOST AND PLAYER 2 }
  248.     IF HIT( GHOST, PLAYER_ONE ) THEN BEGIN
  249.       PLAYER_ONE.SCORE := 0;
  250.       Wappo( PLAYER_ONE );
  251.       wappo( ghost );
  252.       ScoreCard
  253.     END
  254.     ELSE IF HIT( GHOST, PLAYER_TWO ) THEN BEGIN
  255.       PLAYER_TWO.SCORE := 0;
  256.       Wappo( PLAYER_TWO );
  257.       wappo( ghost );
  258.       ScoreCard
  259.     END
  260.     ELSE
  261.       PLAYER.SCORE := PLAYER.SCORE + 1
  262.   END{ELSE}
  263. END{ of GenScore };
  264.  
  265.  
  266. FUNCTION GENSTATE( CH: CHAR ): ACTION;
  267. { GENERATES A NEW STATE DEPENDING UPON THE CHARACTER PASSED }
  268. { USING THE FOLLOWING CONVENTION:
  269.     PLAYER 1    PLAYER 2
  270.      Q W E     I O P
  271.       \!/      \!/
  272.     A--S--D    J--K--L
  273.       /!\      /!\
  274.      Z X C     N M ,
  275. }
  276. BEGIN
  277.   CASE TOUPPER(CH) OF
  278.    'S','K':    GENSTATE := NULL;
  279.    'D','L':    GENSTATE := EA;
  280.    'E','P':    GENSTATE := NE;
  281.    'W','O':    GENSTATE := NO;
  282.    'Q','I':    GENSTATE := NW;
  283.    'A','J':    GENSTATE := WE;
  284.    'Z','N':    GENSTATE := SW;
  285.    'X','M':    GENSTATE := SO;
  286.    'C',',','<': GENSTATE := SE
  287.   END
  288. END{ of GENSTATE };
  289.  
  290.  
  291. PROCEDURE GenMove( VAR PLAYER: PLAYERTYPE; ext: byte );
  292. var    ch: char;
  293.  
  294.    PROCEDURE DELAY( FACTOR: INTEGER);
  295.    VAR IX : INTEGER;
  296.    BEGIN
  297.      FOR IX:=1 TO FACTOR DO {DELAY}
  298.    END;
  299.  
  300. BEGIN
  301.   { if new character entered from keyboard then generate }
  302.   { a new direction for the player concerned }
  303.   if ( ext<>0 ) then begin
  304.      ch := chr(ext);
  305.      IF ( CH IN ASET ) THEN
  306.     PLAYER_ONE.STATE := GENSTATE(CH)
  307.      ELSE IF ( CH IN BSET ) THEN
  308.     PLAYER_TWO.STATE := GENSTATE(CH)
  309.   end;
  310.  
  311.   WITH PLAYER DO BEGIN
  312.     ERASE( PLAYER );
  313.  
  314.     CASE STATE OF
  315.       NULL: {HOLD PRESENT POSITION};
  316.     EA:  X := X + 1;
  317.     NE:  BEGIN X := X + 1; Y := Y - 1 END;
  318.     NO:  Y := Y - 1;
  319.     NW:  BEGIN X := X - 1; Y := Y - 1 END;
  320.     WE:  X := X - 1;
  321.     SW:  BEGIN X := X - 1; Y := Y + 1 END;
  322.     SO:  Y := Y + 1;
  323.     SE:  BEGIN X := X + 1; Y := Y + 1 END
  324.     END{CASE};
  325.  
  326.     { CHECK IF WE ARE MOVING OFF THE SCREEN }
  327.     IF ( Y>BBM ) THEN
  328.        Y := BTM
  329.     ELSE IF ( Y<BTM ) THEN
  330.        Y := BBM;
  331.     IF X>BRM THEN
  332.        X := BLM
  333.     ELSE IF X<BLM THEN
  334.        X := BRM;
  335.  
  336.     GOTOXY(X,Y); WRITE( CH )
  337.   END{WITH};
  338.  
  339.   GENSCORE( PLAYER );
  340.   DELAY(FACTOR)
  341. END{ of GenMove };
  342.  
  343.  
  344. PROCEDURE SIGN( TXT: STRING34 );
  345. CONST
  346.   border = '**********************************';
  347. begin
  348.   GOTOXY(25,7); { row=7 }
  349.   writes(invon);write(border);writes(invoff);
  350.   GOTOXY(25,8);
  351.   writes(invon);write(txt);writes(invoff);
  352.   GOTOXY(25,9);
  353.   writes(invon);write(border);writes(invoff);
  354. end{ of SIGN };
  355.  
  356.  
  357. PROCEDURE INITIALIZE;
  358. TYPE    MSTRING = STRING 255;
  359. VAR    IX: BYTE;
  360.  
  361.    PROCEDURE HALT( TXT: MSTRING ); EXTERNAL;
  362.  
  363. BEGIN
  364.   { INITIALIZE TERMINAL SPECIFIC VARIABLES }
  365.   IF NOT INITTERM THEN
  366.      HALT('File "TERMIO.FIL not found. Run INSTALL.');
  367.   COUNT := 0;        { ROUNDS COUNTER }
  368.   SEED := 4.0;        { THIS ISN'T TRULY RANDOM! }
  369.  
  370.   { init the first set of keyboard control keys }
  371.   ASET := ['q','Q','w','W','e','E',
  372.        'a','A','s','S','d','D',
  373.        'z','Z','x','X','c','C'];
  374.  
  375.   { init the second set of keyboard control keys }
  376.   BSET :=  ['i','I','o','O','p','P',
  377.         'j','J','k','K','l','L',
  378.         'n','N','m','M',',','<'];
  379.  
  380.   { clear the terminal screen and signon }
  381.   writes( CLRSCR );
  382.   SIGN( '***      T H E  G A M E        ***' );
  383.   WRITELN;WRITELN;WRITELN;
  384.  
  385.   { SET UP DELAY FACTOR }
  386.   WRITELN(' ':12, '1 - BEGINNING GAME');
  387.   WRITELN(' ':12, '2 - ADVANCED GAME');
  388.   WRITELN(' ':12, '3 - MASTER CRAFTSMAN');
  389.   WRITELN;
  390.   WRITE(' ':12, 'SELECT ->');
  391.   DREAD(inchar);
  392.   LASTMOVE := 5000;
  393.   case inchar of
  394.     '1': begin FACTOR := 500; lastmove := 1000 end;
  395.     '2': FACTOR := 250;
  396.     '3': FACTOR := 1;
  397.    ELSE: FACTOR := 50
  398.   end;
  399.  
  400.   writes( CLRSCR );
  401.   writes( CRSOFF ); { TURN CURSOR DISPLAY OFF ON TERMINALS THAT CAN DO SO. }
  402.  
  403.   { PLACE A BOARDER AROUND THE PLAYING FIELD }
  404.   for ix:=LM to RM do begin { top and bottom borders }
  405.       gotoxy(ix,TM); write('=');
  406.       gotoxy(ix,BM); write('=')
  407.     end;
  408.   for ix:=TM to BM do begin { left and right borders }
  409.     gotoxy(LM,ix); write('=');
  410.     gotoxy(RM,ix);write ('=')
  411.   end;
  412.  
  413.  
  414.   { INITIALIZE PLAYERS AND GHOST }
  415.   WITH GHOST DO BEGIN
  416.     CH := 'C';
  417.     SCORE := 0
  418.   END;
  419.   WITH PLAYER_ONE DO BEGIN
  420.     CH := '+';
  421.     SCORE := 0
  422.   END;
  423.   WITH PLAYER_TWO DO BEGIN
  424.     CH := '*';
  425.     SCORE := 0
  426.   END;
  427.  
  428.   InitScoreBoard;
  429.  
  430.   { PLACE THE BEGINNING MOVES }
  431.   Wappo( GHOST );    { FIRST - PICK RANDOM POINTS FOR PLACEMENT }
  432.   Wappo( PLAYER_ONE );
  433.   Wappo( PLAYER_TWO );
  434.  
  435.   GenMove( PLAYER_ONE, 0 );
  436.   GenMove( PLAYER_TWO, 0 );
  437.   GenMove( GHOST, 0 )
  438. END{ of Initialize };
  439.  
  440.  
  441.  
  442. BEGIN{ MAIN PROGRAM }
  443.   INITIALIZE;
  444.   GAMEOVER := FALSE;
  445.   mover := 1;
  446.   ext := 0; { preload ext to no character input }
  447.   WHILE not gameover do begin
  448.     if ext=1 then begin
  449.        gameover := true
  450.     end
  451.     else begin
  452.       case mover of
  453.       1: GenMove( player_one, ext );
  454.     2,4: GenMove( ghost, ext );
  455.       3: GenMove( player_two, ext )
  456.       end;
  457.       mover := mover + 1;
  458.       if mover>4 then mover := 1;
  459.       COUNT := COUNT + 1;
  460.       gameover := ( count>lastmove );
  461.       if ( count mod 6=0 ) then { TRY A NEW DIRECTION FOR THE GHOST }
  462.      GHOST.STATE := DIRECTION( TRUNC(8.0*RANDOM(SEED))+1 );
  463.       { keep reading the console }
  464.       ext := conchar
  465.     end {else}
  466.   END{WHILE};
  467.  
  468.   SIGN( '***      G A M E  O V E R      ***' );
  469.  
  470.   GOTOXY(0,0);
  471.   writes( CRSON )    { TURN CURSOR BACK ON }
  472. END{ of Program LeGame }.
  473.