 Thanks to Julio Marchi for this space in MSX All Games Course Game 21
Updated

Portugues

Twenty-one is a famous card game where two players take cards from the deck, aiming at scoring more points than the opponent without exceeding 21 points. In that case, the player looses.

1. Game rules
Initially, both player and computer receives 2 cards each. The player starts the game, choosing between stand or hit. The player stands when he/she believes that his/her hand is great enough to beat the computer. In that case, he stops the game and the turn is passed to the computer. The player hits when he/she thinks his hand is weak, taking more cards from the deck. When the computer plays, it does the same as the player did. The maximum cards stored at hand is the 5 and whenever someone exceeds 21 points, he/she busts and the oppenent is the winner.

Card values:
• A - scores 1 or 11 ponits, according to the player decision.
• 2 a 10 - scores the same card value.
• J, Q e K - scores 10 ponits each.

Pascal version:

Source code and binary: 21.zip

The file 21.zip has inside:
• 21.PAS - Source code for PC Free Pascal.
• 21PT.PAS - Source code in portugues for MSX.
• 21EN.PAS - Source code in english for MSX.
• 21PT.COM - Binary file in portuguese. Run under MSX-DOS.
• 21EN.COM - Binary file in english. Run under MSX-DOS.

C version:

Source code: 21_c.zip

Use Hitech-C to compile, where the patch "time.obj" was added in order to the rand() function works fine.

3. Game structure
The game is divided into functions and procedures which are responsible for many tasks. The following table describe such functions and procedures.

 Function / Procedure Description clean Clear all variables. take_card Pick up randomly one card for any player. player_score Calculate the player or the computer score. print_hand Print player or computer hand. check_winner Routine to check the winner. computer Routine for the computer. player Routine for the player. game Game main routine. main body Starts the game.

4. Code review
The code was written in Pascal and must be compiled using Turbo Pascal.

Global variables

Let's take a look to the game global variables.
```var card : array[1..13,1..4] of boolean;
hand : array[1..5,1..2] of char;
card_hand : array[1..2] of integer;
```
The matrix "card" represents the card selected from the deck. The values from 1 to 13 are directly related to the cards A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K whereas the values from 1 to 4 are related to the four suits -- heart, diamond, spades and clubs. The matrix type is boolean, once this table is designed to mark the cards that has already been selected.
The matrix "hand" holds the player and the computer hands. The card is not relevant here, but only the score.
The vector "card_hand" saves the next position inside the "hand" matrix for both player and computer. The player or computer total cards is calculated as follows:
```total := card_hand[x] - 1;
```

Clean

This function resets the global variables, or, the game tables. Once the work here is only set initial values to the tables, no further comments are necessary.
```{ Clean }
procedure clean;
var f,g : integer;
begin
{ Clear cards }
for f := 1 to 13 do
begin
for g := 1 to 4 do
card[f,g] := false;
end;

{ Clear hand }
for f := 1 to 5 do
begin
hand[f,1] := ' ';
hand[f,2] := ' ';
end;

{ Clear cards from hand }
hand_pointer := 1;
hand_pointer := 1;
end;
```
After this routine runs, the tables are valued as follows:

card
 1 2 3 4 1 false false false false 2 false false false false 3 false false false false 4 false false false false 5 false false false false 6 false false false false 7 false false false false 8 false false false false 9 false false false false 10 false false false false 11 false false false false 12 false false false false 13 false false false false

hand
 1 2 3 4 5 1 ' ' ' ' ' ' ' ' ' ' 2 ' ' ' ' ' ' ' ' ' '

card_hand
 1 2 1 1

Take_card

