{
This program is based on the MD LOTTO.  It was developed as a training
exercise that got out of hand and became a home project for a potentially
salable product.
}


PROGRAM LOTTERY(INPUT,OUTPUT);

{$I-}  {IGNORE I/O ERRORS)
{$R+}  {SET UP RANGE AND BOUNDS CHECKING}
     {GLOBAL CONSTANTS AND TYPES}

CONST
      RELNO = 1.003; {RELEASE NUMBER}
      NUMPIC = 6;
      MAXNUM = 40;
      TKTMAX = 200;

TYPE
      LOTNUMS = 1 .. MAXNUM;
      TKT = ARRAY [1..NUMPIC] OF LOTNUMS;
      TKTAR = ARRAY [1..TKTMAX] OF TKT;
      OPT = (Y,N);
      TICKETRECORD = RECORD TICKET:TKT; END;
      TKTFILE = FILE OF TICKETRECORD;
      FILENAME = STRING[32];
      DIRECTARRAY = ARRAY [1..100] OF FILENAME;
VAR
      WTKT,CTKT                                          : TKT;
      TKTS                                               : TKTAR;
      NUMTKTS, I, J, K, ARRAYSIZE                        : INTEGER;
      WINNERVALID                                        : BOOLEAN;
      PWPRINT, PWDISP, AUTOPRINT, AUTODISP               : OPT;
      TKTREC                                             : TICKETRECORD;
      INFILE, OUTFILE                                    : TKTFILE;
      STARTBYTE, POKEBYTE, NOWBYTE, OLDCON               : BYTE;

CONST {TYPED}
      IOVal    : Integer = 0;
      IOErr    : Boolean = False;

{
     These procedures CHIRP, BEEP, BEEPBEEP, HILOTONE, SIREN, and ALERT1
provide the bells and whistles that are used throughout the program.
}

PROCEDURE CHIRP;

BEGIN
SOUND (500);
DELAY (200);
NOSOUND;
END;


PROCEDURE BEEP;

BEGIN
SOUND(750);
DELAY(250);
NOSOUND;
END {PROC};


PROCEDURE BEEPBEEP(I:INTEGER);

VAR J:INTEGER;

BEGIN
FOR J := 1 TO I DO BEGIN BEEP; DELAY(175); END;
END {PROC};


PROCEDURE HILOTONE(I:INTEGER);

VAR J:INTEGER;

BEGIN
FOR J := 1 TO I DO BEGIN
   SOUND (1000);
   DELAY (500);
   NOSOUND;
   SOUND (500);
   DELAY (500);
   NOSOUND;
END {DO};
END {PROC};


PROCEDURE SIREN(I:INTEGER);

VAR J,K:INTEGER;

BEGIN
FOR J := 1 TO I DO BEGIN
   FOR K := 500  TO     2000 DO BEGIN SOUND(K);DELAY(1);END;
   FOR K := 2000 DOWNTO  500 DO BEGIN SOUND(K);DELAY(1);END;
END {DO};
NOSOUND;
END {PROC};

PROCEDURE YELP(I:INTEGER);

VAR J,K:INTEGER;

BEGIN
FOR J := 1 TO I DO BEGIN
   FOR K := 1 TO 500 DO BEGIN SOUND(2000 - (3 * K));DELAY(1);END;
   FOR K := 1 TO 500 DO BEGIN SOUND(2000 - (3 * K));DELAY(1);END;
END {DO};
NOSOUND;
END {PROC};


PROCEDURE ALERT1(I:INTEGER);

VAR J,K:INTEGER;

BEGIN
FOR J := 1 TO I DO BEGIN
   FOR K := 1 TO 500 DO BEGIN SOUND(500 + (3 * K));DELAY(1);END;
   FOR K := 1 TO 500 DO BEGIN SOUND(500 + (3 * K));DELAY(1);END;
END {DO};
NOSOUND;
END {PROC};


PROCEDURE HILITE;

BEGIN
TEXTCOLOR (YELLOW);
TEXTBACKGROUND (BLACK);
END;


PROCEDURE LOLITE;

BEGIN
TEXTCOLOR (YELLOW);
TEXTBACKGROUND (BLUE);
END;


PROCEDURE SCRNRESET; {GENERAL SCREEN RESET YELLOW ON BLUE}

BEGIN
WINDOW(1,1,80,25);
TEXTCOLOR (YELLOW);
TEXTBACKGROUND (BLUE);
CLRSCR;
END {PROC};

{ *** RANDOMIZE, IOCHECK AND DOS DIRECTORY CALL PROCEDURES ADAPTED FROM
      TURBO PASCAL 2.0 WITH PERMISSION OF BORLAND INTERNATIONAL AS
      STATED IN THEIR DOCUMENTATION
      }

procedure IOCheckA;
{
       This routine sets IOErr equal to IOresult, then sets
       IOFlag accordingly.  It is a subset of routine IOCHECK.
}
var
  Ch                   : Char;
begin
  IOVal := IOresult;
  IOErr := (IOVal <> 0);
end {proc};


procedure IOCheck;
{
       This routine sets IOErr equal to IOresult, then sets
       IOFlag accordingly.  It also prints out a message on
       the 25th line of the screen, then waits for the user
       to hit any character before proceding.
}
var
  Ch                   : Char;
begin
  IOVal := IOresult;
  IOErr := (IOVal <> 0);
  if IOErr then begin
    GoToXY(1,25); ClrEol; {CLEAR ANYTHING ON LINE 25}
    BEEPBEEP(2);
    case IOVal of
      $01  :  Write('File does not exist');
      $02  :  Write('File not open for input');
      $03  :  Write('File not open for output');
      $04  :  Write('File not open');
      $05  :  Write('Can''t read from this file');
      $06  :  Write('Can''t write to this file');
      $10  :  Write('Error in numeric format');
      $20  :  Write('Operation not allowed on a logical device');
      $21  :  Write('Not allowed in direct mode');
      $22  :  Write('Assign to standard files not allowed');
      $90  :  Write('Record length mismatch');
      $91  :  Write('Seek beyond end of file');
      $99  :  Write('Unexpected end of file');
      $F0  :  Write('Disk write error');
      $F1  :  Write('Directory is full');
      $F2  :  Write('File size overflow');
      $FF  :  Write('File disappeared')
    else      Write('Unknown I/O error:  ',IOVal:3)
    end{case};
    Read(Kbd,Ch);
    GoToXY(1,25);
    ClrEol;
  end{if};
end; { of proc IOCheck }


