PROGRAM BlackJack (INPUT,OUTPUT,FileName);
{Purpose: This program plays BlackJack against a single player and
          keeps statistics about the game in a file.  It can then
          draw a bar chart showing the statistics.  Programmed in
          Turbo Pascal v6.0}

USES CRT;

TYPE
    CardType = RECORD
             InDeck : Boolean;
             Suit : CHAR;
             Face : INTEGER;
    END; {CardType}
    StatType = RECORD
             FiveCard : INTEGER;
             BlackJack : INTEGER;
             Other : INTEGER;
             Non21 : INTEGER;
             Computer : INTEGER;
             Customer : INTEGER;
             Games : INTEGER;
    END; {StatType}
    PlayerType = RECORD
               Total : INTEGER;
               NumCards : INTEGER;
               Cards : ARRAY[1..5] OF INTEGER;
    END; {PlayerType}

VAR
   Deck : ARRAY [1..52] OF CardType;
   Stats : StatType;

PROCEDURE Shuffle;
VAR Count : INTEGER;
BEGIN
     FOR COUNT:=1 TO 52 DO WITH Deck[Count] DO BEGIN
         InDeck:=True;
         Suit:=(CHR(((Count-1) DIV 13)+3));
         IF (Count MOD 13)<> 0 THEN Face:=(Count MOD 13) ELSE Face:=13;
     END; {Count}
END;{Shuffle}

PROCEDURE Init;
BEGIN
     WITH Stats DO BEGIN
          FiveCard:=0;
          BlackJack:=0;
          Other:=0;
          Non21:=0;
          Computer:=0;
          Customer:=0;
          Games:=0;
     END; {WITH Stats DO}
     Shuffle;
END;{Init}

PROCEDURE MakePlayer(VAR Player : PlayerType);
VAR Count : INTEGER;
BEGIN
     WITH Player DO BEGIN
          Total:=0;
          NumCards:=0;
          FOR Count:=1 TO 5 DO Cards[Count]:=0;
     END;{WITH Player DO}
END;{MakePlayer}

PROCEDURE Value (VAR Player : PlayerType; PlayerNum : INTEGER);
VAR Value, Total : INTEGER;
BEGIN
     Total:=0;
         CASE Deck[Player.Cards[Player.NumCards]].Face OF
              2..10 : Total:=Deck[Player.Cards[Player.NumCards]].Face;
              11..13 : Total:=10;
              1 : BEGIN
                       IF PlayerNum=1 THEN IF Total>10 THEN Total:=Total+1 ELSE Total:=Total+11
                       ELSE BEGIN
                            REPEAT
                                  GOTOXY(1,23);
                                  WRITELN(Output,'Do you want your ace to count as a 1 or 11?');
                                  {$I-}
                                  READLN(INPUT,Value);
                                  WRITELN(Value);
                                  {$I+}
                            UNTIL (Value IN [1,11]) AND (IOResult=0);
                            Total:=Value;
                       END;{PlayerNum=2}
                  END;{1}
         END;{CASE}
         Player.Total:=Player.Total+Total;
END;{Value}

PROCEDURE HitMe(VAR Player : PlayerType; PlayerNum : INTEGER);
VAR Card : INTEGER;
BEGIN
     REPEAT
           Card:=RANDOM(52);
     UNTIL ((Card<>0) AND (Deck[Card].InDeck<>False));
     IF Player.NumCards<>5 THEN BEGIN
        Deck[Card].InDeck:=False;
        Player.Cards[Player.NumCards+1]:=Card;
        Player.NumCards:=Player.NumCards+1;
     END;{NumCards<>5}
     Value(Player,PlayerNum);
END;{HitMe}

PROCEDURE DisplayCards (PlayerNum, NumCards : INTEGER; Player : PlayerType);
VAR Count : INTEGER;
BEGIN
     GOTOXY(20,1);
     WRITE(Output,'Computer''s Cards');
     GOTOXY(40,1);
     WRITE(Output,'Human''s Cards');
     FOR Count:=1 TO NumCards DO BEGIN
         GOTOXY(PlayerNum*20,Count+1);
         CASE Deck[Player.Cards[Count]].Face OF
              1 : WRITE(Output,'A');
              2..10 : WRITE(Output,Deck[Player.Cards[Count]].Face);
              11 : WRITE(Output,'J');
              12 : WRITE(Output,'Q');
              13 : WRITE(Output,'K');
         END; {CASE Deck[Player.Cards[Count]].Face}
         WRITE(Output,Deck[Player.Cards[Count]].Suit);
         GOTOXY(PlayerNum*20,10);
         IF PlayerNum=2 THEN WRITE(Output,'Total of Cards: ',Player.Total);
     END;{Count:=1}