This function removes one card from the deck for one player. The "player" parameter chooses between player (player :=1 ) or computer (player :=2).
```{ Take_card }
procedure take_card(player : integer);
var card_value, card_suit : integer;
flag : boolean;
card_char : char;
begin
Randomize;
flag := false;

while (not flag) do
begin
card_value := Random(13)+1;
card_suit := Random(4)+1;

if (card[card_value, card_suit] = false) then
begin
card[card_value, card_suit] := true;
flag := true;
end;
end;

case (card_value) of
1 : card_char := 'A';
2..9 : card_char := char(card_value + 48);
10 : card_char := 'D';
11 : card_char := 'J';
12 : card_char := 'K';
13 : card_char := 'Q';
end;

hand[hand_pointer[player], player] := card_char;
hand_pointer[player] := hand_pointer[player]+1;
end;
```
This routine stores the random calculated value for the card at the "card_value" variable, whereas stores the suit at the "card_suit" variable. Then, it checks if the card is available on the deck. If not, another random values are calculated until the card is available.
After validating the card, it is marked on the table "card". Then, it stores the card value at the "hand" table at the proper player hand.
The matrix "hand" is a char type, storing the character corresponding to the card value. Once the number 10 has two digits, it is stored as letter "D".
Let's take a look on how it works. Suppose that the third card from the player is (10,2). In that case, we have:

card
 1 2 3 4 1 true false false false 2 false false false false 3 false false false true 4 false false false false 5 false false false false 6 false false false false 7 false false false false 8 false false false false 9 false false true false 10 false true false false 11 false false false false 12 false false false false 13 true false false false

hand
 1 2 3 4 5 1 'A' '3' 'D' ' ' ' ' 2 '9' 'K' ' ' ' ' ' '

Player_score

This routine calculates the hand value for the player (1) or the computer (2).
```{ Player_score }
function player_score(player : integer) : integer;
var f, total, no_a : integer;
begin
total := 0;
no_a:= 0;

for f := 1 to hand_pointer[player]-1 do
begin
case (hand[f, player]) of
'A' : begin
total := total + 1;
no_a := no_a + 1;
end;
'2'..'9' : total := total + ord(hand[f, player]) - 48;
'D', 'J', 'Q', 'K' : total := total + 10;
end;
end;

{ Card 'A' may value 11 instead of 1 }
while (no_a > 0) and (total + 10 <= 21) do
begin
total := total + 10;
no_a := no_a - 1;
end;

player_score := total;
end;
```
Once the "A" card may value 1 or 11, the final routine check if the score exceeds 21 points when the "A" is valued as 11. If not, the "A" values 11. This routine take in account each "A" present in the player or computer hand.

Print_hand

This routine is responsible for printing the player or computer hand on the screen.
```{ Print_hand }
procedure print_hand(player : integer);
var f, total : integer;
begin
total := player_score(player);

for f := 1 to hand_pointer[player]-1 do
begin
if (hand[f, player] <> 'D') then
write(hand[f, player] + ' ')
else
write('10 ');
end;

writeln('- ', total);
end;
```

Check_winner

This procedure checks who is the winner.
```{ Check_winner }
procedure check_winner;
var f, total_player, total_cpu : integer;
begin
{ Get score }
total_player := player_score(1);
total_cpu := player_score(2);

{ Check winner }
writeln;
if ((total_player>total_cpu) and (total_player<=21)) or (total_cpu>21) then
writeln('You win!');
if ((total_player<total_cpu) and (total_cpu<=21)) or (total_player>21) then
writeln('I win!');
if (total_player=total_cpu) then
writeln('Draw!');

{ Write final hands }
write('My hand: ');
print_hand(2);
print_hand(1);

end;
```
After calculating the scores, the winner is declared:
• The player - must score more than the computer, at the same time do not exceeds 21 points. If the opponent exceeds, the player wins.
• The computer - must score more than the player, at the same time do not exceeds 21 points. If the opponent exceeds, the computer wins.
• Empate - if both score the same.
Obs: it is not allowed to both players exceed 21 points together.

Computer