{
   Randomize Procedure For MS-DOS & PC-DOS Turbo Pascal

   This new Randomize has two Integer parameters.  If they are both 0, then
the random number seed is set randomly.  If either of the parameters is
nonzero, then they are both stored directly into the 32 bit seed.

   To set the seed randomly (Randomize(0,0)), the procedure calls MS-DOS
to get the current time.  This is a 32 bit value, which is also stored
directly into the seed.  On some systems, (i.e. the NCR Decision Mate V),
the clock does not tick, so the time never changes.  Randomize checks this,
and if the clock hasn't changed after a Delay(100), it asks the user to hit
a key.  While waiting for the key, it continuously increments two counters.
These are then stored into the seed.

{ Please note:  This routine is for MS-Dos/PC-Dos Turbo ONLY! }

procedure Randomize(I,J: Integer);

var
  RSet    : record
              AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags: Integer;
            end;
  Ch      : Char;

begin
  if (I=0) and (J=0) then begin     { Generate a random random number seed }
    RSet.AX:=$2C00;                             { DOS time of day function }
    MSDos(RSet);
    I:=RSet.CX;                           { Set I and J to the system time }
    J:=RSet.DX;
    Delay(100);   { This delay may have to be increased for faster systems }
    MSDos(RSet);
    if (I=RSet.CX) and (J=RSet.DX) then begin        { Clock isn't ticking }
      I := 0;
      J := 0;
      while KeyPressed do
        Read(Kbd,Ch);                              { Clear keyboard buffer }
      Write('Hit any key to set the random number generator: ');
      repeat
        I := I+13;
        J := J+17
      until Keypressed;
      Read(Kbd,Ch);                                 { Absorb the character }
      WriteLn
    end
  end;
  MemW[DSeg:$129]:=I;  { This is the core of the routine: store a 32 bit }
  MemW[DSeg:$12B]:=J;  {  seed at locations DSeg:$0129...DSeg:$012C      }
end; { of procedure Randomize }


PROCEDURE DirList (VAR DirArray  : DirectArray;
                   VAR ArraySize : INTEGER);

{
       This is a simple procedure to build an array of names
       out the directory of the current (logged) drive.
}
type
  Char12arr            = array [ 1..12 ] of Char;
  String20             = string[ 20 ];
  RegRec =
    record
      AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags : Integer;
    end;

var
  Regs                 : RegRec;
  DTA                  : array [ 1..43 ] of Byte;
  Mask                 : Char12arr;
  NamR                 : String20;
  Error, I, KK         : Integer;

begin { main body of procedure DirList }

  ArraySize := 0;
  FOR KK := 1 TO 100 DO DIRARRAY[KK] := '';
  FillChar(DTA,SizeOf(DTA),0);        { Initialize the DTA buffer }
  FillChar(Mask,SizeOf(Mask),0);      { Initialize the mask }
  FillChar(NamR,SizeOf(NamR),0);      { Initialize the file name }

  Regs.AX := $1A00;         { Function used to set the DTA }
  Regs.DS := Seg(DTA);      { store the parameter segment in DS }
  Regs.DX := Ofs(DTA);      {   "    "      "     offset in DX }
  MSDos(Regs);              { Set DTA location }
  Error := 0;
  Mask := '????????.LFD';    { Use global search }
  Regs.AX := $4E00;          { Get first directory entry }
  Regs.DS := Seg(Mask);      { Point to the file Mask }
  Regs.DX := Ofs(Mask);
  Regs.CX := 22;             { Store the option }
  MSDos(Regs);               { Execute MSDos call }
  Error := Regs.AX and $FF;  { Get Error return }
  I := 1;                    { initialize 'I' to the first element }
  if (Error = 0) then BEGIN
    repeat
      NamR[I] := Chr(Mem[Seg(DTA):Ofs(DTA)+29+I]);
      I := I + 1;
    until not (NamR[I-1] in [' '..'~']) or (I>20);

    NamR[0] := Chr(I-1);          { set string length because assigning }
                                  { by element does not set length }
    ArraySize := 1;
    DirArray[ArraySize] := NAMR;
  END{IF};
  while (Error = 0) do begin
    Error := 0;
    Regs.AX := $4F00;           { Function used to get the next }
                                { directory entry }
    Regs.CX := 22;              { Set the file option }
    MSDos( Regs );              { Call MSDos }
    Error := Regs.AX and $FF;   { get the Error return }
    I := 1;
    repeat
      NamR[I] := Chr(Mem[Seg(DTA):Ofs(DTA)+29+I]);
      I := I + 1;
    until not (NamR[I-1] in [' '..'~'] ) or (I > 20);
    NamR[0] := Chr(I-1);
    if (Error = 0) THEN BEGIN
      ArraySize := ArraySize + 1;
      DirArray[ArraySize] := NAMR;
    END {IF};
  end{WHILE};
end{ of procedure DirList  };

{
This procedure outputs the array generated in Dirlist and generates the
user screen display in 6 wide format.
}


PROCEDURE DISPDIR;

LABEL
    EXIT;

VAR
    DIRARRAY                      : DIRECTARRAY;
    ARRAYSIZE, I, J, K, M         : INTEGER;
    CH                            : CHAR;

BEGIN
SCRNRESET;
DIRLIST(DIRARRAY,ARRAYSIZE);
GOTOXY (33,2);
WRITELN ('LIST OF FILES');
WRITELN;
IF ARRAYSIZE < 1 THEN GOTO EXIT;

                  {PRINT 6 WIDE WITHOUT THE .LPD SUFFIX}

I := 1;
J := 6;
REPEAT
  IF J > ARRAYSIZE THEN J := ARRAYSIZE;  {MAKE SURE NOT TO PRINT TOO MANY}
  FOR K := I TO J DO BEGIN
    WHILE ((LENGTH (DIRARRAY[K]) > 0) AND (DIRARRAY[K][1] = ' ')) DO BEGIN
       DELETE (DIRARRAY[K],1,1); {TRIM LEADING BLANKS}
    END {DO};
    WHILE ((LENGTH (DIRARRAY[K]) > 0)
            AND (DIRARRAY[K][LENGTH(DIRARRAY[K])] = ' ')) DO BEGIN
      DELETE (DIRARRAY[K], (LENGTH(DIRARRAY[K])), 1); {TRIM TRAILING BLANKS}
    END {DO};
                    {TRIM TO SHOW FILE NAME ONLY}
    IF LENGTH (DIRARRAY[K]) > 8 THEN DELETE (DIRARRAY[K], 9, 32);
    M := POS ('.',DIRARRAY[K]);
    IF M > 0 THEN DELETE (DIRARRAY[K], M, 8);
    IF K = I THEN
       WRITE (DIRARRAY[K]:15) {WRITE IN A 15 COLUMN FIELD}
    ELSE
       WRITE (DIRARRAY[K]:12) {WRITE IN A 15 COLUMN FIELD}
    {ENDIF};
  END {DO};
  WRITELN;
  I := I + 6; {INCREMENT LINE POINTER}
  J := I + 5;
UNTIL I > ARRAYSIZE;
WRITELN;
WRITELN;
GOTOXY (28,WHEREY);
WRITELN ('PRESS ANY KEY TO CONTINUE');
READ (KBD,CH);
CLRSCR;
EXIT: END {PROC};


{
This procedure initializes the major common variables of the program and
effectively acts as a data reset function.
}

PROCEDURE REINIT;

VAR I, J : INTEGER;

BEGIN
WINNERVALID := FALSE;
NUMTKTS := 0;
FOR I := 1 TO TKTMAX DO FOR J := 1 TO NUMPIC DO TKTS[I,J] := MAXNUM;
FOR I := 1 TO NUMPIC DO WTKT[I] := MAXNUM;
END{PROC};



{
This procedure allows the changing of the print and display options for the
program.

It uses a case procedure to toggle the control variable for each parameter.

A test for value 0 is used for termination and return to the main program.
}

PROCEDURE OPTMENU;

VAR ANSWER : INTEGER;

BEGIN
REPEAT
   SCRNRESET;
   GOTOXY (34,2);
   WRITELN ('OPTIONS MENU');
   GOTOXY (1,5);
   WRITELN ('0.  EXIT MEMU');
   WRITELN;
   IF PWPRINT = Y THEN BEGIN
      WRITELN ('1.  PRINT WINNERS WHEN FOUND   = YES.');END
   ELSE  BEGIN
      WRITELN ('1.  PRINT WINNERS WHEN FOUND   = NO.');
   END{IF};
   IF PWDISP = Y THEN BEGIN
      WRITELN ('2.  DISPLAY WINNERS WHEN FOUND = YES.');END
   ELSE BEGIN
      WRITELN ('2.  DISPLAY WINNERS WHEN FOUND = NO.');
   END{IF};
   WRITELN;
   IF AUTOPRINT = Y THEN BEGIN
      WRITELN ('3.  AUTOPRINT TICKETS   = YES.');END
   ELSE BEGIN
      WRITELN ('3.  AUTOPRINT TICKETS   = NO.');
   END{IF};
   IF AUTODISP = Y THEN BEGIN
      WRITELN ('4.  AUTODISPLAY TICKETS = YES.');END
   ELSE BEGIN
      WRITELN ('4.  AUTODISPLAY TICKETS = NO.');
   END{IF};
   GOTOXY (10,20);
   WRITE ('ENTER SELECTION TO CHANGE.   ');
   ANSWER := 30; {STORE DEFAULT VALUE TO CAUSE RECYCLE}
   READLN (ANSWER);
   IOCHECKA;
   IF IOERR = TRUE THEN ANSWER := 30; {ON ERROR RELOAD INVALID ANSWER}
   CASE ANSWER OF
   0 :  {NO OPERATION};
   1 :  IF PWPRINT = Y THEN PWPRINT := N ELSE PWPRINT := Y;
   2 :  IF PWDISP = Y THEN PWDISP := N ELSE PWDISP := Y;
   3 :  IF AUTOPRINT = Y THEN AUTOPRINT := N ELSE AUTOPRINT := Y;
   4 :  IF AUTODISP = Y THEN AUTODISP := N ELSE AUTODISP := Y;
   ELSE BEEP
END{CASE};
UNTIL ANSWER = 0;
END{PROC};


{
This procedure compares two tickets and keeps track of the number of matches.
As it is called very, very frequently, quick end tests are made to cut the
number of comparisons made to a minimum.  If 3 misses on a ticket are
accumulated, the tickets cannot be matched and the comparison terminates.
Win is set to the number of matches if 4 or more matches occur.  Otherwise
a 0 is returned.
}



PROCEDURE COMPARE(TICK1,TICK2               :TKT;
                  VAR WIN                   :INTEGER);

VAR   POINT1,POINT2,MISS1,MISS2,HIT         :INTEGER;
      DONE                                  :BOOLEAN;

BEGIN
               {INITIALIZE VARIABLES}
POINT1 := 1 ;
POINT2 := 1 ;
WIN    := 0 ;
MISS1  := 0 ;
MISS2  := 0 ;
HIT    := 0 ;

               {BEGIN EXAMINING THE TICKETS FOR A MATCH}

DONE   := FALSE;
REPEAT
  IF (TICK1[POINT1] = TICK2[POINT2]) THEN  {COMPARE NUMBER ON EACH TICKET}
     BEGIN {TRUE}
     HIT := HIT + 1                        {A HIT, TRY FOR 6};
     POINT1 := POINT1 + 1 ;                {INDEXING POINTERS}
     POINT2 := POINT2 + 1 ;
     END {TRUE BRANCH}
  ELSE                                      {A MISS}
     BEGIN {FALSE PATH}
     {INDEX MISS COUNT AND POINTER OF TICKET WITH SMALLEST NUMBER}
     IF (TICK1[POINT1] > TICK2[POINT2]) THEN
        BEGIN {A MISS ON TICKET 2}
        MISS2  := MISS2  + 1 ;
        POINT2 := POINT2 + 1 ;
        END {TICKET 2 MISS}
     ELSE
        BEGIN {MISS ON TICKET 1}
        MISS1  := MISS1  + 1 ;
        POINT1 := POINT1 + 1 ;
        END {TICKET 1 MISS}
     {ENDIF}
     END {FALSE PATH}
  {ENDIF};

  {TEST FOR DONE, 3 MISSES ON A TICKET OR OUT OF NUMBERS TO COMPARE}
  IF ((MISS1 > 2) OR (MISS2 > 2) OR (POINT1 > NUMPIC) OR (POINT2 > NUMPIC))
     THEN DONE := TRUE;
UNTIL (DONE = TRUE);
{TEST AND REPORT A WIN IF OVER 3 HITS}
IF (HIT > 3) THEN WIN := HIT;
END;


{
This procedure will print or display winning tickets based on the option
variables.  A variety of bells and whistles are used to alert various
levels of wins.  If PWDISP and PWPRINT are both N this routine will produce
no output.
}

PROCEDURE PWIN(TKTNO,WINSIZE        :INTEGER;
               PTKT,WTKT            :TKT;
               PWPRINT,PWDISP       :OPT);


VAR  I                              : INTEGER;

BEGIN

HILITE;
CLRSCR;

IF PWDISP = Y THEN {WRITE TO SCREEN}
BEGIN
   WRITELN;
   WRITELN ('   !!!   YOU HAVE A WINNER   !!!   ');
   FOR I := 1 TO 3 DO WRITELN;
   WRITELN ('TICKET NO: ',TKTNO:4,'.');
   WRITELN;
   WRITELN ('WINSIZE:',WINSIZE:4,'.');
   WRITELN;
   WRITE ('PICK Nos:');
   FOR I := 1 TO NUMPIC DO WRITE (PTKT[I]:6);
   WRITELN;
   WRITELN;
   WRITE ('THE LOTTO DRAW WAS:');
   FOR I:= 1 TO NUMPIC DO WRITE (WTKT[I]:6);
   WRITELN;
   WRITELN;
   CASE WINSIZE OF {CELEBRATION WORDS AND MUSIC}
   4 : BEEPBEEP(3);
   5 : BEGIN
          WRITELN ('   !!!   AND IT`S A BIG ONE   !!!');
          YELP(3);
       END;
   6 : BEGIN
          WRITELN ('          !!!  YOU`RE RICH  !!!');
          WRITELN;
          WRITELN ('RICH I TELL YOU!...  RICH RICH RICH !!!!');
          WRITELN;
          WRITELN ('RETIRE NOW, AVOID THE RUSH');
          WRITELN;
          WRITELN ('YOU MAY EXIT THE PROGRAM AND SHUT OFF THE COMPUTER');
          WRITELN;
          WRITELN ('              WHEN YOU WAKE UP');
          SIREN(2);
          DELAY(10);
          YELP(3);
          DELAY(10);
          ALERT1(3);
          DELAY(10);
       END;
   END{CASE};
   GOTOXY(1,25);
   CLREOL;
   WRITE ('PRESS ANY KEY TO CONTINUE');
   REPEAT BEGIN END UNTIL KEYPRESSED;
   READ (KBD);
   CLRSCR;
END {IF};

IF PWPRINT = Y THEN {WRITE TO PRINTER}
BEGIN
   WRITELN (LST);
   WRITELN (LST,'   !!!   YOU HAVE A WINNER   !!!   ');
   FOR I := 1 TO 3 DO WRITELN (LST);
   WRITELN (LST,'TICKET NO: ',TKTNO:4,'.');
   WRITELN (LST);
   WRITELN (LST,'WINSIZE:',WINSIZE:4,'.');
   WRITELN (LST);
   WRITE (LST,'PICK Nos:');
   FOR I := 1 TO NUMPIC DO WRITE (LST,PTKT[I]:6);
   WRITELN (LST);
   WRITELN (LST);
   WRITE (LST,'THE LOTTO DRAW WAS:');
   FOR I:= 1 TO NUMPIC DO WRITE (LST,WTKT[I]:6);
   FOR I := 1 TO 4 DO WRITELN (LST);
   WRITELN (LST);
   WRITELN (LST);
   IF ((PWDISP = N) AND (PWPRINT = Y)) THEN BEGIN { WHEN PRINTING ONLY}
      CASE WINSIZE OF {CELEBRATION WORDS AND MUSIC}
      4 : BEEPBEEP(3);
      5 : BEGIN
             WRITELN (LST,'   !!!   AND IT`S A BIG ONE   !!!');
             YELP(3);
          END;
      6 : BEGIN
             WRITELN (LST,'          !!!  YOU`RE RICH  !!!');
             WRITELN (LST);
             WRITELN (LST,'RICH I TELL YOU!...  RICH RICH RICH !!!!');
             WRITELN (LST);
             WRITELN (LST,'RETIRE NOW, AVOID THE RUSH');
             WRITELN (LST);
             WRITELN (LST,'YOU MAY EXIT THE PROGRAM AND SHUT OFF THE COMPUTER');
             WRITELN (LST,'              WHEN YOU WAKE UP');
             SIREN(2);
             DELAY(10);
             YELP(3);
             DELAY(10);
             ALERT1(3);
             DELAY(10);
          END;
      END{CASE};
   END{IF};
   FOR I := 1 TO 2 DO WRITELN (LST);
END{IF};
END{PROC};

{
This procedure uses the compare procedure to test for winning tickets,
and then calls pwin in case of winners to print out the winners to screen or
printer.  A summary of the tickets scanned is displayed at the end of the
procedure.
}

PROCEDURE SCANTKTS;

VAR
   I, WIN, WIN4, WIN5, WIN6, LOSERS        : INTEGER;
   CTKT                                    : TKT;

BEGIN {PROC}
{INITIALIZE VARIABLES}
WIN4   := 0;
WIN5   := 0;
WIN6   := 0;
LOSERS := 0;
SCRNRESET;
FOR I := 1 TO NUMTKTS DO
    BEGIN {DO}
    CTKT := TKTS[I];                {SELECT A TICKET}
    COMPARE(CTKT,WTKT,WIN);  {COMPARE WITH WINNING NOS.}
    IF (WIN > 3) THEN               {TEST FOR A WINNER, WIN>3}
       BEGIN {TRUE}
       PWIN(I,WIN,CTKT,WTKT,PWPRINT,PWDISP); {PRINT WINNING TICKET}
       CASE WIN OF
          4:   WIN4 := WIN4 + 1;
          5:   WIN5 := WIN5 + 1;
          6:   WIN6 := WIN6 + 1;
       END {CASE}
       END {TRUE}
    ELSE
       LOSERS := LOSERS + 1
    {ENDIF}
END {DO} ;
SCRNRESET;
WINDOW (3,3,77,22);
HILITE;
CLRSCR;
WINDOW (4,3,77,22);
WRITELN;
WRITELN ('THERE WERE ',NUMTKTS,' TICKETS CHECKED.');
WRITELN;
WRITELN ('THERE WERE ',WIN4,' TICKET(S) WITH 4 MATCHING NUMBERS.');
WRITELN;
WRITELN ('THERE WERE ',WIN5,' TICKET(S) WITH 5 MATCHING NUMBERS.');
WRITELN;
WRITELN ('THERE WERE ',WIN6,' JACKPOT TICKET(S).');
WRITELN;
WRITELN ('THERE WERE ',LOSERS,' LOSERS.');
GOTOXY (10,20);
WRITE ('PRESS ANY KEY TO CONTINUE');
REPEAT UNTIL KEYPRESSED;
READ (KBD);
SCRNRESET;
END {PROC};

{
This procedure sorts the elements of a ticket into ascending order
}

PROCEDURE SORTPICK (VAR STKT:TKT);

VAR
   I, J, TEMP           : INTEGER;

BEGIN

FOR I := 1 TO (NUMPIC - 1) DO BEGIN
   FOR J := (I + 1) TO NUMPIC DO BEGIN
      IF (STKT[I] > STKT[J]) THEN BEGIN
         TEMP := STKT[I];
         STKT[I] := STKT[J];
         STKT[J] := TEMP;
      END {IF};
   END {DO};
END {DO};
END; {PROC}


{
This procedure generates a ticket using the random number generator.
Nupic number of pics are generated.  The ticket is sorted, and checked
for duplicates.  If no duplicates are found then the ticket is accepted.
Otherwise, a new number is issued for one of the duplicates and the new
ticket is retested.
}
PROCEDURE GENTKT (VAR RNDTKT:TKT);

VAR I,J,TEMP           :INTEGER;
    FAULT              :BOOLEAN;

BEGIN {PROC}

FOR I := 1 TO NUMPIC DO RNDTKT[I] := (RANDOM(MAXNUM)) + 1;
REPEAT
   SORTPICK (RNDTKT); {SORT THE ENTRYS}
   FAULT := FALSE;
   FOR I := 1 TO (NUMPIC - 1) DO BEGIN {CHECK FOR INVALID TICKET,
                              i.e. DUPLICATE PICK NUMBERS}
      J := I + 1;
      IF (RNDTKT[I] = RNDTKT[J]) THEN BEGIN {DUPLICATE FOUND}
        FAULT := TRUE; {SET FOR RECHECK}
        RNDTKT[J] := (RANDOM(MAXNUM)) + 1; {REPLACE WITH NEW PICK}
      END {IF}
   END; {DO}
UNTIL FAULT = FALSE;
END; {PROC}

{
This procedure generates a complete set of tickets for a simulation run.
The value of numtkts is used to determine the number of tickets to generate.
}


PROCEDURE SIMULATE;

VAR I : INTEGER;

BEGIN {PROC}
FOR I := 1 TO NUMTKTS DO GENTKT(TKTS[I]); {GENERATE NUMTKTS NUMBER OF RANDOM
                                            LOTTERY TICKETS}
GENTKT(WTKT); {GENERATE WINNING TICKET}
WINNERVALID := TRUE;
END; {PROC}


{
This procedure generates a screen display of the tickets including any valid
winning draw in the ticket data set.
}

PROCEDURE DISPTKTS;

VAR I, J, LINECOUNT, PCOUNT    :INTEGER;

BEGIN {PROC}

SCRNRESET;
IF WinnerValid = TRUE THEN BEGIN  {display the winning ticket}
   WRITELN ('THE WINNING TICKET IS:');
   I := 0;
   WRITE ('TKT NO. ',I:4,'::::');
   FOR J := 1 TO NUMPIC DO WRITE (WTKT[J]:6);
   WRITELN;
   WRITELN;
   WRITELN('YOUR TICKET PICKS ARE:');
   WRITELN;
   LINECOUNT := 7;
   END
ELSE
   LINECOUNT := 0
{ENDIF};
PCOUNT := 0;
FOR I := 1 TO NUMTKTS DO BEGIN           {print out the tickets}
   WRITE ('TKT NO. ',I:4,'::::');
   FOR J := 1 TO NUMPIC DO WRITE (TKTS[I,J]:6);
   WRITELN;
   PCOUNT := PCOUNT + 1;
   LINECOUNT := LINECOUNT + 1;
   IF ((PCOUNT MOD 5 = 0) AND (I < NUMTKTS)) THEN BEGIN {IF}
      WRITELN;
      PCOUNT := 0;
      LINECOUNT := LINECOUNT + 1;
      IF LINECOUNT > 18 THEN BEGIN {IF2} {screen full test}
         GOTOXY(1,25);
         HILITE;
         CLREOL;
         WRITE ('   ***  SCREEN FULL, PRESS ANY KEY TO CONTINUE   ***   ');
         REPEAT UNTIL KEYPRESSED; {WAIT FOR KEYSTROKE}
         READ (KBD);
         SCRNRESET;
         LINECOUNT := 0;
         END
      {END IF2};
      END
   {END IF};
END {DO};
GOTOXY(1,25);
HILITE;
CLREOL;
WRITE ('   ***   END OF ENTRIES, PRESS ANY KEY TO CONTINUE   ***   ');
REPEAT UNTIL KEYPRESSED; {WAIT FOR KEYSTROKE}
READ (KBD);
SCRNRESET;
END {PROC};


{
This procedure is used to build a ticket entry from the keyboard.
}

PROCEDURE BUILDTKT (VAR BTKT : TKT; VAR ABORT:BOOLEAN);

LABEL EXIT;

VAR
    I, ENTRY, J                             : INTEGER;
    DONE, DONE2, DONE3, DONE4               : BOOLEAN;
    ANSWER                                  : CHAR;

BEGIN
DONE := FALSE;
ABORT := FALSE;
REPEAT              {UNTIL VALID TICKET OR ABORT}
   FOR I := 1 TO NUMPIC DO BEGIN
      DONE2 := FALSE;
      REPEAT                 {UNTIL VALID ENTRY OR ABORT}
         GOTOXY(5,5);
         CLREOL;
         WRITE ('PICK NO.',I:2,' (0 TO QUIT)? ');
         ENTRY := -1; {SET DEFAULT}
         READLN (ENTRY);
         IOCHECKA;
         IF IOERR = TRUE THEN ENTRY := -1; {RESET DEFAULT ON I/O ERROR}
         CASE ENTRY OF           {TEST ENTRY}
         0           : BEGIN     {ABORT ENTRY}
                       ABORT := TRUE;
                       GOTO EXIT;
                       END;
         1..MAXNUM   : BEGIN     {VALID ENTRY}
                       DONE2 := TRUE;
                       BTKT[I] := ENTRY;
                       END;
         ELSE CHIRP
         END{CASE};
      UNTIL DONE2 = TRUE;
      GOTOXY (2,10);   {SELECT ECHO}
      CLREOL;
      WRITE ('YOU HAVE PICKED:');
      FOR J := 1 TO I DO WRITE (BTKT[J]:5);
      WRITELN;
   END{DO};
   SORTPICK (BTKT);           {SORT ENTRYS}
   DONE3 := TRUE;             {TEST FOR VALID TICKET}
   FOR I := 1 TO (NUMPIC - 1) DO BEGIN
      J := I + 1;
      IF BTKT[I] = BTKT[J] THEN DONE3 := FALSE;      {= MEANS INVALID TICKET}
   END{DO};
   DONE4 := TRUE;
   IF DONE3 = TRUE THEN BEGIN
      CLRSCR; 
      GOTOXY (2,10); {ECHO BACK SORTED CHOICE}
      WRITE ('YOU HAVE PICKED:');
      FOR J := 1 TO NUMPIC DO WRITE (BTKT[J]:5);
      WRITELN;
      GOTOXY (2,15);   {PLACE PROMPT ON SCREEN}
      CLREOL;
      WRITE ('IS THIS CORRECT (Y/N)? ');
      REPEAT
         ANSWER := 'Z'; {SET DEFAULT}
         READ (KBD,ANSWER);
         IOCHECKA;
         IF IOERR = TRUE THEN ANSWER := 'Z'; {RESET DEFAULT ON I/O ERROR}
         ANSWER := UPCASE(ANSWER);
         IF (ANSWER IN ['Y','N']) = FALSE THEN BEEPBEEP(2);
      UNTIL ANSWER IN ['Y','N'];
      CLRSCR; {CLEAR ECHO AND PROMPT}
      IF ANSWER = 'N' THEN DONE4 := FALSE;
      END
   ELSE BEGIN
      GOTOXY(2,10);
      CLREOL;
      HILITE;
      WRITE ('   ***   INVALID NUMBER SELECTION, RETRY   ***   ');
      BEEPBEEP(3);
      DELAY(1500);
      LOLITE;
      GOTOXY(2,10);
      CLREOL;
      DONE4 := FALSE;
   END{IF};
   DONE := DONE2 AND DONE3 AND DONE4;
UNTIL DONE = TRUE;
EXIT : END{PROC};

{
This procedure will generate a random winning draw, or a manually entered
winning draw.  It will also erase the winning draw.
}


PROCEDURE BUILDWIN;

VAR BTKT        : TKT;
    ABORT       : BOOLEAN;
    I           : INTEGER;
    ANSWER      : CHAR;

BEGIN
SCRNRESET;
GOTOXY(1,3);
WRITE ('PRESS ');
HILITE;
WRITE ('A');
LOLITE;
WRITELN ('FOR ABORT.');
WRITE ('PRESS ');
HILITE;
WRITE ('R');
LOLITE;
WRITELN ('FOR RANDOM SELECTION OF WINNING DRAW.');
WRITE ('PRESS ');
HILITE;
WRITE ('E');
LOLITE;
WRITELN ('TO ENTER WINNING DRAW FROM KEYBOARD.');
WRITE ('PRESS ');
HILITE;
WRITE ('W');
LOLITE;
WRITELN ('TO ERASE WINNING DRAW.');
GOTOXY(1,10);
WRITE ('ENTER YOUR CHOICE (A,R,E,W)? ');
REPEAT
   ANSWER := 'Z'; {SET DEFAULT}
   READ (KBD,ANSWER);
   IOCHECKA;
   IF IOERR = TRUE THEN ANSWER := 'Z'; {RESET DEFAULT ON I/O ERROR}
   ANSWER := UPCASE (ANSWER);
   IF (ANSWER IN ['A','R','E','W']) = FALSE THEN BEEP;
UNTIL ANSWER IN ['A','R','E','W'];
WRITELN (ANSWER);  {ECHO ACCEPTED ANSWER}
DELAY(500); {LET THE USER SEE IT}
CASE ANSWER OF
'R' : BEGIN
         GENTKT(WTKT);
         WINNERVALID := TRUE;
      END;
'E' : BEGIN
         SCRNRESET;
         BUILDTKT(BTKT,ABORT);
         IF ABORT = FALSE THEN BEGIN
            WTKT := BTKT;
            WINNERVALID := TRUE;
         END{IF};
      END;
'W' : BEGIN
         FOR I := 1 TO NUMPIC DO WTKT[I] := MAXNUM; {FILL WITH NULL PATTERN}
         WINNERVALID := FALSE;
      END;
END{CASE};
END{PROC};


{
This procedure is called from the main menu to build a series of tickets from
the keyboard.  It calls BUILDTKT repeatedly.
}

PROCEDURE ADDTKTS;

VAR BTKT         : TKT;
    ABORT        : BOOLEAN;
    GOMAX        : INTEGER;

BEGIN
SCRNRESET;
REPEAT
   GOMAX := TKTMAX - NUMTKTS;
   GOTOXY (2,2);
   CLREOL;
   WRITE ('YOU MAY ENTER UP TO',GOMAX:4,' ADDITIONAL ENTRIES.');
   BUILDTKT(BTKT,ABORT);
   IF ABORT = FALSE THEN BEGIN
      NUMTKTS := NUMTKTS + 1;
      TKTS[NUMTKTS] := BTKT;
   END{IF};
UNTIL ((ABORT = TRUE) OR (NUMTKTS >= TKTMAX))
END{PROC};


{
This procedure will add a number of random tickets to the data set based on
input from the keyboard.  Procedure GENTKT is called to generate each ticket.
}

PROCEDURE ADDRANDUM;

VAR MAXGO, KOUNT, I           : INTEGER;
    RNDTKT                    : TKT;
    DONE                      : BOOLEAN;

BEGIN
MAXGO := TKTMAX - NUMTKTS;
SCRNRESET;
GOTOXY (5,2);
WRITELN ('YOU MAY REQUEST UP TO',MAXGO:4,' TICKETS.');
DONE := FALSE;
REPEAT
   GOTOXY (5,5);
   CLREOL;
   WRITE ('HOW MANY TICKETS? ');
   KOUNT := -1; {SET DEFAULT}
   READLN (KOUNT);
   IOCHECKA;
   IF IOERR = TRUE THEN KOUNT := -1; {RESET DEFAULT ON I/O ERROR}
   IF KOUNT = 0 THEN DONE:=TRUE;                     {ABORT}
   IF ((KOUNT > 0) AND (KOUNT <= MAXGO)) THEN BEGIN  {VALID INPUT}
      FOR I := 1 TO KOUNT DO BEGIN                   {BUILD THE TICKETS LOOP}
         GENTKT(RNDTKT);                             {BUILD SINGLE TICKET}
         NUMTKTS := NUMTKTS + 1;
         TKTS[NUMTKTS] := RNDTKT;
      END{DO};
      DONE := TRUE;        {FINISHED WITH TASK}
      END
   ELSE BEEP {INVALID OR DEFAULT REPLY}
   {ENDIF};
UNTIL DONE = TRUE;
END{PROC};


{
This procedure removes a ticket from the ticket set.
}

PROCEDURE DROPTKTS;

VAR
   ANSWER              : CHAR;
   I, J, K             : INTEGER;

BEGIN
SCRNRESET;

                    {RED ON WHITE TOP BANNER}

TEXTCOLOR (RED);
TEXTBACKGROUND (WHITE);
CLREOL;
WRITELN;
CLREOL;
WRITELN ('   !!!   WARNING   !!!  REMAINDER OF SET WILL BE RENUMBERED!');
CLREOL;

                    {RETURN TO NORMAL}

LOLITE;
REPEAT
   GOTOXY (5,9);
   CLREOL;
   WRITE ('DO YOU WISH TO PROCEDE (Y/N)? ');
   ANSWER := 'Z'; {SET DEFAULT INVALID ANSWER}
   READ (KBD,ANSWER);
   IOCHECKA;
   IF IOERR = TRUE THEN ANSWER := 'Z'; {RESET DEFAULT ON I/O ERROR}
   ANSWER := UPCASE(ANSWER);
   IF NOT(ANSWER IN ['Y','N']) THEN BEEPBEEP(2);
UNTIL ANSWER IN ['Y','N'];
WRITE (ANSWER); {ECHO ACCEPTED ANSWER}
IF ANSWER = 'Y' THEN BEGIN
   GOTOXY (5,12);
   WRITELN ('THERE ARE',NUMTKTS:4,' TICKETS IN THE SET.');
   REPEAT
      GOTOXY (2,15);
      CLREOL;
      WRITE ('TICKET NUMBER TO BE DELETED? ');
      I := NUMTKTS + 1; {SET DEFAULT}
      READLN (I);
      IOCHECKA;
      IF IOERR = TRUE THEN I:= NUMTKTS + 1; {RESET DEFAULT ON I/O ERROR}
      IF NOT(I IN [1..NUMTKTS]) THEN BEEP;
   UNTIL I IN [1..NUMTKTS];      {VALID INPUT TEST}
   IF I <> NUMTKTS THEN BEGIN                {DROP THE STACK}
      FOR J := I TO (NUMTKTS - 1) DO BEGIN
         K := J + 1;
         TKTS[J] := TKTS[K];
      END{DO};
   END{IF};
   FOR J := 1 TO NUMPIC DO TKTS[NUMTKTS,J] := MAXNUM; {ERASE TOP OF STACK}
   NUMTKTS := NUMTKTS - 1; {DECREASE TOP OF DATA POINTER}
END{IF};
SCRNRESET;
END{PROC};


{
THIS PROCEDURE INSERTS A TICKET INTO THE SET
}

PROCEDURE INSERTTKT;

LABEL
    EXIT;

VAR
    DONE, ABORT                          : BOOLEAN;
    INSRTPOINT, OLDTOP, I                : INTEGER;
    BTKT                                 : TKT;

BEGIN
DONE := FALSE;
REPEAT
  SCRNRESET;
  WRITELN;
  WRITELN ('ENTER TICKET NUMBER FROM 1 TO ',NUMTKTS);
  WRITE (' OR ENTER 0 TO EXIT.   ');
  INSRTPOINT := -1; {SET DEFAULT INVALID VALUE}
  READLN (INSRTPOINT);
  IOCHECKA;
  IF IOERR = TRUE THEN INSRTPOINT := -1; {RESET TO DEFAULT VALUE}
  IF (INSRTPOINT IN [0..NUMTKTS]) THEN
    DONE := TRUE
  ELSE BEGIN
    ALERT1 (1);
    DELAY (1000);
  END {IF};
UNTIL DONE = TRUE;
IF INSRTPOINT = 0 THEN GOTO EXIT;
BUILDTKT (BTKT,ABORT);
IF ABORT = FALSE THEN BEGIN
  OLDTOP := NUMTKTS;
  NUMTKTS := NUMTKTS + 1;
  FOR I:= OLDTOP DOWNTO INSRTPOINT DO BEGIN
    TKTS[I+1] := TKTS[I];
  END;
  TKTS[INSRTPOINT] := BTKT
END {IF};
EXIT:
END {PROC};


{
THIS PROCEDURE REPLACES ONE TICKET IN THE SET WITH ANOTHER ENTERED FROM THE
KEYBOARD.
}

PROCEDURE REPLACETKTS;

LABEL
    LOOP;

VAR
    SELECT                              : INTEGER;
    DONE, ABORT                         : BOOLEAN;
    BTKT                                : TKT;

BEGIN
DONE := FALSE;
REPEAT
  SCRNRESET;
  WRITELN;
  WRITELN ('ENTER TICKET NO. FROM 1 TO ',NUMTKTS);
  WRITE   (' OR ENTER 0 TO ABORT.   ');
  SELECT := -1; {SET DEFAULT VALUE}
  BEEP;
  READLN (SELECT);
  IOCHECKA;
  IF IOERR = TRUE THEN BEGIN
    ALERT1 (1);
    DELAY (1000);
    GOTO LOOP;
  END {IF};
  IF SELECT = 0 THEN BEGIN
    DONE := TRUE;
    GOTO LOOP;
  END {IF};
  IF ((SELECT >= 1) AND (SELECT <= NUMTKTS)) THEN BEGIN
    BUILDTKT(BTKT,ABORT);
    IF ABORT = FALSE THEN TKTS[SELECT] := BTKT;
    DONE := TRUE; END
  ELSE BEGIN
    ALERT1 (1);
    DELAY (1000);
  END {IF};
LOOP:
UNTIL DONE = TRUE;
END {PROC};


{
THIS PROCEDURE DISPLAYS THE EDIT MENU AND EXECUTES THE APPROPRIATE SUBROUTINES
TO EDIT EXISTING ENTRIES.
}

PROCEDURE EDITMENU;

LABEL
    EXIT, LOOP;

VAR
    DONE                                  : BOOLEAN;
    SELECTION                             : INTEGER;

BEGIN
DONE := FALSE;
REPEAT
  SCRNRESET;
  IF NUMTKTS < 1 THEN GOTO EXIT;
  GOTOXY (35,2);
  WRITELN ('EDIT MENU');
  WRITELN ;
  WRITELN ('0.  EXIT THIS MENU.');
  IF NUMTKTS < TKTMAX THEN
    WRITELN ('1.  INSET TICKET INTO SET.')
  ELSE
    WRITELN
  {END IF};
  WRITELN ('2.  DELETE TICKET FROM SET.');
  WRITELN ('3.  REPLACE TICKET IN SET.');
  WRITELN;
  WRITE ('ENTER YOUR SELECTION.  ');
  SELECTION := -1; {SET DEFAULT INVALID}
  BEEP;
  READLN (SELECTION);
  IOCHECKA;
  IF IOERR = TRUE THEN SELECTION := -1; {RESTORE DEFAULT VALUE}
  IF NOT(SELECTION IN [0..3]) THEN BEGIN
    GOTOXY (1,22);
    WRITE ('ERROR TRY AGAIN');
    HILOTONE(2);
    DELAY (1000);
    GOTO LOOP;
  END {IF};
  CASE SELECTION OF
  0 : DONE := TRUE;
  1 : BEGIN
        INSERTTKT;
        IF AUTODISP = Y THEN DISPTKTS;
      END;
  2 : BEGIN
        DROPTKTS;
        IF AUTODISP = Y THEN DISPTKTS;
      END;
  3 : BEGIN
        REPLACETKTS;
        IF AUTODISP = Y THEN DISPTKTS;
      END;
  END {CASE};
LOOP :
UNTIL DONE = TRUE;
EXIT :
END {PROC};


{opening display, copyright notice and music}

PROCEDURE BANNER;

BEGIN
HILITE;
ClrScr;
GoToXY (28,5);
WRITELN ('***   LOTTERY FUN   ***');
GoToXY (31,8);
WRITELN ('BY KARL W. EHRLICH');
GOTOXY (1,14);
LOLITE;
WRITELN ('  COPYRIGHT (c) AUGUST 1986     ');
WRITELN ('           AND OCTOBER 1986     ');
WRITELN ('                                ');
WRITELN ('  ALL RIGHTS RESERVED           ');
HILITE;
WRITELN;
WRITELN ('RELEASE NUMBER: ',RELNO:6:3);
HILOTONE (3);
DELAY (5000);
END;


{
This procedure take an input file name and verifies that it is either a
standard file name, or a drive:filename without an extension.  If the
file name is valid the extension .lfd is added and fault is set to false.

In case of error fault is set to true and the original name is unchanged.
}


PROCEDURE VFNAME (VAR FILEB : FILENAME; VAR FAULT : BOOLEAN);

VAR
    FILEA                          : FILENAME;
    I                              : INTEGER;

BEGIN
FILEA := FILEB;
FAULT := FALSE;

{CONVERT TO UPPER CASE LETTERS}

FOR I := 1 TO LENGTH(FILEA) DO FILEA[I] := UPCASE (FILEA[I]);

{STRIP LEADING BLANKS}

WHILE ((LENGTH (FILEA) > 0) AND (FILEA[1] = ' ')) DO DELETE (FILEA,1,1);

{STRIP TRAILING BLANKS}

WHILE ((LENGTH (FILEA) > 0) AND (FILEA[LENGTH (FILEA)] = ' ')) DO
     DELETE (FILEA, (LENGTH (FILEA)), 1);

{CHECK FOR VALID REMAINING CHARACTERS BASED ON LENGTH}

CASE LENGTH (FILEA) OF
0     : FAULT := TRUE;
9,10  : BEGIN {BRANCH}
        IF ((FILEA[1] IN ['A'..'F']) AND (FILEA[2] = ':')) THEN BEGIN
          IF NOT (FILEA[3] IN ['A'..'Z']) THEN FAULT := TRUE
          ELSE BEGIN
            FOR I := 3 TO LENGTH (FILEA) DO BEGIN
              IF NOT (FILEA[I] IN ['A'..'Z','0'..'9']) THEN FAULT := TRUE;
            END {DO};
          END {IF};
          END
        ELSE FAULT := TRUE
        {END IF};
        END {BRANCH};
1..8  : BEGIN {BRANCH}
        IF ((FILEA[1] IN ['A'..'F']) AND (FILEA[2] = ':') AND
             (LENGTH (FILEA) > 2)) THEN BEGIN
          IF NOT (FILEA[3] IN ['A'..'Z']) THEN FAULT := TRUE
          ELSE BEGIN
            FOR I := 3 TO LENGTH (FILEA) DO BEGIN
              IF NOT (FILEA[I] IN ['A'..'Z','0'..'9']) THEN FAULT := TRUE;
            END {DO};
          END {IF};
          END
        ELSE BEGIN
          IF NOT (FILEA[1] IN ['A'..'Z']) THEN FAULT := TRUE
          ELSE BEGIN
            FOR I := 1 TO LENGTH (FILEA) DO BEGIN
              IF NOT (FILEA[I] IN ['A'..'Z','0'..'9']) THEN FAULT := TRUE;
            END {DO};
          END {IF};
        END {IF};
        END {BRANCH};
ELSE    FAULT := TRUE
END {CASE};
IF FAULT = FALSE THEN FILEB := FILEA + '.LFD';
END {PROC};


{THIS PROCEDURE RENAMES A FILE FOR DATA.}

PROCEDURE RENFILE;

LABEL
     EXIT;

VAR
     FOUND1, FOUND2, FAULT                  : BOOLEAN;
     FILE1, FILE11, FILE2, FILE21           : FILENAME;
     OLDFILE, NEWFILE                       : TEXT;
     IOVAR, IOVAR2                          : INTEGER;
BEGIN
SCRNRESET;
GOTOXY (1,6);
WRITELN ('OR PRESS RETURN TO EXIT');
REPEAT
  GOTOXY (1,4);
  CLREOL;
  WRITE ('NAME OF FILE TO BE RENAMED? ');
  READLN (FILE1);
  IOCHECKA;
  IF IOERR = TRUE THEN FAULT := TRUE
  ELSE BEGIN
    IF FILE1 = '' THEN GOTO EXIT;
    FILE11 := FILE1;
    VFNAME (FILE11, FAULT);
  END{IF};
UNTIL FAULT = FALSE;
GOTOXY (1,12);
WRITELN ('OR PRESS RETURN TO EXIT');
REPEAT
  GOTOXY (1,10);
  CLREOL;
  WRITE ('NEW FILE NAME? ');
  READLN (FILE2);
  IOCHECKA;
  IF IOERR = TRUE THEN FAULT := TRUE
  ELSE BEGIN
    IF FILE2 = '' THEN GOTO EXIT;
    FILE21 := FILE2;
    VFNAME (FILE21, FAULT);
  END{IF};
UNTIL FAULT = FALSE;
ASSIGN (OLDFILE,FILE11);
RESET (OLDFILE) {CHECK TO SEE FILE EXISTS.};
IOVAR := IORESULT;
IF IOVAR = 0 THEN BEGIN {FILE FOUND}
  CLOSE (OLDFILE);
  ASSIGN (NEWFILE, FILE21);
  RESET (NEWFILE) {CHECK TO SEE THAT FILE DOESN'T EXIST};
  IOVAR2 := IORESULT;
  IF IOVAR2 IN [$01,$02] THEN BEGIN
    RENAME (OLDFILE, FILE21);
    IOCHECK;
    IF IOERR = TRUE THEN BEGIN
      WRITELN ('RENAME ABORTED',^G);
      DELAY (1000);
      GOTO EXIT;
    END{IF};
    END
  ELSE BEGIN
    GOTOXY (1,20);
    IF IOVAR2 = 0 THEN WRITELN ('FILE > ',FILE2,' < ALREADY EXISTS')
    ELSE WRITELN ('I/O ERROR WITH > ',FILE2,' <',IOVAR2:10)
    {ENDIF};
    WRITELN ('RENAME ABORTED',^G);
    DELAY (1000);
    GOTO EXIT;
  END{IF};
  END
ELSE BEGIN {FILE NOT FOUND}
  GOTOXY (1,20);
  IF IOVAR IN [1,2] THEN WRITELN ('FILE > ',FILE1,' < NOT FOUND')
  ELSE WRITELN ('I/O ERROR WITH > ',FILE1,' <',IOVAR:10)
  {ENDIF};
  WRITELN ('RENAME ABORTED',^G);
  DELAY (1000);
END{IF};
EXIT:
END {PROC};


{
This procedure requests a data file name for deletion.  Data files all have
the extension .LFD.  The operator only puts in the file name.  The file name
is checked for proper input I/O and then to see if it fits the format of name
or X:name.  If the file name passes these checks an attempt is made to erase
the file and an I/O check is performed.
}

PROCEDURE DROPFILE;

LABEL
     EXIT;

VAR
     FILEA                          : FILENAME;
     FAULT                          : BOOLEAN;
     ERASEFILE                      : TEXT;

BEGIN
SCRNRESET;
GOTOXY (1,7);
WRITELN ('OR PRESS RETURN TO EXIT');
REPEAT {ENTER FILE NAME}
   GOTOXY (1,5);
   CLREOL;
   WRITE ('NAME OF DATA FILE TO BE ERASED? ');
   READLN (FILEA);
   IOCHECKA;
   IF IOERR = TRUE THEN FAULT := TRUE {bad input, cause a retry}
   ELSE BEGIN
     IF FILEA = '' THEN GOTO EXIT; {test for abort}
     VFNAME (FILEA, FAULT); {verify file name and append suffix if valid
                             else cause a retry}
   END{IF};
UNTIL FAULT = FALSE;
ASSIGN (ERASEFILE, FILEA);   {try to locate the file}
ERASE (ERASEFILE);         {erase the file}
IOCHECK;
EXIT :
END {PROC};


{
THIS PROCEDURE READS IN THE TICKET DATA FROM A DISK DATA FILE.
}

PROCEDURE RDISKTKTS;

LABEL
     PEXIT;

TYPE
     FILENAME = STRING[32];


VAR
     I, IOVAL                               : INTEGER;
     FOUND, FAULT                           : BOOLEAN;
     FILEA                                  : FILENAME;

BEGIN
SCRNRESET;
FOUND := FALSE;
GOTOXY (1,3);
WRITELN;
WRITELN ('OR PRESS RETURN TO EXIT.');
WRITELN;
WRITELN ('WARNING!  CURRENT TICKET SET WILL BE LOST!');
REPEAT {UNTIL IO GOOD}
   GOTOXY (1,2);
   CLREOL;
   WRITE ('NAME OF FILE TO READ? ');
   READLN (FILEA);
   IOCHECKA;
   IF IOERR = TRUE THEN FAULT := TRUE
   ELSE BEGIN
      IF FILEA = '' THEN GOTO PEXIT;
      VFNAME (FILEA, FAULT);
   END {IF};
UNTIL FAULT = FALSE;
ASSIGN (INFILE,FILEA);
RESET (INFILE);
IOCHECK;
IF IOERR = TRUE THEN GOTO PEXIT;
FOUND := TRUE;
REINIT;   {CLEAR AWAY OLD TICKETS}
SEEK (INFILE,0);  {ASSURE STARTING POSITION}
READ (INFILE,TKTREC);
IOCHECK;
IF IOERR = TRUE THEN GOTO PEXIT; {BOMB & OUT}
WTKT := TKTREC.TICKET;
IF WTKT[1] < MAXNUM THEN WINNERVALID := TRUE;
WHILE NOT(EOF(INFILE)) DO BEGIN
   READ (INFILE,TKTREC); {READ IN A TICKET VALUE}
   IOCHECK;
   IF IOERR = TRUE THEN GOTO PEXIT; {BOMB & OUT}
   NUMTKTS := NUMTKTS + 1;
   IF NUMTKTS > TKTMAX THEN GOTO PEXIT; {FILE TOO LARGE}
   TKTS[NUMTKTS] := TKTREC.TICKET;  {STORE IN THE ARRAY}
END{WHILE};
PEXIT : IF FOUND = TRUE THEN CLOSE(INFILE);  {HOUSEKEEPING SHUTDOWN FILE}
END {PROC};


{
THIS PROCEDURE WRITES TICKET DATA TO DISK.  ONLY DATA FOR VALID TICKETS
AND THE WINNER ARE WRITTEN TO THE DISK.
}

PROCEDURE WDiskTkts;

LABEL
    EXIT;

TYPE
    FILENAME = STRING[32];

VAR I, Ioval1                           : INTEGER;
    Found, Open, IOErr1, FAULT          : BOOLEAN;
    CH                                  : CHAR;
    FILEA                               : FILENAME;

BEGIN
SCRNRESET;
Found := FALSE;
GOTOXY (1,3);
WRITELN ('OR PRESS RETURN TO EXIT');
REPEAT {UNTIL FILE TO WRITE OR ABORT}
   GOTOXY (1,2);
   CLREOL;
   WRITE ('NAME OF FILE TO WRITE? ');
   READLN (FileA);
   IOCHECKA;
   IF IOERR = TRUE THEN FAULT := TRUE  {BAD INPUT}
   ELSE BEGIN
      IF FILEA = '' THEN GOTO EXIT;    {ABORT CHECK}
      VFNAME (FILEA, FAULT);           {VERIFY FILE NAME OR FAULT:=TRUE}
   END {IF};
UNTIL FAULT = FALSE; {VALID INPUT TEST}
ASSIGN (OutFile,FileA);
RESET (OutFile); {TEST FOR FILE FOUND BY OPENING FOR READ}
IOCHECKA;
IF IOERR = FALSE THEN BEGIN {FILE FOUND}
   CLOSE (OutFile);     {CLOSE IT SO IT CAN BE REOPENED FOR WRITITNG}
   GOTOXY (1,6); {ALERT & PROMPT}
   HILITE;
   WRITELN ('FILE> ',FileA,' ALREADY EXISTS.');
   WRITELN;
   WRITE (' !!! WARNING !!!  ');
   WRITELN ('OVERWRITE WILL WIPE OUT WHATEVER IS IN THE FILE!');
   WRITELN;
   REPEAT          {HUMAN DECISION REQUIRED}
      GOTOXY (1,12);
      CLREOL;
      WRITE ('OVERWRITE (Y/N)? ');
      BEEPBEEP (3);
      CH := 'A'; {SET DEFAULT FOR RECYCLE}
      READ (KBD,CH);
      IOCHECKA;
      IF IOERR = TRUE THEN CH := 'A'; {RESET DEFAULT ON I/O FILE ERROR}
      CH := UPCASE(CH);
   UNTIL CH IN ['Y','N'];
   WRITELN (CH); {ECHO}
   DELAY (500);              {SHOW THE CHOICE}
   IF CH = 'N' THEN GOTO EXIT;
   END
ELSE BEGIN     {FILE NOT SUCCESSFULLY FOUND}
   IF Ioval > $02 THEN BEGIN {PROBLEM OTHER THAN FILE NOT FOUND}
      HILITE;
      GOTOXY (1,9);
      WRITELN ('I/O ERROR NO. ',Ioval1,' HAS OCCURRED');
      WRITE (^G);
      REPEAT UNTIL KEYPRESSED;
      READ (KBD);
      GOTO EXIT;
   END{IF};
END{IF};
ASSIGN (OUTFILE,FILEA);
REWRITE (OutFile);
IOCheck;
IF IOERR = TRUE THEN GOTO EXIT;
Open := TRUE;
SEEK (OutFile,0); {ASSURE FIRST RECORD}
IOCheck;
IF IOERR = TRUE THEN GOTO EXIT;  {BOMB & OUT}
TKTREC.TICKET := WTKT;
WRITE (OutFile,TKTREC);
IOCheck;
IF IOERR = TRUE THEN GOTO EXIT;  {BOMB & OUT}
FOR I := 1 TO NUMTKTS DO BEGIN
   TKTREC.TICKET := TKTS[I];
   WRITE (OutFile,TKTREC);
   IOCheck;
   IF IOERR = TRUE THEN GOTO EXIT;  {BOMB & OUT}
END{DO};
EXIT : IF Open = TRUE THEN CLOSE(OutFile);
END{PROC};


{
THIS PROCEDURE PRINTS OUT THE TICKET SET WITH APPROPRIATE PAUSE LOGIC
}

Procedure PrintTickets;

CONST Space=' ';

Var StartTktNo, EndTktNo, TktsRem, PrintCount, GroupCount,
    ColGroups, I, J, K, LCount                             : INTEGER;

    LastPage                                               : Boolean;

BEGIN
HILITE;
CLRSCR;
WRITELN ('SET PRINTER TO TOP OF FORM AND ON LINE,');
WRITELN;
WRITELN ('THEN PRESS ANY KEY TO CONTINUE PRINTING.');
BEEPBEEP (3);
REPEAT UNTIL KEYPRESSED;
READ (KBD);
StartTktNo := 1;
TktsRem := NumTkts;
LastPage := TRUE;
WHILE TktsRem > 0 do BEGIN
   IF TktsRem > 80 THEN BEGIN
      PrintCount := 40;
      EndTktNo := StartTktNo + 79;
      LastPage := FALSE;
      END
   ELSE BEGIN
      GroupCount := TktsRem DIV 5;
      IF (TktsRem MOD 5) > 0 THEN GroupCount := GroupCount +1;
      ColGroups := (GroupCount DIV 2) + (GroupCount MOD 2);
      PrintCount := ColGroups * 5;
      EndTktNo := NUMTKTS;
      LastPage := TRUE;
   END {IF};
   FOR I := 1 TO 6 DO WRITELN (LST);
   FOR I := 1 TO 33 DO WRITE (LST,Space);
   WRITELN (LST,'TICKETS PICKED');
   WRITELN (LST);
   WRITE (LST,'TICKET     NUMBERS');
   FOR I := 1 TO 23 DO WRITE (LST,Space);
   WRITELN (LST,'TICKET     NUMBERS');
   WRITE (LST,'NUMBER      PICKED');
   FOR I := 1 TO 23 DO WRITE (LST,Space);
   WRITELN (LST,'NUMBER      PICKED');
   WRITELN (LST);
   LCount := 0;
   FOR I:= StartTktNo TO (StartTktNo + PrintCount - 1) DO BEGIN
      J := I + PrintCount;
      WRITE (LST,I:3);
      WRITE (LST,TKTS[I,1]:8);
      FOR K := 2 TO Numpic DO WRITE (LST,TKTS[I,K]:4);
      IF J > NumTkts THEN WRITELN(LST)
      ELSE BEGIN
         WRITE (LST,J:13);
         WRITE (LST,TKTS[J,1]:8);
         FOR K := 2 TO Numpic DO WRITE (LST,TKTS[J,K]:4);
         WRITELN (LST);
      END{IF};
      LCount := LCount + 1;
      IF ((LCount +1) MOD 6) = 0 THEN BEGIN
         WRITELN (LST);
         LCount := LCount + 1;
      END{IF};
   END{DO};
   IF LastPage = FALSE THEN BEGIN
      WRITE (LST,^L);      {TOP OF PAGE}
      StartTktNo := EndTktNo + 1;
      TktsRem := NumTkts - EndTktNo;
      END
   ELSE BEGIN
      TktsRem := 0;
   END{IF};
END{WHILE};
IF WINNERVALID = TRUE THEN BEGIN
   IF LCount > 43 THEN BEGIN     {CHECK FOR ENOUGH PAGE REMAINING}
      WRITE (LST,^L);  {EJECT PAGE}
      FOR I := 1 TO 6 DO WRITELN (LST);
      WRITELN;
   END{IF};
   WRITELN (LST);
   WRITELN (LST);
   FOR I := 1 TO 24 DO WRITE (LST,Space);
   WRITELN (LST,'THE WINNING LOTTO NUMBERS WERE:');
   WRITELN (LST);
   FOR I := 1 TO 24 DO WRITE (LST,Space);
   FOR I := 1 TO Numpic DO WRITE (LST,WTKT[I]:4);
END{IF};
WRITE (LST,^L); {EJECT PAGE}
END{PROC};


{
THIS PROCEDURE ACTS AS THE MAIN MENU AND TASK SCHEDULER FOR THE LOTTERY
PROGRAM.  IT SCHEDULES ALL EXECUTION EXCEPT FOR PROGRAM INITIALIZATION AND
TERMINATION.
}

PROCEDURE MAINMENU;

LABEL
      ENDLOOP;

CONST
      MAXCHOICE = 15;
      QUESTLINE = 19;

TYPE
      CHOICETYPE = 0..MAXCHOICE;
      CHOICESET = SET OF 0..MAXCHOICE;
VAR
      DONE                                               : BOOLEAN;
      REPLYVALID                                         : CHOICESET;
      SELECTION                                          : CHOICETYPE;

BEGIN
DONE := FALSE;
REPEAT

     { This procedure generates the main selection menu for the program.}

   SCRNRESET;
   REPLYVALID := [0..2,7,11..15];
   HILITE;
   GoToXY (35,2);
   WRITELN ('MAIN MENU');
   WINDOW (3,4,78,23);
   ClrScr;
   WRITELN;
   WRITELN (' 0.  EXIT PROGRAM');
   WRITELN (' 1.  READ TICKET SET FROM DISK');
   WRITELN (' 2.  START NEW TICKET SET');
   IF (NUMTKTS < TKTMAX) THEN BEGIN
      REPLYVALID := REPLYVALID + [3,4];
      WRITELN (' 3.  ENTER MORE TICKETS INTO SET');
      WRITELN (' 4.  ADD RANDOM PICKS TO SET');END
   ELSE BEGIN
      WRITELN; WRITELN;
   END{IF};
   IF NUMTKTS > 0 THEN BEGIN
      REPLYVALID := REPLYVALID + [5,6];
      WRITELN (' 5.  EDIT TICKETS IN SET');
      WRITELN (' 6.  STORE TICKET SET TO DISK');END
   ELSE BEGIN
      WRITELN; WRITELN;
   END{IF};
   WRITELN (' 7.  ENTER WINNING TICKET DRAWN');
   IF ((NUMTKTS > 0) AND (WINNERVALID = TRUE)) THEN BEGIN
      REPLYVALID := REPLYVALID + [8];
      WRITELN (' 8.  SCAN TICKET SET FOR WINNERS');END
   ELSE BEGIN
      WRITELN;
   END{IF};
   IF ((NUMTKTS > 0) OR (WINNERVALID = TRUE)) THEN BEGIN
      REPLYVALID := REPLYVALID + [9,10];
      WRITELN (' 9.  PRINT TICKET SET');
      WRITELN ('10.  DISPLAY TICKET SET');END
   ELSE BEGIN
      WRITELN; WRITELN;
   END{IF};
   WRITELN ('11.  RUN SIMULATION');
   WRITELN ('12.  OPTIONS MENU');
   WRITELN ('13.  DATA FILE DIRECTORY');
   WRITELN ('14.  ERASE DATA FILE');
   WRITELN ('15.  RENAME DATA FILE');

            {GET USER SELECTION AND TEST FOR VALIDITY}

   REPEAT
      GoToXY (15,QUESTLINE);
      CLREOL;
      WRITE ('ENTER YOUR SELECTION      ');
      SELECTION := -1; {ENTER DEFAULT}
      READLN (SELECTION);
      IOCheckA;
      IF IOERR = TRUE THEN SELECTION := -1; {RESET AS INVALID ANSWER}
      IF NOT(SELECTION IN REPLYVALID) THEN BEGIN
         GoToXY (1,QUESTLINE);
         CLREOL;
         GoToXY (10,QUESTLINE);
         WRITE ('ERROR !!! - ILLEGAL CHOICE, TRY AGAIN');
         ALERT1 (1);
         DELAY (1000);
         GoToXY (1,QUESTLINE);
         CLREOL;
         GOTO ENDLOOP;
      END{IF};
   UNTIL SELECTION IN REPLYVALID;

                       {PROCESS VALID RESPONSE}

   CASE SELECTION OF
    0 :   DONE:=TRUE;
    1 :   BEGIN
             RDISKTKTS;
             IF AUTODISP = Y THEN DISPTKTS;
             IF AUTOPRINT = Y THEN PRINTTICKETS;
          END;
    2 :   REINIT;
    3 :   ADDTKTS;
    4 :   BEGIN
             RANDOMIZE (0,0);
             ADDRANDUM;
             IF AUTODISP = Y THEN DISPTKTS;
          END;
    5 :   EDITMENU;
    6 :   WDISKTKTS;
    7 :   BUILDWIN;
    8 :   SCANTKTS;
    9 :   PRINTTICKETS;
   10 :   DISPTKTS;
   11 :   BEGIN
             REINIT;
             NUMTKTS := TKTMAX;
             SIMULATE;
             SCANTKTS;
             IF AUTODISP = Y THEN DISPTKTS;
          END;
   12 :   OPTMENU;
   13 :   DISPDIR;
   14 :   DROPFILE;
   15 :   RENFILE;
   END{CASE};
ENDLOOP :
UNTIL DONE = TRUE;
END {PROC};

      {  MAIN PROGRAM BEGINS HERE ...... MAIN PROGRAM BEGINS HERE  }


BEGIN {LOTTERY}

                               {INITIALIZE}

RANDOMIZE(0,0);
NOSOUND; {SET UP THE SOUND EFFECTS GENERATOR}
REINIT;  {ZERO OUT THE DATA ARRAYS}

{SET KEYBOARD TO CAPS LOCK AND NUM LOCK ON
THIS IS DONE BY SETTING BITS 6 & 5 OF MEMORY LOCATION 00417H TO 1.}

STARTBYTE := MEM[$0000:$0417]; {GET STARTING CONDITION OF KBD}
POKEBYTE := STARTBYTE OR $60; {SET BITS 6 & 5}
MEM[$0000:$0417] := POKEBYTE; {POKE BACK INTO MEMORY}

{SET INITIAL OPTIONS}

PWPRINT := N;
PWDISP := Y;
AUTODISP := N;
AUTOPRINT := N;

                             {RUN MAIN PROGRAM}

BANNER; {PRINT OUT A GREETING}

MAINMENU; {MAIN DRIVER MENU}

                            {PROGRAM TERMINATION}

WINDOW (1,1,80,25);
HILITE;
ClrScr;
GoToXY (35,13);
WRITELN ('GOOD BYE!');

{RETURN KEYBOARD TO ORIGINAL CONDITION}

OLDCON := STARTBYTE AND $60; {GET ORIGINAL BITS 6 & 5}
NOWBYTE := MEM[$0000:$0417]; {GET CURRENT BYTE}
POKEBYTE := (NOWBYTE AND NOT($60)) OR OLDCON; {MASK OUT BITS 6 & 5 THEN OR IN
                                               THE OLD VALUES}
MEM[$0000:$0417] := POKEBYTE; {POKE VALUE BACK INTO MEMORY}
BEEPBEEP(3);
END.

