21 game

You are encouraged to solve this task according to the task description, using any language you may know.
21 is a two player game, the game is played by choosing
a number (1, 2, or 3) to be added to the running total.

The game is won by the player whose chosen number causes the running total
to reach exactly 21.

The running total starts at zero.
One player will be the computer.

Players alternate supplying a number to be added to the running total.



Task

Write a computer program that will:

 do the prompting (or provide a button menu), 
 check for errors and display appropriate error messages, 
 do the additions (add a chosen number to the running total), 
 display the running total, 
 provide a mechanism for the player to quit/exit/halt/stop/close the program,
 issue a notification when there is a winner, and
 determine who goes first (maybe a random or user choice, or can be specified when the game begins). 


 
program Game21;
 
{$APPTYPE CONSOLE}
 
{$R *.res}
 
uses
  System.SysUtils,
  System.StrUtils, // for IfThen
  Winapi.Windows;  // for ClearScreen
 
const
  HARD_MODE = True;
 
var
  computerPlayer: string = 'Computer';
  humanPlayer: string = 'Player 1';
 
  // for change color
  ConOut: THandle;
  BufInfo: TConsoleScreenBufferInfo;
 
procedure ClearScreen;
var
  stdout: THandle;
  csbi: TConsoleScreenBufferInfo;
  ConsoleSize: DWORD;
  NumWritten: DWORD;
  Origin: TCoord;
begin
  stdout := GetStdHandle(STD_OUTPUT_HANDLE);
  Win32Check(stdout <> INVALID_HANDLE_VALUE);
  Win32Check(GetConsoleScreenBufferInfo(stdout, csbi));
  ConsoleSize := csbi.dwSize.X * csbi.dwSize.Y;
  Origin.X := 0;
  Origin.Y := 0;
  Win32Check(FillConsoleOutputCharacter(stdout, ' ', ConsoleSize, Origin, NumWritten));
  Win32Check(FillConsoleOutputAttribute(stdout, csbi.wAttributes, ConsoleSize,
    Origin, NumWritten));
  Win32Check(SetConsoleCursorPosition(stdout, Origin));
end;
 
procedure ResetColor;
begin
  SetConsoleTextAttribute(ConOut, BufInfo.wAttributes);
end;
 
procedure ChangeColor(color: Word);
begin
  ConOut := TTextRec(Output).Handle;
  GetConsoleScreenBufferInfo(ConOut, BufInfo);
  SetConsoleTextAttribute(TTextRec(Output).Handle, color);
end;
 
function SwapPlayer(currentPlayer: string): string;
begin
  Result := IfThen(currentPlayer = humanPlayer, computerPlayer, humanPlayer);
end;
 
function RandomPlayerSelect(): string;
begin
  Result := IfThen(Random() < 0.5, computerPlayer, humanPlayer);
end;
 
function CheckIfCanWin(total: Integer): Boolean;
begin
  result := (total >= 18);
end;
 
function CheckIfCanLose(total: Integer; var choose: Integer; hardMode: Boolean =
  False): Boolean;
var
  range: Integer;
begin
  range := 17 - total;
  Result := false;
  if (range > 0) and (range < 4) then
  begin
    Result := true;
    if hardMode then
      choose := range
    else
      choose := Random(range - 1) + 1;
  end;
end;
 
function CompMove(total: Integer): Integer;
begin
  if (CheckIfCanWin(total)) then
  begin
    exit(21 - total);
  end;
 
  if CheckIfCanLose(total, Result, HARD_MODE) then
    exit;
 
  Result := Random(3) + 1;
end;
 
function HumanMove: Integer;
var
  choice: string;
begin
  repeat
    Writeln('Choose from numbers: 1, 2, 3');
    Readln(choice);
  until TryStrToInt(choice, Result) and (Result in [1..3]);
end;
 
procedure PlayGame();
var
  playAnother: Boolean;
  total, final_, roundChoice, compWins, humanWins: Integer;
  choice, currentPlayer: string;
begin
  playAnother := True;
  total := 0;
  final_ := 21;
  roundChoice := 0;
  Randomize;
  currentPlayer := RandomPLayerSelect();
  compWins := 0;
  humanWins := 0;
 
  while (playAnother) do
  begin
    ClearScreen;
    ChangeColor(FOREGROUND_INTENSITY or FOREGROUND_GREEN);
    Writeln(total);
    ResetColor;
    Writeln('');
 
    Writeln('Now playing: ' + currentPlayer);
    if currentPlayer = computerPlayer then
      roundChoice := CompMove(total)
    else
      roundChoice := HumanMove;
    inc(total, roundChoice);
 
    if (total = final_) then
    begin
      if (currentPlayer = computerPlayer) then
      begin
        inc(compWins);
      end
      else
      begin
        inc(humanWins);
      end;
 
      ClearScreen;
      Writeln('Winner: ' + currentPlayer);
      Writeln('Comp wins: ', compWins, '. Human wins: ', humanWins, #10);
      Writeln('Do you wan to play another round? y/n');
 
      readln(choice);
 
      if choice = 'y' then
      begin
        total := 0;
        ClearScreen;
      end
      else if choice = 'n' then
        playAnother := false
      else
      begin
        Writeln('Invalid choice! Choose from y or n');
        Continue;
      end;
    end
    else if total > 21 then
    begin
      Writeln('Not the right time to play this game :)');
      break;
    end;
 
    currentPlayer := SwapPlayer(currentPlayer);
  end;
 
end;
 
const
  WELLCOME_MSG: array[0..5] of string = ('Welcome to 21 game'#10,
    '21 is a two player game.', 'The game is played by choosing a number.',
    '1, 2, or 3 to be added a total sum.'#10,
    'The game is won by the player reaches exactly 21.'#10, 'Press ENTER to start!'#10);
 
var
  i: Integer;
 
begin
  try
    for i := 0 to High(WELLCOME_MSG) do
      Writeln(WELLCOME_MSG[i]);
    ResetColor;
    Readln; // Wait press enter
 
    PlayGame();
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
 