This procedure controls the computer in the game.
```{ Computer }
procedure computer;
label break;
var total_cpu : integer;
p : real;
flag : boolean;
begin
Randomize;
flag := false;

while (not flag) do
begin
total_cpu := player_score(2);

if (total_cpu > 21) then
begin
writeln('Oh, I busted !!');
goto break;
end;

if (hand_pointer > 5) then
begin
writeln('My last hand !');
goto break;
end;

if (total_cpu = 21) then
goto break;

{ Probabilistic value used to decide if cpu hits or stands }
p := random(3)*0.1 + (total_cpu/21)*0.7;

if (p > 0.7) then
begin
flag := true;
writeln('The computer stands ...');
end
else
begin
take_card(2);
writeln('The computer hits ...');
end;
end;

break :
end;
```
The routine goes into a loop, making the computer plays. It will be interrupted when:
• The computer exceeds 21 points.
• The computer hits 21 points.
• The computer has a full hand.
• The computer stands.
Obs: The full hand verification must come after the bust verification, otherwise the full hand bust will not be noticed by the program.

A calculation using a combination of probability and random numbers is used in order to make the computer take a decision between stand or hit. In such case, the greater the computer score the greater is the probability of him to stand.

Player

This function controls the computer in the game.
```{ Player }
function player:boolean;
label break;
var total_player : integer;
c : char;
flag : boolean;
begin
flag := false;
player := true;

while (not flag) do
begin
total_player := player_score(1);

if (total_player > 21) then
begin
writeln('Oh, you busted !!');
player := false;
goto break;
end;

if (hand_pointer > 5) then
begin
goto break;
end;

if (total_player = 21) then
goto break;

{ Waits for the player decision }
repeat
writeln('(h) - hit / (s) - stand');
until (c='h') or (c='s');

if (c='s') then
begin
flag := true;
writeln('The player stands ...');
end
else
take_card(1);

{ Print player hand }
print_hand(1);
end;

break :
end;
```
The routine goes into a loop, controlling the player plays. It will be interrupted when:
• The player exceeds 21 points.
• The player hits 21 points.
• The player has a full hand.
• The player stands.

Game

This is the game main control routine.
```{ Game }
procedure game;
var f : integer;
begin
clean;

{ Take 2 cards for player and cpu }
for f := 1 to 2 do
begin
take_card(1);
take_card(2);
end;

{ Show initial hands }
if (hand[1,2] = 'D') then
writeln('My hand: 10 ?')
else
writeln('My hand: ' + hand[1,2] + ' ?');
print_hand(1);

{ Give handle to the player, then to the cpu }
if player then
computer;

check_winner;
end;
```
The first step is to give two cards for each player. Then, the cards are printed on screen. Only the first card from the computer is revealed now.
Each player will perform all rounds at once, starting with the human player. If the human player busts, the function "player" returns false and the computer does not even play. This ensures that both players cannot bust together.
After all players play, we check the winner.

Pascal's main routine

Here it is possible to handle multiple games. Nevertheless, we call the game once.
```{ Main body }
begin
clrscr;
writeln('-= 21 Game - MarMSX 2017 =-');
writeln;
game;
end.
```