END;{Display}

PROCEDURE Play(VAR Human,Computer : PlayerType);
VAR Done : Boolean;
    YesNo : CHAR;
BEGIN
     Done:=False;
     REPEAT
           REPEAT
                 {$I-}
                 GOTOXY(1,23);
                 WRITELN(OUTPUT,'Do you want another card?');
                 READLN(INPUT,YesNo);
                 YesNo:=UPCASE(YesNo);
                 {$I+}
           UNTIL (IOResult=0) AND (YesNo IN ['Y','N']);
           IF YesNo='Y' THEN HitMe(Human,2) ELSE Done:=True;
           CLRSCR;
           DisplayCards(1,1,Computer);
           DisplayCards(2,Human.NumCards,Human);
     UNTIL (Done=True) OR (Human.Total>20);
END;{Play}

PROCEDURE ComputerPlay(VAR Human,Computer : PlayerType);
VAR Done : Boolean;
BEGIN
     DisplayCards(1,Computer.NumCards,Computer);
     DisplayCards(2,Human.NumCards,Human);
     Done:=False;
     REPEAT
           IF Computer.Total>17 THEN Done:=True ELSE Hitme(Computer,1);
           DisplayCards(1,Computer.NumCards,Computer);
           DisplayCards(2,Human.NumCards,Human);
           GOTOXY(20,10);
           WRITE(Output,'Total of Cards: ',Computer.Total);
     UNTIL (Done=True) OR (Computer.Total>20);
END;{ComputerPlay}

PROCEDURE Winner(Human, Computer : PlayerType);
VAR CompWon : Boolean;
BEGIN
     CompWon:=True;
     IF (Computer.Total>=22) OR (Human.Total>=22) THEN BEGIN
        IF Human.Total>=22 THEN CompWon:=True ELSE CompWon:=False;
        Stats.Non21:=Stats.Non21+1;
     END ELSE BEGIN
         IF (Computer.NumCards=5) OR (Human.NumCards=5) THEN BEGIN
            Stats.FiveCard:=Stats.FiveCard+1;
            IF Computer.NumCards=5 THEN CompWon:=True ELSE CompWon:=False;
         END {IF Player.NumCards=5}ELSE BEGIN
             IF (Computer.Total=21) OR (Human.Total=21) THEN BEGIN
                IF (Computer.Total=21) AND (Computer.NumCards=2) THEN BEGIN
                   CompWon:=True;
                   Stats.BlackJack:=Stats.BlackJack+1;
                END {Computer BlackJack} ELSE BEGIN
                    IF (Human.Total=21) AND (Human.NumCards=2) THEN BEGIN
                       CompWon:=False;
                       Stats.BlackJack:=Stats.BlackJack+1;
                    END {Human BlackJack} ELSE BEGIN
                        IF (Computer.Total=21) THEN CompWon:=True ELSE CompWon:=False;
                        Stats.Other:=Stats.Other+1;
                    END;{Computer or Human 21}
                END;
             END {Player.Total=21} ELSE BEGIN
                 IF (Computer.Total>=Human.Total) AND (Computer.Total<=21) THEN CompWon:=True ELSE
                    IF (Human.Total<=21) THEN CompWon:=False;
                    Stats.Non21:=Stats.Non21+1;
             END;
        END;{Else}
     END;{Else}
        IF CompWon THEN BEGIN
           Stats.Computer:=Stats.Computer+1;
           GOTOXY(37,23);
           WRITELN(Output,'I win!');
        END ELSE BEGIN
           Stats.Customer:=Stats.Customer+1;
           GOTOXY(36,23);
           WRITELN(Output,'You win!');
        END;
        Stats.Games:=Stats.Games+1;
        READLN(Input);
END;{Winner}

PROCEDURE Deal;
VAR Computer, Human : PlayerType;
    Count : INTEGER;
BEGIN
     Shuffle;
     MakePlayer(Computer);
     MakePlayer(Human);
     FOR Count:=1 TO 2 DO BEGIN
         HitMe(Computer,1);
         HitMe(Human,2);
         CLRSCR;
         DisplayCards(1,1,Computer);
         DisplayCards(2,Human.NumCards,Human);
     END;{Count}
     Play(Human,Computer);
     ComputerPlay(Human,Computer);
     Winner(Human,Computer);
