21 game: Difference between revisions

From Rosetta Code
Content added Content deleted
(C++ example added)
Line 271: Line 271:
*/
*/


#include <cstdlib>
#include <ctime>
#include <iostream>
#include <iostream>
#include <limits>
#include <limits>

using namespace std;
using namespace std;


Line 401: Line 404:


int main(int argc, char* argv) {
int main(int argc, char* argv) {

srand(time(NULL));

while (true) {
while (true) {
Model* model = new Model();
Model* model = new Model();
Line 406: Line 412:
Controller* controllers[] = { new Human(), new AI() };
Controller* controllers[] = { new Human(), new AI() };
Presenter* presenter = new Presenter(model, view, controllers);
Presenter* presenter = new Presenter(model, view, controllers);

presenter->run();
presenter->run();

delete model;
delete model;
delete view;
delete view;

Revision as of 21:54, 22 July 2020


Task
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   (possibly a random choice, or it can be specified when the game begins).




bash

<lang bash>shopt -s expand_aliases alias bind_variables=' {

 local -ri goal_count=21
 local -ri player_human=0
 local -ri player_computer=1
 local -i turn=1
 local -i total_count=0
 local -i input_number=0
 local -i choose_turn=0

} ' whose_turn() {

 case $(( ( turn + choose_turn ) % 2 )) in
  ${player_human}) echo "player";;
  ${player_computer}) echo "computer";;
 esac

} next_turn() {

 let turn++

} validate_number() {

 ! test ${input_number} -ge 1 -a ${input_number} -le $( max_guess )

} prompt_number() {

 local prompt_str
 test $( max_guess ) -eq 1 && {
   prompt_str="enter the number 1 to win"
 true
 } || {
   prompt_str="enter a number between 1 and $( max_guess )"
 }
 while [ ! ]
 do
  read -p "${prompt_str} (or quit): "
  input_number=${REPLY}
  case ${REPLY} in
   "quit") {
     false
     return
   } ;;
  esac
  validate_number || break
  echo "try again"
 done

} update_count() {

 let total_count+=input_number

} remaining_count() {

 echo $(( goal_count - total_count ))

} max_guess() {

 local -i remaining_count
 remaining_count=$( remaining_count )
 case $( remaining_count ) in
   1|2|3) echo ${remaining_count} ;;
   *) echo 3 ;;
 esac

} iter() {

 update_count
 next_turn

} on_game_over() {

 test ! ${input_number} -eq $( remaining_count ) || {
   test ! "$( whose_turn )" = "player" && {
     echo -ne "\nYou won!\n\n"
   true
   } || {
     echo -ne "\nThe computer won!\nGAME OVER\n\n"
   }
   false
 }

} on_game_start() {

 echo 21 Game
 read -p "Press enter key to start"

} choose_turn() {

 let choose_turn=${RANDOM}%2

} choose_number() {

 local -i remaining_count
 remaining_count=$( remaining_count )
 case ${remaining_count} in
  1|2|3) {  
    input_number=${remaining_count}
  } ;;
  5|6|7) {  
    let input_number=remaining_count-4
  } ;;
  *) {  
    let input_number=${RANDOM}%$(( $( max_guess ) - 1 ))+1
  }
 esac

} game_play() {

 choose_turn
 while [ ! ]
 do
   echo "Total now ${total_count} (remaining: $( remaining_count ))"
   echo -ne "Turn: ${turn} ("
   test ! "$( whose_turn )" = "player" && {
     echo -n "Your"
   true
   } || {
     echo -n "Computer"
   }
   echo " turn)"
   test ! "$( whose_turn )" = "player" && {
     prompt_number || break
   true
   } || {
     choose_number
     sleep 2
     echo "Computer chose ${input_number}"
   }
   on_game_over || break
   sleep 1
   iter
 done

} 21_Game() {

 bind_variables
 on_game_start
 game_play

} if [ ${#} -eq 0 ] then

true

else

exit 1

fi 21_Game </lang>

C

<lang C>/**

*  Game 21 - an example in C language for Rosseta Code.
*
*  A simple game program whose rules are described below
*  - see DESCRIPTION string.
*
*  This program should be compatible with C89 and up.
*/
  1. define _CRT_SECURE_NO_WARNINGS /* turn off MS Visual Studio panic warnings */
  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <time.h>


static char DESCRIPTION[] = "21 is a two player game, the game is played by choosing a number \n" "(1, 2, or 3) to be added to the running total. The game is won by\n" "the player whose chosen number causes the running total to reach \n" "exactly 21. The running total starts at zero. \n\n";


/**

*  Choosing the best answer if the current number of points is exactly n.
*
*  The algorithm:
*
*      1. It is best if it is possible to reach the number 21 at once.
*      2. If it is not possible to reach the number 21 just now then
*         force opponent to take lost position.
*      3. If we cannot do any of the above, we choose a random number.
*
* @param n the existing running running total BEFORE our move/choice
* @return best move/choice, may be 1 or 2 or 3
*/

int bestChoice(int n) {

   int i;
   for(i = 1; i <= 3; i++)
       if(n + i == 21)
           return i;
   for(i = 1; i <= 3; i++)
       if ((n + i - 1) % 4 == 0)
           return i;
   return 1 + rand() % 3;

}


int main(int argc, char* argv[]) {

   int player; /* 0 is for AI, 1 is for human player */
   char* PLAYERS_NAMES[] = {"AI", "human"};
   runningTotal;
   int choice;
   srand(time(NULL));
   while(1) {
       puts(DESCRIPTION);
       player = rand() % 2; /* It randomly chooses a side to start the game. */
       runningTotal = 0;
       while(runningTotal < 21)
       {
           if(player)
           {
               printf("\n%s", "enter 1 or 2 or 3: ");
               if(scanf("%d", &choice) != 1 || choice < 1 || choice > 3)
                   continue;
           }
           else
               choice = bestChoice(runningTotal);
           printf("\n%8s  %2d = %2d + %1d\n",
               PLAYERS_NAMES[player],
               runningTotal + choice, runningTotal, choice);
           runningTotal += choice;
           if (runningTotal == 21)
               printf("\nThe winner is %s.\n\n\n", PLAYERS_NAMES[player]);
           player = !player;
       }
   }
   return EXIT_SUCCESS;

} </lang>

C++

<lang cpp>/**

*  Game 21 - an example in C++ language for Rosseta Code.
*
*  This version is an example of MVP architecture. The user input, as well as
*  the AI opponent, is handled by separate passive subclasses of abstract class
*  named Controller. It can be noticed that the architecture support OCP,
*  for an example the AI module can be "easily" replaced by another AI etc.
*
*  BTW, it would be better to place each class in its own file. But it would
*  be less convinient for Rosseta Code, where "one solution" mean "one file".
*/
  1. include <cstdlib>
  2. include <ctime>
  3. include <iostream>
  4. include <limits>

using namespace std;

class Model { protected:

   int runningTotal;

public:

   Model() {
       runningTotal = 0;
   }
   int getRunningTotal() {
       return runningTotal;
   }
   void update(int value) {
       runningTotal += value;
   }
   bool isEndGame() {
       return runningTotal == 21;
   }

};

class View { public:

   void show(Model* model) {
       cout << model->getRunningTotal() << endl;
   }
   void newGame()
   {
       cout << "----NEW GAME----" << endl << endl;
   }
   void endGame(string name) {
       cout << endl << "The " << name << " is a winner." << endl;
   }

};

class Controller { public:

   virtual string getName() = 0;
   virtual int getChoice(Model* model) = 0;

};

class AI : public Controller {

   string getName() {
       return "AI";
   }
   int getChoice(Model* model) {
       int n = model->getRunningTotal();
       for (int i = 1; i <= 3; i++)
           if (n + i == 21)
               return i;
       for (int i = 1; i <= 3; i++)
           if ((n + i - 1) % 4 == 0)
               return i;
       return 1 + rand() % 3;
   }

};

class Human : public Controller {

   string getName()
   {
       return "human";
   }
   int getChoice(Model* model)
   {
       int n = model->getRunningTotal();
       int value;
       while (true) {
           if (n == 20)
               cout << "enter 1: ";
           else if (n == 19)
               cout << "enter 1 or 2: ";
           else
               cout << "enter 1 or 2 or 3: ";
           cin >> value;
           if (!cin.fail()) {
               if (n + value > 21)
                   continue;
               else
                   return value;
           }
           else {
               cin.clear();
               cin.ignore((streamsize)numeric_limits<int>::max, '\n');
           }
       }
   }

};

class Presenter { protected:

   Model* model;
   View* view;
   Controller** controllers;

public:

   Presenter(Model* model, View* view, Controller** controllers) {
       this->model = model;
       this->view = view;
       this->controllers = controllers;
   }
   void run() {
       view->newGame();
       view->show(model);
       int player = rand() % 2;
       while (true) {
           model->update(controllers[player]->getChoice(model));
           view->show(model);
           if (model->isEndGame()) {
               view->endGame(controllers[player]->getName());
               break;
           }
           player = (player + 1) % 2;
       }
   }

};

int main(int argc, char* argv) {

   srand(time(NULL));
   while (true) {
       Model* model = new Model();
       View* view = new View();
       Controller* controllers[] = { new Human(), new AI() };
       Presenter* presenter = new Presenter(model, view, controllers);
       presenter->run();
       delete model;
       delete view;
       delete controllers[0];
       delete controllers[1];
       delete presenter;
   }
   return EXIT_SUCCESS; // dead code

}</lang>

C#

<lang csharp> 21 Game

using System;

namespace _21Game {

   public class Program
   {
       private const string computerPlayer = "Computer";
       private const string humanPlayer = "Player 1";
       public static string SwapPlayer(string currentPlayer)
       {
           if (currentPlayer == computerPlayer)
           {
               currentPlayer = humanPlayer;
           }
           else
           {
               currentPlayer = computerPlayer;
           }
           return currentPlayer;
       }
       public static void PlayGame()
       {
           bool playAnother = true;
           int total = 0;
           int final = 21;
           int roundChoice = 0;
           string currentPlayer = RandomPLayerSelect();
           int compWins = 0;
           int humanWins = 0;
           while (playAnother)
           {
               Console.WriteLine($"Now playing: {currentPlayer}");
               try
               {
                   if (currentPlayer == computerPlayer)
                   {
                      roundChoice =  CompMove(total);
                   }
                   else
                   {
                       roundChoice = int.Parse(Console.ReadLine());
                   }
                   
                   if (roundChoice != 1 && roundChoice != 2 && roundChoice != 3)
                   {
                       throw new Exception();
                   }
                   total += roundChoice;
               }
               catch (Exception)
               {
                   Console.WriteLine("Invalid choice! Choose from numbers: 1, 2, 3.");
                   continue;
               }
               Console.WriteLine(total);
               if (total == final)
               {
                   if (currentPlayer == computerPlayer)
                   {
                       compWins++;
                   }
                   if (currentPlayer == humanPlayer)
                   {
                       humanWins++;
                   }
                   Console.WriteLine($"Winner: {currentPlayer}");
                   Console.WriteLine($"Comp wins: {compWins}. Human wins: {humanWins}");
                   Console.WriteLine($"do you wan to play another round? y/n");
                   var choice = Console.ReadLine();
                   if (choice == "y")
                   {
                       total = 0;
                   }
                   else if (choice == "n")
                   {
                       break;
                   }
                   else
                   {
                       Console.WriteLine("Invalid choice! Choose from y or n");
                       continue;
                   }
               }
               else if (total > 21)
               {
                   Console.WriteLine("Not the right time to play this game :)");
                   break;
               }
               currentPlayer = SwapPlayer(currentPlayer);
           }
       }
       public static bool CheckIfCanWin(int total)
       {
           bool result = false;
           if (total == 18)
           {
               result = true;
           }
           return result;
       }
       public static int CompMove(int total)
       {
           int choice = 0;
           if (CheckIfCanWin(total))
           {
               choice = 21 - total;
           }
           else
           {
               choice = new Random().Next(1,4);
           }
           return choice;
       }
       public static string RandomPLayerSelect()
       {
           string[] players = new string[] { computerPlayer, humanPlayer };
           var random = new Random().Next(0,2);
           return players[random];
       }
       public static void Main(string[] args)
       {
           // welcome message and rules
           Console.WriteLine("Welcome to 21 game \n");
           Console.WriteLine(@"21 is a two player game. 

The game is played by choosing a number. 1, 2, or 3 to be added a total sum. \n The game is won by the player reaches exactly 21. \n" );

           Console.WriteLine("Choose your number: (1, 2 or 3)");
           PlayGame();
       }
   }

}


</lang>

Factor

A difficulty system has been implemented. The player chooses a difficulty from 1-10. There is a difficulty in 10 chance that the computer opponent gets to move first. There is a difficulty in 10 chance each turn that the computer opponent will make the optimal move (i.e. setting the total to a number of the form 4n+1) as opposed to a random move. At difficulty level 10, it is impossible for the human player to win the game.

Works with: Factor version 0.99 2020-07-03

<lang factor>USING: accessors combinators.random continuations formatting io kernel math math.functions math.parser multiline qw random sequences ; IN: rosetta-code.21-game

STRING: welcome 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 21.

One player will be the computer. Players alternate supplying a number to be added to the running total.

.welcome ( -- ) welcome print ;

SYMBOLS: +computer+ +human+ ;

TUPLE: game total difficulty who ;

! Instead of saying something dry like 'invalid input,' spice ! it up a little.

insult ( -- )
   {
       "No." "Er..." "Learn to read." "I think not."
       "Come on, is it really this difficult?"
   } random print ;
get-input ( options-seq prompt-str -- str )
   dup "%s: " printf flush readln dup reach member?
   [ 2nip ] [ drop insult get-input ] if ;
get-integer ( options-seq prompt-str -- n )
   get-input string>number ;
get-difficulty ( -- x )
   qw{ 1 2 3 4 5 6 7 8 9 10 }
   "Difficulty (1-10)" get-integer 10 / ;
human-move ( game -- n )
   drop qw{ q 1 2 3 } "Your move (1-3) or q to quit" get-input
   dup "q" = [ drop return ] when string>number ;
choose-first-player ( difficulty -- player )
   [ +computer+ ] [ +human+ ] ifp ;
<game> ( -- game )
   0 get-difficulty dup choose-first-player game boa ;
swap-player ( game -- )
   [ +human+ = +computer+ +human+ ? ] change-who drop ;
.total ( game -- ) total>> "Running total: %d\n" printf ;
random-move ( game -- n ) drop 3 random 1 + ;
boundary? ( n -- ? ) 1 - 4 divisor? ;
(optimal-move) ( m -- n ) dup 4 / ceiling 4 * 1 + swap - ;
optimal-move ( game -- n )
   total>> dup boundary? [ random-move ] [ (optimal-move) ] if ;
computer-move ( game -- n )
   dup difficulty>> [ optimal-move ] [ random-move ] ifp
   dup "Computer chose %d.\n" printf ;
do-turn ( game -- )
   dup dup who>> +human+ = [ human-move ] [ computer-move ] if
   [ + ] curry change-total dup .total swap-player ;
do-turns ( game -- )
   [ dup total>> 20 > ] [ dup do-turn ] until
   dup swap-player who>> "%u wins!\n" printf ;
play-21-game ( -- )
   .welcome nl [ <game> do-turns ] with-return ;

MAIN: play-21-game</lang>

Output:
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 21.

One player will be the computer. Players alternate supplying a
number to be added to the running total.

Difficulty (1-10): 3
Your move (1-3) or q to quit: 3
Running total: 3
Computer chose 2.
Running total: 5
Your move (1-3) or q to quit: 2
Running total: 7
Computer chose 2.
Running total: 9
Your move (1-3) or q to quit: 1
Running total: 10
Computer chose 3.
Running total: 13
Your move (1-3) or q to quit: apple
Er...
Your move (1-3) or q to quit: 3
Running total: 16
Computer chose 1.
Running total: 17
Your move (1-3) or q to quit: 1
Running total: 18
Computer chose 1.
Running total: 19
Your move (1-3) or q to quit: 2
Running total: 21
+human+ wins!

Go

To give the human player a reasonable chance whoever goes first, the computer always chooses randomly when the running total is below 18 but otherwise chooses the exact number needed to win the game. <lang go>package main

import (

   "bufio"
   "fmt"
   "log"
   "math/rand"
   "os"
   "strconv"
   "time"

)

var scanner = bufio.NewScanner(os.Stdin)

var (

   total = 0
   quit  = false

)

func itob(i int) bool {

   if i == 0 {
       return false
   }
   return true

}

func getChoice() {

   for {
       fmt.Print("Your choice 1 to 3 : ")
       scanner.Scan()
       if scerr := scanner.Err(); scerr != nil {
           log.Fatalln(scerr, "when choosing number")
       }
       text := scanner.Text()
       if text == "q" || text == "Q" {
           quit = true
           return
       }
       input, err := strconv.Atoi(text)
       if err != nil {
           fmt.Println("Invalid number, try again")
           continue
       }
       newTotal := total + input
       switch {
       case input < 1 || input > 3:
           fmt.Println("Out of range, try again")
       case newTotal > 21:
           fmt.Println("Too big, try again")
       default:
           total = newTotal
           fmt.Println("Running total is now", total)
           return
       }
   }

}

func main() {

   rand.Seed(time.Now().UnixNano())
   computer := itob(rand.Intn(2))
   fmt.Println("Enter q to quit at any time\n")
   if computer {
       fmt.Println("The computer will choose first")
   } else {
       fmt.Println("You will choose first")
   }
   fmt.Println("\nRunning total is now 0\n")
   var choice int
   for round := 1; ; round++ {
       fmt.Printf("ROUND %d:\n\n", round)
       for i := 0; i < 2; i++ {
           if computer {
               if total < 18 {
                   choice = 1 + rand.Intn(3)
               } else {
                   choice = 21 - total
               }
               total += choice
               fmt.Println("The computer chooses", choice)
               fmt.Println("Running total is now", total)
               if total == 21 {
                   fmt.Println("\nSo, commiserations, the computer has won!")
                   return
               }
           } else {
               getChoice()
               if quit {
                   fmt.Println("OK, quitting the game")
                   return
               }
               if total == 21 {
                   fmt.Println("\nSo, congratulations, you've won!")
                   return
               }
           }
           fmt.Println()
           computer = !computer
       }
   }

}</lang>

Output:

A sample game where the human player manages to win even though the computer (chosen randomly) goes first.

Enter q to quit at any time

The computer will choose first

Running total is now 0

ROUND 1:

The computer chooses 1
Running total is now 1

Your choice 1 to 3 : 3
Running total is now 4

ROUND 2:

The computer chooses 2
Running total is now 6

Your choice 1 to 3 : 4
Out of range, try again
Your choice 1 to 3 : 3
Running total is now 9

ROUND 3:

The computer chooses 2
Running total is now 11

Your choice 1 to 3 : 2
Running total is now 13

ROUND 4:

The computer chooses 2
Running total is now 15

Your choice 1 to 3 : 2
Running total is now 17

ROUND 5:

The computer chooses 1
Running total is now 18

Your choice 1 to 3 : 3
Running total is now 21

So, congratulations, you've won!

A sample game where the human player plays sensibly but still loses when the computer goes first.

Enter q to quit at any time

The computer will choose first

Running total is now 0

ROUND 1:

The computer chooses 3
Running total is now 3

Your choice 1 to 3 : 3
Running total is now 6

ROUND 2:

The computer chooses 3
Running total is now 9

Your choice 1 to 3 : 3
Running total is now 12

ROUND 3:

The computer chooses 1
Running total is now 13

Your choice 1 to 3 : 3
Running total is now 16

ROUND 4:

The computer chooses 1
Running total is now 17

Your choice 1 to 3 : 1
Running total is now 18

ROUND 5:

The computer chooses 3
Running total is now 21

So, commiserations, the computer has won!

Haskell

The computer chooses values randomly. <lang haskell>import System.Random import System.IO import System.Exit import Control.Monad import Text.Read import Data.Maybe

promptAgain :: IO Int promptAgain = do

 putStrLn "Invalid input - must be a number among 1,2 or 3. Try again."
 playerMove

playerMove :: IO Int playerMove = do

 putStr "Your choice(1 to 3):"
 number <- getLine
 when (number == "q") $ do
   exitWith ExitSuccess
 let n = readMaybe number :: Maybe Int
 x <- if isNothing n
            then promptAgain
            else let val = read number
                  in if (val > 3 || val < 1)
                     then promptAgain
                     else return val
 return x

computerMove :: IO Int computerMove = do

 x <- randomRIO (1, 3)
 putStrLn $ "Computer move:" ++ (show x)
 return x

gameLoop :: (IO Int, IO Int) -> IO Int gameLoop moveorder = loop moveorder 0

 where loop moveorder total = do
       number <- fst moveorder
       let total1 = number + total
       putStrLn $ "Running total:" ++ (show total1)
       if total1 >= 21
          then return 0
          else do
            number <- snd moveorder
            let total2 = number + total1
            putStrLn $ "Running total:" ++ (show total2)
            if total2 >= 21
               then return 1
               else loop moveorder total2

main :: IO () main = do

 hSetBuffering stdout $ BlockBuffering $ Just 1
 putStrLn "Enter q to quit at any time"
 x <- randomRIO (0, 1) :: IO Int
 let (moveorder, names) = if x == 0
                then ((playerMove, computerMove), ("Player", "Computer"))
                else ((computerMove, playerMove), ("Computer", "Player"))
 when (x == 1) $ do
   putStrLn "Computer will start the game"
 y <- gameLoop moveorder
 when (y == 0) $ do
   putStrLn $ (fst names) ++ " has won the game"
 when (y == 1) $ do
   putStrLn $ (snd names) ++ " has won the game"</lang>

Java

Options for creating a game:

  1. Who goes first on the first game. Thereafter, the winner of the current game goes first on the next game.
  2. The value needed to win the game.
  3. Valid values allowed to select on each turn.


Sample output provided for options as follows:

  1. Computer goes first on the first game.
  2. Value needed to win game is 21.
  3. Valid values to select is 1, 2, or 3.


The computer strategy is to see if a valid value will win the game. If so, that value is selected. Otherwise, a random value among the valid values is selected.

<lang java> import java.util.Random; import java.util.Scanner;

public class TwentyOneGame {

   public static void main(String[] args) {
       new TwentyOneGame().run(true, 21, new int[] {1, 2, 3});
   }
   
   public void run(boolean computerPlay, int max, int[] valid) {
       String comma = "";
       for ( int i = 0 ; i < valid.length ; i++ ) {
           comma += valid[i];
           if ( i < valid.length - 2 && valid.length >= 3 ) {
               comma += ", ";
           }
           if ( i == valid.length - 2 ) {
               comma += " or ";
           }
       }
       System.out.printf("The %d game.%nEach player chooses to add %s to a running total.%n" + 
               "The player whose turn it is when the total reaches %d will win the game.%n" + 
               "Winner of the game starts the next game.  Enter q to quit.%n%n", max, comma, max);
       int cGames = 0;
       int hGames = 0;
       boolean anotherGame = true;
       try (Scanner scanner = new Scanner(System.in);) {
           while ( anotherGame ) {
               Random r = new Random();
               int round = 0;
               int total = 0;
               System.out.printf("Start game %d%n", hGames + cGames + 1);
               DONE:
                   while ( true ) {
                       round++;
                       System.out.printf("ROUND %d:%n%n", round);
                       for ( int play = 0 ; play < 2 ; play++ ) {
                           if ( computerPlay ) {
                               int guess = 0;
                               //  try find one equal
                               for ( int test : valid ) {
                                   if ( total + test == max ) {
                                       guess = test;
                                       break;
                                   }
                               }
                               //  try find one greater than
                               if ( guess == 0 ) {
                                   for ( int test : valid ) {
                                       if ( total + test >= max ) {
                                           guess = test;
                                           break;
                                       }
                                   }
                               }
                               if ( guess == 0 ) {
                                   guess = valid[r.nextInt(valid.length)];
                               }
                               total += guess;
                               System.out.printf("The computer chooses %d%n", guess);
                               System.out.printf("Running total is now %d%n%n", total);
                               if ( total >= max ) {
                                   break DONE;
                               }
                           }
                           else {
                               while ( true ) {
                                   System.out.printf("Your choice among %s: ", comma);
                                   String line = scanner.nextLine();
                                   if ( line.matches("^[qQ].*") ) {
                                       System.out.printf("Computer wins %d game%s, human wins %d game%s.  One game incomplete.%nQuitting.%n", cGames, cGames == 1 ? "" : "s", hGames, hGames == 1 ? "" : "s");
                                       return;
                                   }
                                   try {
                                       int input = Integer.parseInt(line);
                                       boolean inputOk = false;
                                       for ( int test : valid ) {
                                           if ( input == test ) {
                                               inputOk = true;
                                               break;
                                           }
                                       }
                                       if ( inputOk ) {
                                           total += input;
                                           System.out.printf("Running total is now %d%n%n", total);
                                           if ( total >= max ) {
                                               break DONE;
                                           }
                                           break;
                                       }
                                       else {
                                           System.out.printf("Invalid input - must be a number among %s.  Try again.%n", comma);
                                       }
                                   }
                                   catch (NumberFormatException e) {
                                       System.out.printf("Invalid input - must be a number among %s.  Try again.%n", comma);
                                   }
                               }
                           }
                           computerPlay = !computerPlay;
                       }
                   }
               String win;
               if ( computerPlay ) {
                   win = "Computer wins!!";
                   cGames++;
               }
               else {
                   win = "You win and probably had help from another computer!!";
                   hGames++;
               }
               System.out.printf("%s%n", win);
               System.out.printf("Computer wins %d game%s, human wins %d game%s%n%n", cGames, cGames == 1 ? "" : "s", hGames, hGames == 1 ? "" : "s");
               while ( true ) {
                   System.out.printf("Another game (y/n)? ");
                   String line = scanner.nextLine();
                   if ( line.matches("^[yY]$") ) {
                       //  OK
                       System.out.printf("%n");
                       break;
                   }
                   else if ( line.matches("^[nN]$") ) {
                       anotherGame = false;
                       System.out.printf("Quitting.%n");
                       break;
                   }
                   else {
                       System.out.printf("Invalid input - must be a y or n.  Try again.%n");
                   }
               }
           }
       }
   }

} </lang>

Output:

A sample of 3 games.

The 21 game.
Each player chooses to add 1, 2 or 3 to a running total.
The player whose turn it is when the total reaches 21 will win the game.
Winner of the game starts the next game.  Enter q to quit.

Start game 1
ROUND 1:

The computer chooses 3
Running total is now 3

Your choice among 1, 2 or 3: 4
Invalid input - must be a number among 1, 2 or 3.  Try again.
Your choice among 1, 2 or 3: r
Invalid input - must be a number among 1, 2 or 3.  Try again.
Your choice among 1, 2 or 3: 1
Running total is now 4

ROUND 2:

The computer chooses 1
Running total is now 5

Your choice among 1, 2 or 3: 3
Running total is now 8

ROUND 3:

The computer chooses 1
Running total is now 9

Your choice among 1, 2 or 3: 3
Running total is now 12

ROUND 4:

The computer chooses 3
Running total is now 15

Your choice among 1, 2 or 3: 2
Running total is now 17

ROUND 5:

The computer chooses 3
Running total is now 20

Your choice among 1, 2 or 3: 1
Running total is now 21

You win and probably had help from another computer!!
Computer wins 0 games, human wins 1 game

Another game (y/n)? y

Start game 2
ROUND 1:

Your choice among 1, 2 or 3: 3
Running total is now 3

The computer chooses 1
Running total is now 4

ROUND 2:

Your choice among 1, 2 or 3: 3
Running total is now 7

The computer chooses 3
Running total is now 10

ROUND 3:

Your choice among 1, 2 or 3: 3
Running total is now 13

The computer chooses 2
Running total is now 15

ROUND 4:

Your choice among 1, 2 or 3: 2
Running total is now 17

The computer chooses 1
Running total is now 18

ROUND 5:

Your choice among 1, 2 or 3: 3
Running total is now 21

You win and probably had help from another computer!!
Computer wins 0 games, human wins 2 games

Another game (y/n)? y

Start game 3
ROUND 1:

Your choice among 1, 2 or 3: 3
Running total is now 3

The computer chooses 3
Running total is now 6

ROUND 2:

Your choice among 1, 2 or 3: 3
Running total is now 9

The computer chooses 2
Running total is now 11

ROUND 3:

Your choice among 1, 2 or 3: 3
Running total is now 14

The computer chooses 2
Running total is now 16

ROUND 4:

Your choice among 1, 2 or 3: 3
Running total is now 19

The computer chooses 2
Running total is now 21

Computer wins!!
Computer wins 1 game, human wins 2 games

Another game (y/n)? n
Quitting.

Julia

The computer or player can always win if they go first and choose a number that brings the total to one of the following: 1, 5, 9, 13, or 17. To make things vary more, there is a choice of computer play level at the start. The computer will randomly respond with level 1 and choose a random response 1/4 of the time at level 2. <lang julia> function trytowin(n)

   if 21 - n < 4
       println("Computer chooses $(21 - n) and wins. GG!")
       exit(0)
   end

end

function choosewisely(n)

   trytowin(n)
   targets = [1, 5, 9, 13, 17, 21]
   pos = findfirst(x -> x > n, targets)
   bestmove = targets[pos] - n
   if bestmove > 3
       println("Looks like I could lose. Choosing a 1, total now $(n + 1).")
       return n + 1
   end
   println("On a roll, choosing a $bestmove, total now $(n + bestmove).")
   n + bestmove

end

function choosefoolishly(n)

   trytowin(n)
   move = rand([1, 2, 3])
   println("Here goes, choosing $move, total now $(n + move).")
   n + move

end

function choosesemiwisely(n)

   trytowin(n)
   if rand() > 0.75
       choosefoolishly(n)
   else
       choosewisely(n)
   end

end

prompt(s) = (println(s, ": => "); return readline())

function playermove(n)

   rang = (n > 19) ? "1 is all" : ((n > 18) ? "1 or 2" : "1, 2 or 3")
   choice = 0
   while true
       nstr = prompt("Your choice ($rang), 0 to exit")
       if nstr == "0"
           exit(0)
       elseif nstr == "1"
           return n + 1
       elseif nstr == "2" && n < 20
           return n + 2
       elseif nstr == "3" && n < 19
           return n + 3
       end
   end

end


function play21game()

   n = 0
   level = prompt("Level of play (1=dumb, 3=smart)")
   algo = choosewisely
   if level == "1"
       algo = choosefoolishly
   elseif level == "2"
       algo = choosesemiwisely
   elseif level != "3"
       println("Bad choice syntax--default to smart choice")
   end
   whofirst = prompt("Does computer go first? (y or n)")
   if whofirst[1] == 'y' || whofirst[1] == 'Y'
       n = algo(n)
   end
   while n < 21
       n = playermove(n)
       if n == 21
           println("Player wins! Game over, gg!")
           break
       end
       n = algo(n)
   end

end

play21game() </lang>

Lua

<lang lua> gamewon = false running_total = 0 player = 1 opponent = 2

while not gamewon do

 num = 0
 
 if player == 1 then
   opponent = 2
   repeat
     print("Enter a number between 1 and 3 (0 to quit):")
     num = io.read("*n")
     if num == 0 then
         os.exit()
     end
   until (num > 0) and (num <=3)
 end
 
 if player == 2 and not (gamewon) then
     opponent = 1
     if (21 - running_total <= 3) then
       num = 21 - running_total
     else
       num = math.random(1,3)
     end
     print("Player 2 picks number "..num)
 end
 
 running_total = running_total + num
 print("Total: "..running_total)
 
 if running_total == 21 then
   print("Player "..player.." wins!")
   gamewon = true
 end
 
 if running_total > 21 then
   print("Player "..player.." lost...")
   print("Player "..opponent.." wins!")
   gamewon = true
 end
 
 if player == 1 then
   player = 2
 else player = 1 
 end

end </lang>

Objeck

<lang objeck>class TwentyOne {

 @quit : Bool;
 @player_total : Int;
 @computer_total : Int;
 function : Main(args : String[]) ~ Nil {
   TwentyOne->New()->Play();
 }
 New() {
 }
 method : Play() ~ Nil {
   player_first := Int->Random(1) = 1;
   "Enter 'q' to quit\n==="->PrintLine();
   do {
     if(player_first) {
       PlayerTurn();
       if(<>@quit) {
         "---"->PrintLine();
         ComputerTurn();
       };
     }
     else {
       ComputerTurn();
       "---"->PrintLine();
       PlayerTurn();
     };
     "==="->PrintLine();
   }
   while(<>@quit);
 }
 method : ComputerTurn() ~ Nil {
   input := Int->Random(1, 3);
   
   "Computer choose: {$input}"->PrintLine();
   @computer_total += input;
   if(@computer_total = 21) {
     "Computer Wins!"->PrintLine();
     @quit := true;
   }
   else if(@computer_total > 21) {
     "Computer Loses."->PrintLine();
     @quit := true;
   }
   else {
     "Computer total is {$@computer_total}."->PrintLine();
   };
 }
 method : PlayerTurn() ~ Nil {
   input := GetInput();
   if(input = -1) {
     "Quit"->PrintLine();
     @quit := true;
   }
   else if(input = 0) {
     "Invalid Input!"->PrintLine();
   }
   else {
     @player_total += input;
   };
   if(@player_total = 21) {
     "Player Wins!"->PrintLine();
     @quit := true;
   }
   else if(@player_total > 21) {
     "Player Loses."->PrintLine();
     @quit := true;
   }
   else {
     "Player total is {$@player_total}."->PrintLine();
   };
 }
 function : GetInput() ~ Int {
   "Choosing a number beween 1-3: "->Print();
   input := System.IO.Console->ReadString();
   if(input->Size() = 1) {
     if(input->Get(0) = 'q') {
       return -1;
     };
     return input->ToInt();
   };
   return 0;
 }

}</lang>

Pascal

<lang pascal> 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. </lang>

Perl

Translation of: Raku

<lang perl>print <<'HERE'; The 21 game. Each player chooses to add 1, 2, or 3 to a running total. The player whose turn it is when the total reaches 21 wins. Enter q to quit. HERE

my $total = 0;

while () {

   print "Running total is: $total\n";
   my ($me,$comp);
   while () {
       print 'What number do you play> ';
       $me = <>; chomp $me;
       last if $me =~ /^[123]$/;
       insult($me);
   }
   $total += $me;
   win('Human') if $total >= 21;
   print "Computer plays: " . ($comp = 1+int(rand(3))) . "\n";
   $total += $comp;
   win('Computer') if $total >= 21;

}

sub win {

   my($player) = @_;
   print "$player wins.\n";
   exit;

}

sub insult {

   my($g) = @_;
   exit if $g =~ /q/i;
   my @insults = ('Yo mama', 'Jeez', 'Ummmm', 'Grow up');
   my $i = $insults[1+int rand($#insults)];
   print "$i, $g is not an integer between 1 and 3...\n"

}</lang>

Output:
The 21 game. Each player chooses to add 1, 2, or 3 to a running total.
The player whose turn it is when the total reaches 21 wins. Enter q to quit.
Running total is: 0
What number do you play> 3
Computer plays: 3
Running total is: 6
What number do you play> 3
Computer plays: 3
Running total is: 12
What number do you play> 3
Computer plays: 2
Running total is: 17
What number do you play> 1
Computer plays: 1
Running total is: 19
What number do you play> 2
Human wins.

Phix

If the computer goes first you cannot win.
Once the computer stops displaying "no clear strategy" you cannot win.
The computer_first flag only applies to the first game. After winning, losing, or conceding, you go first. <lang Phix>bool computer_first = false bool show_spoiler = false

integer total = 0

procedure computer_play()

   integer move = 0
   for i=1 to 3 do
       if mod(total+i,4)=1 then
           move = i
           exit
       end if
   end for
   if move=0 then
       puts(1,"no clear strategy\n")
       move = rand(min(3,21-total))
   end if
   printf(1,"Total is %d. I play %d.\n",{total,move})
   total += move
   if total=21 then
       puts(1,"21! I win!\n")
   end if

end procedure

puts(1,"\n21 game\n\n") puts(1,"Press escape or q to quit the game, c to concede and start a new game from 0\n\n")

if computer_first then

   printf(1,"Total is %d.\n",{total})  
   computer_play()

elsif show_spoiler then

   -- The secret to winning!
   puts(1,sq_sub("Uif!pomz!xbz!up!xjo!jt!qmbz!2!gjstu-!uifo!5.=dpnqvufs!npwf?!fwfsz!ujnf",1)&"\n\n")

end if

while 1 do

   printf(1,"Total is %d. enter 1, 2, or 3: ",{total})
   integer ch = wait_key()
   puts(1,iff(ch=#1B?"esc":ch)&"\n")
   if ch>='1' and ch<='3' then
       ch -= '0'
       if total+ch>21 then
           puts(1,"Too big\n")
       else
           total += ch
           if total=21 then
               puts(1,"21! You win!\n")
           else
               computer_play()
           end if
       end if
   elsif ch=#1B or lower(ch)='q' then
       puts(1,"Quitting\n")
       exit
   end if
   if lower(ch)='c' or total=21 then
       total = 0
   end if

end while</lang>

Output:
21 game

Press escape or q to quit the game, c to concede and start a new game from 0

Total is 0. enter 1, 2, or 3: 1
no clear strategy
Total is 1. I play 3.
Total is 4. enter 1, 2, or 3: 1
no clear strategy
Total is 5. I play 1.
Total is 6. enter 1, 2, or 3: 3
no clear strategy
Total is 9. I play 1.
Total is 10. enter 1, 2, or 3: 1
Total is 11. I play 2.
Total is 13. enter 1, 2, or 3: 3
Total is 16. I play 1.
Total is 17. enter 1, 2, or 3: 2
Total is 19. I play 2.
21! I win!
Total is 0. enter 1, 2, or 3: q
Quitting

Python

Works with: Python 2.X and 3.X

<lang python> from random import randint def start(): game_count=0 print("Enter q to quit at any time.\nThe computer will choose first.\nRunning total is now {}".format(game_count)) roundno=1 while game_count<21: print("\nROUND {}: \n".format(roundno)) t = select_count(game_count) game_count = game_count+t print("Running total is now {}\n".format(game_count)) if game_count>=21: print("So, commiserations, the computer has won!") return 0 t = request_count() if not t: print('OK,quitting the game') return -1 game_count = game_count+t print("Running total is now {}\n".format(game_count)) if game_count>=21: print("So, congratulations, you've won!") return 1 roundno+=1

def select_count(game_count): selects a random number if the game_count is less than 18. otherwise chooses the winning number if game_count<18: t= randint(1,3) else: t = 21-game_count print("The computer chooses {}".format(t)) return t

def request_count(): request user input between 1,2 and 3. It will continue till either quit(q) or one of those numbers is requested. t="" while True: try: t = raw_input('Your choice 1 to 3 :') if int(t) in [1,2,3]: return int(t) else: print("Out of range, try again") except: if t=="q": return None else: print("Invalid Entry, try again")

c=0 m=0 r=True while r: o = start() if o==-1: break else: c+=1 if o==0 else 0 m+=1 if o==1 else 0 print("Computer wins {0} game, human wins {1} games".format(c,m)) t = raw_input("Another game?(press y to continue):") r = (t=="y")</lang>

Output:
Enter q to quit at any time.
The computer will choose first.
Running total is now 0

ROUND 1: 

The computer chooses 1
Running total is now 1

Your choice 1 to 3 :4
Out of range, try again
Your choice 1 to 3 :w
Invalid Entry, try again
Your choice 1 to 3 :3
Running total is now 4


ROUND 2: 

The computer chooses 1
Running total is now 5

Your choice 1 to 3 :2
Running total is now 7


ROUND 3: 

The computer chooses 3
Running total is now 10

Your choice 1 to 3 :3
Running total is now 13


ROUND 4: 

The computer chooses 1
Running total is now 14

Your choice 1 to 3 :2
Running total is now 16


ROUND 5: 

The computer chooses 1
Running total is now 17

Your choice 1 to 3 :1
Running total is now 18


ROUND 6: 

The computer chooses 3
Running total is now 21

So, commiserations, the computer has won!
Computer wins 1 game, human wins 0 games
Another game?(press y to continue):y
Enter q to quit at any time.
The computer will choose first.
Running total is now 0

ROUND 1: 

The computer chooses 1
Running total is now 1

Your choice 1 to 3 :3
Running total is now 4


ROUND 2: 

The computer chooses 3
Running total is now 7

Your choice 1 to 3 :3
Running total is now 10


ROUND 3: 

The computer chooses 3
Running total is now 13

Your choice 1 to 3 :5
Out of range, try again
Your choice 1 to 3 :3
Running total is now 16


ROUND 4: 

The computer chooses 2
Running total is now 18

Your choice 1 to 3 :3
Running total is now 21

So, congratulations, you've won!
Computer wins 1 game, human wins 1 games
Another game?(press y to continue):y
Enter q to quit at any time.
The computer will choose first.
Running total is now 0

ROUND 1: 

The computer chooses 3
Running total is now 3

Your choice 1 to 3 :4
Out of range, try again
Your choice 1 to 3 :3
Running total is now 6


ROUND 2: 

The computer chooses 3
Running total is now 9

Your choice 1 to 3 :q
OK,quitting the game

Racket

<lang racket>#lang racket

(define limit 21) (define max-resp 3)

(define (get-resp)

 (let loop ()
   (match (read-line)
     [(app (conjoin string? string->number) n)
      #:when (and (exact-integer? n) (<= 1 n max-resp))
      n]
     ["q" (exit)]
     [n (printf "~a is not in range 1 and ~a\n" n max-resp)
        (loop)])))

(define (win human?) (printf "~a wins\n" (if human? "Human" "Computer")))

(printf "The ~a game. Each player chooses to add a number in range 1 and ~a to a running total. The player whose turn it is when the total reaches exactly ~a wins. Enter q to quit.\n\n" limit max-resp limit)

(let loop ([total 0] [human-turn? (= 0 (random 2))])

 (define new-total
   (+ total
      (cond
        [human-turn? (printf "Running total is: ~a\n" total)
                     (printf "Your turn:\n")
                     (get-resp)]
        [else (define resp (random 1 (add1 max-resp)))
              (printf "Computer plays: ~a\n" resp)
              resp])))
 (cond
   [(= new-total limit) (win human-turn?)]
   [(> new-total limit) (win (not human-turn?))]
   [else (loop new-total (not human-turn?))]))</lang>
Output:
The 21 game. Each player chooses to add an integer
in range 1 and 3 to a running total.
The player whose turn it is when the total reaches exactly 21 wins.
Enter q to quit.

Running total is: 0
Your turn:
1
Computer plays: 3
Running total is: 4
Your turn:
foo
foo is not an integer in range 1 and 3
Your turn:
bar
bar is not an integer in range 1 and 3
Your turn:
3
Computer plays: 1
Running total is: 8
Your turn:
1
Computer plays: 1
Running total is: 10
Your turn:
1
Computer plays: 2
Running total is: 13
Your turn:
1
Computer plays: 3
Running total is: 17
Your turn:
1
Computer plays: 2
Running total is: 20
Your turn:
1
Human wins

Raku

(formerly Perl 6)

Works with: Rakudo version 2018.09

Since there is no requirement that the computer play sensibly, it always plays a random guess so the player has some chance to win. <lang perl6>say qq :to 'HERE';

   The 21 game. Each player chooses to add 1, 2, or 3 to a running total.
   The player whose turn it is when the total reaches 21 wins. Enter q to quit.
   HERE

my $total = 0; loop {

   say "Running total is: $total";
   my ($me,$comp);
   loop {
       $me = prompt 'What number do you play> ';
       last if $me ~~ /^<[123]>$/;
       insult $me;
   }
   $total += $me;
   win('Human') if $total >= 21;
   say "Computer plays: { $comp = (1,2,3).roll }\n";
   $total += $comp;
   win('Computer') if $total >= 21;

}

sub win ($player) {

   say "$player wins.";
   exit;

}

sub insult ($g) {

   exit if $g eq 'q';
   print ('Yo mama,', 'Jeez,', 'Ummmm,', 'Grow up,', 'Did you even READ the instructions?').roll;
   say " $g is not an integer between 1 & 3..."

}</lang>

Sample game:
The 21 game. Each player chooses to add 1, 2, or 3 to a running total.
The player whose turn it is when the total reaches 21 wins. Enter q to quit.

Running total is: 0
What number do you play> 5
Did you even READ the instructions? 5 is not an integer between 1 & 3...
What number do you play> g
Yo mama, g is not an integer between 1 & 3...
What number do you play> 12
Jeez, 12 is not an integer between 1 & 3...
What number do you play> 3
Computer plays: 2

Running total is: 5
What number do you play> 3
Computer plays: 3

Running total is: 11
What number do you play> 1
Computer plays: 1

Running total is: 13
What number do you play> 3
Computer plays: 2

Running total is: 18
What number do you play> 3
Human wins.

REXX

Around half of the REXX program deals with incorrect or missing input   (of the required integer). <lang rexx>/*REXX program plays the 21 game with a human, each player chooses 1, 2, or 3 which */ /*──────────── is added to the current sum, the first player to reach 21 exactly wins.*/ sep= copies('─', 8); sep2= " "copies('═', 8)" " /*construct an eye─catching msg fences.*/ say sep 'Playing the 21 game.' /*tell what's happening here at the zoo*/ high= 3 /*the highest integer that can be used.*/ game= 21 /*the game target (winning total/sum).*/ $= 0 /*the sum [or running total] (so far).*/

   do j=1  while $<game;     bad= 0             /*obtain optional move from ARG or ask.*/
   low = (j \== 1)                              /*allow user to have computer go first.*/
   say;     say sep  'Please enter a number from ' low " ───► 3               (or Quit):"
   if j==1  then say sep '[A value of 0 (zero) means you want the computer to go first.]'
   pull x _ . 1 ax                              /*obtain an uppercased answer & others.*/
   if   x=             then call ser "Nothing entered."
   if _\==             then call ser "Too many arguments entered: "       ax
   if abbrev('QUIT', x, 1)  then do;   say;      say sep  "quitting.";      exit 1
                                 end
   if \datatype(x, 'N')  then call ser "Argument isn't numeric: "            x
   if \datatype(x, 'W')  then call ser "Number isn't an integer: "           x
   if x<0                then call ser "Number can't be negative: "          x
   if x=0  &  j>1        then call ser "Number can't be zero: "              x
   if x>high             then call ser "Number is too large  (>"high'): '    x
   if bad                then iterate           /*Had an error? Then get another number*/
   x= x / 1                                     /*normalize the  X  number;  +2 001 3. */
   if $+x > game         then call ser "Number will cause the sum to exceed " g': '   x
   if x\==0              then call tot x, 1     /*Not 0?   The user wants to go first. */
   if $==game            then leave             /*the user won the game with the last #*/
   y=.                                          /*indicate that no choice has been made*/
        do c=1  for high  until y\==.           /*find a # that yields the best choice.*/
        if (c+$) // (1+high) == 1  then y= c    /*a bit of a highfalutin calculation.  */
        end   /*c*/
   if y==.  then y= random(1, high)             /*get random choice (maybe a lost game)*/
   say sep 'The computer chooses '     y     " as it's choice."        /*inform player.*/
   call tot y, 0                                /*call subroutine to show the total.   */
   end   /*j*/

say if who then say sep 'Congratulations! You have won the 21 game.'

       else say sep  'The computer has won the  21  game.'

exit 0 /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ ser: if bad then return; bad=1; say; say; say sep '***error***' arg(1); say; return tot: arg q,who; $=$+q; say sep 'The game total is now' sep2 $ sep2; return /*add; show $*/</lang>

output:
──────── Playing the  21  game.

──────── Please enter a number from  0 ───► 3               (or Quit):
──────── [A value of 0 (zero) means you want the computer to go first.]
3                                                                         ◄■■■■■■■■■■ user input
──────── The game total is now  ════════  3  ════════
──────── The computer chooses  1  as it's choice.
──────── The game total is now  ════════  4  ════════

──────── Please enter a number from  1 ───► 3               (or Quit):
3                                                                          ◄■■■■■■■■■■ user input
──────── The game total is now  ════════  7  ════════
──────── The computer chooses  1  as it's choice.
──────── The game total is now  ════════  8  ════════

──────── Please enter a number from  1 ───► 3               (or Quit):
3                                                                          ◄■■■■■■■■■■ user input
──────── The game total is now  ════════  11  ════════
──────── The computer chooses  1  as it's choice.
──────── The game total is now  ════════  12  ════════

──────── Please enter a number from  1 ───► 3               (or Quit):
3                                                                          ◄■■■■■■■■■■ user input
──────── The game total is now  ════════  15  ════════
──────── The computer chooses  1  as it's choice.
──────── The game total is now  ════════  16  ════════

──────── Please enter a number from  1 ───► 3               (or Quit):
1                                                                          ◄■■■■■■■■■■ user input
──────── The game total is now  ════════  17  ════════
──────── The computer chooses  1  as it's choice.
──────── The game total is now  ════════  18  ════════

──────── Please enter a number from  1 ───► 3               (or Quit):
3                                                                          ◄■■■■■■■■■■ user input 
──────── The game total is now  ════════  21  ════════

──────── Congratulations!   You have won the  21  game.

Ring

<lang ring>

  1. Project : 21 Game

load "guilib.ring"

limit = 21 posold = 0 button = list(limit) mynum = list(3) yournum = list(3)

new qapp

       {
       win1 = new qwidget() {
                 setwindowtitle("21 Game")
                 setgeometry(100,100,1000,600)
                 label1 = new qlabel(win1) {
                             setgeometry(10,10,1000,600)
                             settext("")
                 }
                 label2 = new qlabel(win1) {
                             setgeometry(240,50,120,40)
                             setAlignment(Qt_AlignHCenter)
                             setFont(new qFont("Verdana",12,100,0))
                             settext("my number:")
                 }
                 label3 = new qlabel(win1) {
                             setgeometry(640,50,120,40)
                             setAlignment(Qt_AlignHCenter)
                             setFont(new qFont("Verdana",12,100,0))
                             settext("your number:")
                 }
                 for p = 1 to 3
                      mynum[p] = new qpushbutton(win1) {
                                         setgeometry(200+p*40,100,40,40)
                                         setstylesheet("background-color:orange")
                                         settext(string(p))
                                         setclickevent("choose(" + string(p) + ",1)")
                                         }
                  next
                  for p = 1 to 3
                       yournum[p] = new qpushbutton(win1) {
                                            setgeometry(600+p*40,100,40,40)
                                            setstylesheet("background-color:white")
                                            settext(string(p))
                                            setclickevent("choose(" + string(p) + ",2)")
                                            }
                  next
                  for n = 1 to limit
                       button[n] = new qpushbutton(win1) {
                                         setgeometry(40+n*40,190,40,40)
                                         settext(string(n))
                                         }
                   next
                   show()
       }
       exec()
       }

func choose(ch,ym)

       pos = posold + ch
       if pos > limit
          msg = "You must choose number from 1 to " + string(limit - posold)
          msgBox(msg)
          for n = 1 to 3
               mynum[n].setenabled(false)
               yournum[n].setenabled(false)
          next
          return
       ok
       for n = posold+1 to pos
            if ym = 1
               button[n] { setstylesheet("background-color:orange") }
            else
               button[n] { setstylesheet("background-color:white") }
            ok
       next
       posold = pos
       if ym = 1
          for n = 1 to 3
               mynum[n].setenabled(false)
               yournum[n].setenabled(true)
          next
        else
          for n = 1 to 3
               mynum[n].setenabled(true)
               yournum[n].setenabled(false)
          next
        ok
        if pos = 21
           if ym = 1
              msgBox("I won!")
           else
              msgBox("You won!")
           ok
        ok

func msgBox(text) { m = new qMessageBox(win1) { setWindowTitle("21 Game") setText(text) show() }

       }

</lang> Output:
21 Game