5. Complete source code
The code was written in Pascal and must be compiled using Turbo Pascal.
```{ Global Variables }
var card : array[1..13,1..4] of boolean;
hand : array[1..5,1..2] of char;
hand_pointer : array[1..2] of integer;

{ Clean }
procedure clean;
var f,g : integer;
begin
{ Clear cards }
for f := 1 to 13 do
begin
for g := 1 to 4 do
card[f,g] := false;
end;

{ Clear hand }
for f := 1 to 5 do
begin
hand[f,1] := ' ';
hand[f,2] := ' ';
end;

{ Clear cards from hand }
hand_pointer := 1;
hand_pointer := 1;
end;

{ Take_card }
procedure take_card(player : integer);
var card_value, card_suit : integer;
flag : boolean;
card_char : char;
begin
Randomize;
flag := false;

while (not flag) do
begin
card_value := Random(13)+1;
card_suit := Random(4)+1;

if (card[card_value, card_suit] = false) then
begin
card[card_value, card_suit] := true;
flag := true;
end;
end;

case (card_value) of
1 : card_char := 'A';
2..9 : card_char := char(card_value + 48);
10 : card_char := 'D';
11 : card_char := 'J';
12 : card_char := 'K';
13 : card_char := 'Q';
end;

hand[hand_pointer[player], player] := card_char;
hand_pointer[player] := hand_pointer[player]+1;
end;

{ Player_score }
function player_score(player : integer) : integer;
var f, total, no_a : integer;
begin
total := 0;
no_a:= 0;

for f := 1 to hand_pointer[player]-1 do
begin
case (hand[f, player]) of
'A' : begin
total := total + 1;
no_a := no_a + 1;
end;
'2'..'9' : total := total + ord(hand[f, player]) - 48;
'D', 'J', 'Q', 'K' : total := total + 10;
end;
end;

{ Card 'A' may value 11 instead of 1 }
while (no_a > 0) and (total + 10 <= 21) do
begin
total := total + 10;
no_a := no_a - 1;
end;

player_score := total;
end;

{ Print_hand }
procedure print_hand(player : integer);
var f, total : integer;
begin
total := player_score(player);

for f := 1 to hand_pointer[player]-1 do
begin
if (hand[f, player] <> 'D') then
write(hand[f, player] + ' ')
else
write('10 ');
end;

writeln('- ', total);
end;

{ Check_winner }
procedure check_winner;
var f, total_player, total_cpu : integer;
begin
{ Get score }
total_player := player_score(1);
total_cpu := player_score(2);

{ Check winner }
writeln;
if ((total_player>total_cpu) and (total_player<=21)) or (total_cpu>21) then
writeln('You win!');
if ((total_player<total_cpu) and (total_cpu<=21)) or (total_player>21) then
writeln('I win!');
if (total_player=total_cpu) then
writeln('Draw!');

{ Write final hands }
write('My hand: ');
print_hand(2);
print_hand(1);

end;

{ Computer }
procedure computer;
label break;
var total_cpu : integer;
p : real;
flag : boolean;
begin
Randomize;
flag := false;

while (not flag) do
begin
total_cpu := player_score(2);

if (total_cpu > 21) then
begin
writeln('Oh, I busted !!');
goto break;
end;

if (hand_pointer > 5) then
begin
writeln('My last hand !');
goto break;
end;

if (total_cpu = 21) then
goto break;

{ Probabilistic value used to decide if cpu hits or stands }
p := random(3)*0.1 + (total_cpu/21)*0.7;

if (p > 0.7) then
begin
flag := true;
writeln('The computer stands ...');
end
else
begin
take_card(2);
writeln('The computer hits ...');
end;
end;

break :
end;

{ Player }
function player:boolean;
label break;
var total_player : integer;
c : char;
flag : boolean;
begin
flag := false;
player := true;

while (not flag) do
begin
total_player := player_score(1);

if (total_player > 21) then
begin
writeln('Oh, you busted !!');
player := false;
goto break;
end;

if (hand_pointer > 5) then
begin
goto break;
end;

if (total_player = 21) then
goto break;

{ Waits for the player decision }
repeat
writeln('(h) - hit / (s) - stand');
until (c='h') or (c='s');

if (c='s') then
begin
flag := true;
writeln('The player stands ...');
end
else
take_card(1);

{ Print player hand }
print_hand(1);
end;

break :
end;

{ Game }
procedure game;
var f : integer;
begin
clean;

{ Take 2 cards for player and cpu }
for f := 1 to 2 do
begin
take_card(1);
take_card(2);
end;

{ Show initial hands }
if (hand[1,2] = 'D') then
writeln('My hand: 10 ?')
else
writeln('My hand: ' + hand[1,2] + ' ?');
print_hand(1);

{ Give handle to the player, then to the cpu }
if player then
computer;

check_winner;
end;

{ Main body }
begin
clrscr;
writeln('-= 21 Game - MarMSX 2017 =-');
writeln;
game;
end.
```

Marcelo Silveira
Systems and Computing Engineer
Expert in Image Processing and Artificial Intelligence