END;{Deal}

PROCEDURE Save;
VAR FileName : Text;
BEGIN
     ASSIGN(FileName,'21.DAT');
     REWRITE(FileName);
     WRITELN(FileName,Stats.FiveCard);
     WRITELN(FileName,Stats.BlackJack);
     WRITELN(FileName,Stats.Other);
     WRITELN(FileName,Stats.Non21);
     WRITELN(FileName,Stats.Computer);
     WRITELN(FileName,Stats.Customer);
     WRITELN(FileName,Stats.Games);
     CLOSE(FileName);
END;{Saves}

PROCEDURE CalBar(VAR Bar, Value : INTEGER);
BEGIN
     Bar:=TRUNC(Value/(Stats.Games/20));
END;{CalBar}

PROCEDURE DrawChart;
BEGIN
     CLRSCR;
     GOTOXY(1,10);
     WRITELN(Output,'G');
     WRITELN(Output,'a');
     WRITELN(Output,'m');
     WRITELN(Output,'e');
     WRITELN(Output,'s');
     GOTOXY(3,1);
     WRITELN(Output,Stats.Games);
     GOTOXY(3,5);
     WRITE(Output,TRUNC(Stats.Games*0.75));
     GOTOXY(3,10);
     WRITE(Output,TRUNC(Stats.Games *0.50));
     GOTOXY(3,15);
     WRITE(Output,TRUNC(Stats.Games *0.25));
     GOTOXY(3,20);
     WRITELN(Output,'0');
     WRITELN(Output,'     Five     Black     Other     Non     Customer     Computer');
     WRITELN(Output,'     Card     Jack      21s       21');
     GOTOXY(28,23);
     WRITELN(Output,'Types of Wins');
END;

PROCEDURE WriteBar(X, BarLength : INTEGER);
VAR Count : INTEGER;
BEGIN
     IF BarLength<>0 THEN
     FOR Count:=1 TO BarLength DO BEGIN
         GOTOXY(X,(20-Count));
         WRITE(Output,CHR(177));
     END;
END;{WriteBar}

PROCEDURE StatShow;
VAR FBar, BBar, OBar, NBar, ComBar, CusBar : INTEGER;
BEGIN
     IF Stats.Games>0 THEN BEGIN
        CalBar(FBar,Stats.FiveCard);
        CalBar(BBar,Stats.BlackJack);
        CalBar(OBar,Stats.Other);
        CalBar(NBar,Stats.Non21);
        CalBar(ComBar,Stats.Computer);
        CalBar(CusBar,Stats.Customer);
        DrawChart;
        WriteBar(6,FBar);
        WriteBar(16,BBar);
        WriteBar(26,OBar);
        WriteBar(35,NBar);
        WriteBar(45,CusBar);
        WriteBar(59,ComBar);
        GOTOXY(1,1);
     END ELSE WRITELN(Output,'No stats to show!');
     READLN(Input);
END;{StatShow}

PROCEDURE Menu;
VAR Choice : INTEGER;
BEGIN
     REPEAT
            REPEAT
                   CLRSCR;
                   WRITELN(Output,'1 TO Play Hand');
                   WRITELN(Output,'2 TO See Stats');
                   WRITELN(Output,'3 TO Exit');
                   READLN(Input,Choice);
            UNTIL Choice IN [1,2,3];
            CASE Choice OF
                 1 : Deal;
                 2 : StatShow;
                 3 : Save;
            END;{Case}
    UNTIL Choice=3;
END;{Menu}

PROCEDURE Load;
VAR FileName : TEXT;
BEGIN
     {$I-}
          ASSIGN(FileName,'21.DAT');
          RESET(FileName);
          READLN(FileName,Stats.FiveCard);
          READLN(FileName,Stats.BlackJack);
          READLN(FileName,Stats.Other);
          READLN(FileName,Stats.Non21);
          READLN(FileName,Stats.Computer);
          READLN(FileName,Stats.Customer);
          READLN(FileName,Stats.Games);
          CLOSE(FileName);
     {$I+}
     IF IOResult<>0 THEN Init;
END;{Load}

BEGIN
     RANDOMIZE;
     CLRSCR;
     Init;
     Load;
     Menu;
END.
