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.

Task
21 game
You are encouraged to solve this task according to the task description, using any language you may know.

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).



11l

F select_count(game_count)
   ‘selects a random number if the game_count is less than 18. otherwise chooses the winning number’
   Int t
   I game_count < 18
      t = random:(1..3)
   E
      t = 21 - game_count
   print(‘The computer chooses #.’.format(t))
   R t

F request_count()
   ‘request user input between 1,2 and 3. It will continue till either quit(q) or one of those numbers is requested.’
   V t = ‘’
   L
      X.try
         t = input(‘Your choice 1 to 3 :’)
         I Int(t) C [1, 2, 3]
            R Int(t)
         E
            print(‘Out of range, try again’)
      X.catch
         I t == ‘q’
            R 0
         E
            print(‘Invalid Entry, try again’)

F start()
   V game_count = 0
   print("Enter q to quit at any time.\nThe computer will choose first.\nRunning total is now #.".format(game_count))
   V roundno = 1
   L
      print("\nROUND #.: \n".format(roundno))
      V t = select_count(game_count)
      game_count = game_count + t
      print("Running total is now #.\n".format(game_count))
      I game_count >= 21
         print(‘So, commiserations, the computer has won!’)
         R 0
      t = request_count()
      I t == 0
         print(‘OK,quitting the game’)
         R -1
      game_count = game_count + t
      print("Running total is now #.\n".format(game_count))
      I game_count >= 21
         print(‘So, congratulations, you've won!’)
         R 1
      roundno++

V c = 0
V m = 0
L
   V o = start()
   I o == -1
      L.break
   E
      c += I o == 0 {1} E 0
      m += I o == 1 {1} E 0
   print(‘Computer wins #. game, human wins #. games’.format(c, m))
   V t = input(‘Another game?(press y to continue):’)
   I t != ‘y’
      L.break
Output:

The same as in Python.

AArch64 Assembly

Works with: as version Raspberry Pi 3B version Buster 64 bits
/* ARM assembly AARCH64 Raspberry PI 3B */
/*  program game21_64.s   */ 

/*******************************************/
/* Constantes file                         */
/*******************************************/
/* for this file see task include a file in language AArch64 assembly*/
.include "../includeConstantesARM64.inc"

.equ HITTOTAL,   21
.equ BUFFERSIZE, 10


/*********************************/
/* Initialized data              */
/*********************************/
.data
szMessRules:         .ascii "21 Game\n"
                    .ascii "21 is a two player game, the game is played by choosing a number \n"
                    .ascii "(1, 2, or 3) to be added to the running total. The game is won by\n"
                    .ascii "the player whose chosen number causes the running total to reach \n"
                    .asciz "exactly 21. The running total starts at zero.\n\n\n"

szMessHumanChoice:  .asciz "Enter your choice (1,2, 3 or type (q)uit to exit): "
szMessErrChoise:    .asciz "invalid choice.\n  "
szMessHumanBegin:   .asciz "The first move is human move.\n"
szMessPiBegin:      .asciz "The first move is Raspberry pi. \n"
szMessHumanWon:     .asciz "You won. \n"
szMessHumanLost:    .asciz "You lost. \n"
szMessTotal:        .asciz "The running total is @ \n"
szMessPiChoice:     .asciz "Raspberry choice is  @ \n" 
szMessNewGame:      .asciz "New game (y/n) ? \n"
szCarriageReturn:   .asciz "\n"
.align 4
qGraine:  .quad 123456
/*********************************/
/* UnInitialized data            */
/*********************************/
.bss
sZoneConv:        .skip 24
sBuffer:          .skip BUFFERSIZE
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main 
main:                                 // entry of program 
    
    ldr x0,qAdrszMessRules
    bl affichageMess
1:
    mov x10,#0                         // total = 0
    mov x0,#100
    bl genereraleas 
    cmp x0,#50
    blt 2f
    ldr x0,qAdrszMessHumanBegin        // human begin
    bl affichageMess
    b 4f
2:                                     // Rasp begin
    ldr x0,qAdrszMessPiBegin
    bl affichageMess
    mov x0,#1
3:                                    // rasp turn
    add x10,x10,x0
    ldr x1,qAdrsZoneConv
    bl conversion10                   // call decimal conversion
    ldr x0,qAdrszMessPiChoice
    ldr x1,qAdrsZoneConv              // insert conversion in message
    bl strInsertAtCharInc
    bl affichageMess
    cmp x10,#HITTOTAL
    bne 4f
    ldr x0,qAdrszMessHumanLost
    bl affichageMess
    b 12f
4:                                    // display current total
    mov x0,x10
    ldr x1,qAdrsZoneConv
    bl conversion10                   // call decimal conversion
    ldr x0,qAdrszMessTotal
    ldr x1,qAdrsZoneConv              // insert conversion in message
    bl strInsertAtCharInc
    bl affichageMess

5:                                    // human turn
    ldr x0,qAdrszMessHumanChoice
    bl affichageMess
    bl saisie
    cmp x0,#'q'                       // quit ?
    beq 100f
    cmp x0,#'Q'
    beq 100f
    cmp x0,#'1'
    add x9,x10,1
    csel x10,x9,x10,eq
    beq 6f
    cmp x0,#'2'
    add x9,x10,2
    csel x10,x9,x10,eq
    beq 6f
    cmp x0,#'3'
    add x9,x10,3
    csel x10,x9,x10,eq
    beq 6f
    ldr x0,qAdrszMessErrChoise
    bl affichageMess
    b 5b
6:
    cmp x10,#HITTOTAL                // total = maxi ?
    beq 11f                           // yes -> human won
    cmp x10,#5                       // else compute rasp number
    mov x9,5                         // compare total and optimun
7:
    cmp x10,x9
    ble 8f
    add x9,x9,4
    b 7b
8:
    sub x0,x9,x10                   // compute number rasp
9:                                  // control number rasp
    cmp x0,#0
    ble 10f
    cmp x0,#3
    ble 3b
10:                                  // if not ok, generate random number
    mov x0,#2
    bl genereraleas
    add x0,x0,#1
    b 3b                            // and loop
    
11:                                  // display human won
    ldr x0,qAdrszMessHumanWon
    bl affichageMess
12:                                 // display new game ?
    ldr x0,qAdrszCarriageReturn
    bl affichageMess
    ldr x0,qAdrszMessNewGame
    bl affichageMess
    bl saisie
    cmp x0,#'y'
    beq 1b
    cmp x0,#'Y'
    beq 1b
    
100:                                  // standard end of the program 
    mov x0, #0                        // return code
    mov x8, #EXIT                     // request to exit program
    svc #0                            // perform the system call
 
qAdrszCarriageReturn:     .quad szCarriageReturn
qAdrszMessRules:          .quad szMessRules
qAdrszMessHumanBegin:     .quad szMessHumanBegin
qAdrszMessPiBegin:        .quad szMessPiBegin
qAdrszMessPiChoice:       .quad szMessPiChoice
qAdrszMessTotal:          .quad szMessTotal
qAdrszMessHumanChoice:    .quad szMessHumanChoice
qAdrszMessHumanLost:      .quad szMessHumanLost
qAdrszMessHumanWon:       .quad szMessHumanWon
qAdrszMessNewGame:        .quad szMessNewGame
qAdrszMessErrChoise:      .quad szMessErrChoise
qAdrsZoneConv:            .quad sZoneConv
/******************************************************************/
/*            string saisie                                       */ 
/******************************************************************/
/* x0 return the first character of human entry */
saisie:
    stp x1,lr,[sp,-16]!    // save  registers
    stp x2,x8,[sp,-16]!    // save  registers
    mov x0,#STDIN          // Linux input console
    ldr x1,qAdrsBuffer     // buffer address 
    mov x2,#BUFFERSIZE     // buffer size 
    mov x8,#READ           // request to read datas
    svc 0                  // call system
    ldr x1,qAdrsBuffer     // buffer address 
    ldrb w0,[x1]           // load first character
100:
    ldp x2,x8,[sp],16      // restaur  2 registers
    ldp x1,lr,[sp],16      // restaur  2 registers
    ret                    // return to address lr x30
    
qAdrsBuffer:         .quad sBuffer
/***************************************************/
/*   Generation random number                  */
/***************************************************/
/* x0 contains limit  */
genereraleas:
    stp x1,lr,[sp,-16]!        // save  registers
    stp x2,x3,[sp,-16]!        // save  registers
    stp x4,x5,[sp,-16]!        // save  registers
    ldr x4,qAdrqGraine
    ldr x2,[x4]
    ldr x3,qNbDep1
    mul x2,x3,x2
    ldr x3,qNbDep2
    add x2,x2,x3
    str x2,[x4]                // maj de la graine pour l appel suivant 
    cmp x0,#0
    beq 100f
    udiv x3,x2,x0
    msub x0,x3,x0,x2           // résult = remainder
  
100:                           // end function
    ldp x4,x5,[sp],16          // restaur  2 registers
    ldp x2,x3,[sp],16          // restaur  2 registers
    ldp x1,lr,[sp],16          // restaur  2 registers
    ret                        // return to address lr x30
/*****************************************************/
qAdrqGraine: .quad qGraine
qNbDep1:     .quad 0x0019660d
qNbDep2:     .quad 0x3c6ef35f

/********************************************************/
/*        File Include fonctions                        */
/********************************************************/
/* for this file see task include a file in language AArch64 assembly */
.include "../includeARM64.inc"


Ada

with Ada.Text_IO;
with Ada.Numerics.Discrete_Random;

procedure Game_21 is

   procedure Put (Item : String) is
   begin
      for Char of Item loop
         Ada.Text_IO.Put (Char);
         delay 0.010;
      end loop;
   end Put;

   procedure New_Line is
   begin
      Ada.Text_IO.New_Line;
   end New_Line;

   procedure Put_Line (Item : String) is
   begin
      Put (Item);
      New_Line;
   end Put_LIne;

   type Player_Kind is (Human, Computer);
   type Score_Type  is range 0 .. 21;
   subtype Amount_Range is Score_Type range 1 .. 3;

   package Amount_Generators is
     new Ada.Numerics.Discrete_Random (Amount_Range);

   Gen    : Amount_Generators.Generator;
   Total  : Score_Type := 0;
   Choice : Character;
   Player : Player_Kind;
   Amount : Amount_Range;
begin
   Amount_Generators.Reset (Gen);

   Put_Line ("--- The 21 Game ---"); New_Line;

   Put ("Who is starting human or computer (h/c) ? ");
   Ada.Text_IO.Get_Immediate (Choice);
   New_Line;

   case Choice is
      when 'c' | 'C' =>  Player := Computer;
      when 'h' | 'H' =>  Player := Human;
      when others => return;
   end case;

   Play_Loop : loop
      New_Line;
      Put ("Runing total is "); Put (Total'Image); New_Line;
      New_Line;

      case Player is

         when Human =>
            Put_Line ("It is your turn !");
            Input_Loop : loop
               Put ("Enter choice 1 .. 3 (0 to end) : ");
               Ada.Text_IO.Get_Immediate (Choice);

               case Choice is
                  when '1' .. '3' =>
                     Amount := Amount_Range'Value ("" & Choice);
                     exit Input_Loop;

                  when '0' =>
                     exit Play_Loop;

                  when others =>
                     Put_Line ("Choice must be in 1 .. 3");
               end case;
            end loop Input_Loop;
            New_Line;

         when Computer =>
            delay 1.500;
            Amount := Amount_Generators.Random (Gen);
            Put ("Computer chooses: "); Put (Amount'Image); New_Line;
            delay 0.800;

      end case;

      New_Line;
      Amount := Score_Type'Min (Amount, Score_Type'Last - Total);
      Put ("  "); Put (Total'Image); Put (" + ");  Put (Amount'Image); Put (" = ");
      Total := Total + Amount;
      Put (Total'Image);
      New_Line;

      if Total = 21 then
         New_Line;
         Put ("... and we have a WINNER !!"); New_Line;
         delay 0.500;
         Put_Line ("   ... and the winner is ...");
         delay 1.000;
         Put_Line ("      " & Player'Image);
         exit;
      end if;

      Player := (case Player is
                 when Computer => Human,
                 when Human    => Computer);
   end loop Play_Loop;
end Game_21;

Applesoft BASIC

Translation of: Commodore BASIC

Most comments are removed, see Commodore BASIC for comments. The code is compacted, see the Commodore BASIC listing for a line-by-line listing.

All text is upper case as only the the Apple IIe and later models have can display lower case on the text screen. Input is converted to upper case as the earlier models may not be able to enter lower case from the keyboard.

Commodore control codes are converted to the equivilent Applesoft BASIC command: ?CHR$(147); is HOME ?CHR$(18); is INVERSE and ?CHR$(146); is NORMAL. The code is adjusted when printing a numeric variables as Applesoft BASIC does not print any spaces. There are a few other changes: VAL function instead of ASC to convert number, FOR instead of GOTO or THEN, and WAIT instead of GET in a loop.

 5  DIM P$(2),HC(2),CA(4),CN$(6): READ CA(0),CA(1),CA(2),CA(3): FOR I = 1 TO 6: READ CN$(I): NEXT : DEF  FN M(X) = (X - ( INT (X / 4)) * 4): DEF  FN U(K) = K - (K > 95) * 32:L$ = " ":M$ =  CHR$ (13): DATA 1,1,3,2
 9  FOR PLAY = 0 TO 1
 10     REM SET SCREEN COLORS HERE
 11     LET C% =  INT ( RND (1) * 16)
 12     LET B% =  INT ( RND (1) * 15 + C% + 1): IF B% > 15 THEN B% = B% - 16
 13     POKE 49186,C% + B% * 16: REM FOREGROUND&BACKGROUND
 14     POKE 49204, INT ( RND (1) * 16): REM BORDER
 20     HOME : PRINT M$ SPC( 16);"21 GAME"M$M$" THE GOAL OF THIS GAME IS TO TAKE TURNS"M$" ADDING THE VALUE OF EITHER 1, 2, OR 3"M$" TO A RUNNING TOTAL. THE FIRST PLAYER"M$" TO BRING THE TOTAL TO 21..."M$M$ SPC( 10);"... WINS THE GAME!"M$
 30     GOSUB 1000
 40     FOR SETUP = 0 TO 1: HOME : PRINT 
 42         FOR P = 1 TO 2
 44             PRINT M$"PLAYER";L$;P;", [H]UMAN OR [C]OMPUTER? ";
 45             FOR K = 0 TO 1: GET K$:K$ =  CHR$ ( FN U( ASC (K$))):K = K$ = "C" OR K$ = "H": NEXT K
 46              PRINT K$M$M$"PLAYER";L$;P;",": PRINT "ENTER YOUR NAME";
 50              LET HC(P) = (K$ = "C"): IF HC(P) THEN  FOR PC = 0 TO 1:PN =  INT ( RND (1) * 6) + 1:T$ = CN$(PN):PC = T$ <  > P$(P - 1): NEXT PC:P$(P) = T$: PRINT "? ";P$(P)
 52              IF  NOT HC(P) THEN  INPUT P$(P)
 54          NEXT P
 60          FOR P = 1 TO 2: PRINT M$L$;P;". ";P$(P);: NEXT P: PRINT M$M$"IS THIS CORRECT (Y/N)? ";: FOR K = 0 TO 1: GET K$:K$ =  CHR$ ( FN U( ASC (K$))):K = K$ = "Y" OR K$ = "N": NEXT K: PRINT K$:SETUP = K$ <  > "N"
 70      NEXT SETUP
 80      PRINT M$"WHO WILL PLAY FIRST (1 OR 2)? ";: FOR K = 0 TO 1: GET K$:K = K$ = "1" OR K$ = "2": NEXT K
 90     FP =  VAL (K$): PRINT K$M$M$"OKAY, ";P$(FP);" WILL PLAY FIRST."M$: GOSUB 1000
 100    LET RT = 0:PI = FP - 1
 110    FOR GAME = 0 TO 1
 115        PI = PI * (PI < 2) + 1: HOME : PRINT "TOTAL SO FAR:";L$;RT;M$M$P$(PI);"'S TURN."
 120        IF HC(PI) THEN  PRINT M$"THINKING...";:TT =  INT ( RND (1) * 10): FOR T = 1 TO TT: PRINT ".";: FOR I = 1 TO 250: NEXT I,T:RM =  FN M(RT):AD = CA(RM): PRINT M$M$P$(PI);" ADDS";L$;CA(RM);".": FOR T = 1 TO 1000: NEXT T
 125        IF  NOT HC(PI) THEN  FOR AMOUNT = 0 TO 1: PRINT  MID$ (M$ + "ILLEGAL AMOUNT. TRY AGAIN." + M$,1,AMOUNT * 255)M$"HOW MUCH TO ADD,": INPUT "1, 2, OR 3 (0 TO QUIT) ?";AD:AMOUNT = AD >  = 0 AND AD <  = 3: NEXT AMOUNT
 128        LET RT = RT + AD:GAME = RT >  = 21 OR AD = 0
 130      NEXT GAME
 140      IF RT > 21 THEN  PRINT M$P$(PI);" LOSES BY GOING OVER 21!!"
 150      IF RT = 21 THEN  PRINT M$"21! ";P$(PI);" WINS THE GAME!!!"
 160      IF  NOT AD THEN  PRINT M$"GAME WAS ENDED BY ";P$(PI);".":PLAY = 1
 210      IF AD THEN  PRINT : PRINT "WOULD YOU LIKE TO PLAY AGAIN? ";: FOR K = 0 TO 1: GET K$:K$ =  CHR$ ( FN U( ASC (K$))):K = K$ = "N" OR K$ = "Y": NEXT K: PRINT K$:PLAY = K$ = "N"
 220  NEXT PLAY
 230  IF AD THEN  PRINT M$"OKAY, MAYBE ANOTHER TIME. BYE!"
 240  END 
 1000 Z$ = " PRESS A KEY TO CONTINUE. ": PRINT  SPC( 20 -  INT ( LEN (Z$) / 2));: INVERSE : PRINT Z$;: NORMAL : WAIT 49152,128:K =  PEEK (49168): RETURN 
 2010  DATA "APPLE IIE","APPLE II PLUS","APPLE IIC","APPLE II","APPLE IIGS","MACINTOSH LC"

ARM Assembly

Works with: as version Raspberry Pi
/* ARM assembly Raspberry PI  */
/*  program game21.s   */ 

/* REMARK 1 : this program use routines in a include file 
   see task Include a file language arm assembly 
   for the routine affichageMess conversion10 
   see at end of this program the instruction include */
/* for constantes see task include a file in arm assembly */
/************************************/
/* Constantes                       */
/************************************/
.include "../constantes.inc"
.equ STDIN,  0       @ Linux input console
.equ READ,   3       @ Linux syscall
.equ HITTOTAL,   21
.equ BUFFERSIZE, 10


/*********************************/
/* Initialized data              */
/*********************************/
.data
szMessRules:         .ascii "21 Game\n"
                    .ascii "21 is a two player game, the game is played by choosing a number \n"
                    .ascii "(1, 2, or 3) to be added to the running total. The game is won by\n"
                    .ascii "the player whose chosen number causes the running total to reach \n"
                    .asciz "exactly 21. The running total starts at zero.\n\n\n"

szMessHumanChoice:  .asciz "Enter your choice (1,2, 3 or type (q)uit to exit): "
szMessErrChoise:    .asciz "invalid choice.\n "
szMessHumanBegin:   .asciz "The first move is human move.\n"
szMessPiBegin:      .asciz "The first move is Raspberry pi. \n"
szMessHumanWon:     .asciz "You won. \n"
szMessHumanLost:    .asciz "You lost. \n"
szMessTotal:        .asciz "The running total is @ \n"
szMessPiChoice:     .asciz "Raspberry choice if  @ \n" 
szMessNewGame:      .asciz "New game (y/n) ? \n"
szCarriageReturn:   .asciz "\n"
.align 4
iGraine:  .int 123456
/*********************************/
/* UnInitialized data            */
/*********************************/
.bss
sZoneConv:        .skip 24
sBuffer:          .skip BUFFERSIZE
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main 
main:                                 @ entry of program 
    
    ldr r0,iAdrszMessRules
    bl affichageMess
1:
    mov r10,#0                         @ total = 0
    mov r0,#100
    bl genereraleas 
    cmp r0,#50
    blt 2f
    ldr r0,iAdrszMessHumanBegin        @ human begin
    bl affichageMess
    b 4f
2:                                     @ Rasp begin
    ldr r0,iAdrszMessPiBegin
    bl affichageMess
    mov r0,#1
3:                                    @ rasp turn
    add r10,r10,r0
    ldr r1,iAdrsZoneConv
    bl conversion10                   @ call decimal conversion
    ldr r0,iAdrszMessPiChoice
    ldr r1,iAdrsZoneConv              @ insert conversion in message
    bl strInsertAtCharInc
    bl affichageMess
    cmp r10,#HITTOTAL
    bne 4f
    ldr r0,iAdrszMessHumanLost
    bl affichageMess
    b 10f
4:                                    @ display current total
    mov r0,r10
    ldr r1,iAdrsZoneConv
    bl conversion10                   @ call decimal conversion
    ldr r0,iAdrszMessTotal
    ldr r1,iAdrsZoneConv              @ insert conversion in message
    bl strInsertAtCharInc
    bl affichageMess

5:                                    @ human turn
    ldr r0,iAdrszMessHumanChoice
    bl affichageMess
    bl saisie
    cmp r0,#'q'                       @ quit ?
    beq 100f
    cmp r0,#'Q'
    beq 100f
    cmp r0,#'1'
    addeq r10,r10,#1
    beq 6f
    cmp r0,#'2'
    addeq r10,r10,#2
    beq 6f
    cmp r0,#'3'
    addeq r10,r10,#3
    beq 6f
    ldr r0,iAdrszMessErrChoise
    bl affichageMess
    b 5b
6:
    cmp r10,#HITTOTAL                @ total = maxi ?
    beq 9f                           @ yes -> human won
    cmp r10,#5                       @ else compute rasp number
    rsble r0,r10,#5
    ble 7f
    cmp r10,#9
    rsble r0,r10,#9
    ble 7f
    cmp r10,#13
    rsble r0,r10,#13
    ble 7f
    cmp r10,#17
    rsble r0,r10,#17
    ble 7f
    cmp r10,#21
    rsble r0,r10,#21
    ble 7f
7:                                  @ control number rasp
    cmp r0,#0
    ble 8f
    cmp r0,#3
    ble 3b
8:                                  @ if not ok, generate random number
    mov r0,#2
    bl genereraleas
    add r0,r0,#1
    b 3b                            @ and loop
    
9:                                  @ display human won
    ldr r0,iAdrszMessHumanWon
    bl affichageMess
10:                                 @ display new game ?
    ldr r0,iAdrszCarriageReturn
    bl affichageMess
    ldr r0,iAdrszMessNewGame
    bl affichageMess
    bl saisie
    cmp r0,#'y'
    beq 1b
    cmp r0,#'Y'
    beq 1b
    
100:                                  @ standard end of the program 
    mov r0, #0                        @ return code
    mov r7, #EXIT                     @ request to exit program
    svc #0                            @ perform the system call
 
iAdrszCarriageReturn:     .int szCarriageReturn
iAdrszMessRules:          .int szMessRules
iAdrszMessHumanBegin:     .int szMessHumanBegin
iAdrszMessPiBegin:        .int szMessPiBegin
iAdrszMessPiChoice:       .int szMessPiChoice
iAdrszMessTotal:          .int szMessTotal
iAdrszMessHumanChoice:    .int szMessHumanChoice
iAdrszMessHumanLost:      .int szMessHumanLost
iAdrszMessHumanWon:       .int szMessHumanWon
iAdrszMessNewGame:        .int szMessNewGame
iAdrszMessErrChoise:      .int szMessErrChoise
iAdrsZoneConv:            .int sZoneConv
/******************************************************************/
/*            string saisie                                       */ 
/******************************************************************/
/* r0 return the first character of human entry */
saisie:
    push {r1-r7,lr}        @ save registers
    mov r0,#STDIN          @ Linux input console
    ldr r1,iAdrsBuffer     @ buffer address 
    mov r2,#BUFFERSIZE     @ buffer size 
    mov r7,#READ           @ request to read datas
    svc 0                  @ call system
    ldr r1,iAdrsBuffer     @ buffer address 
    ldrb r0,[r1]           @ load first character
100:
    pop {r1-r7,lr}
    bx lr                   @ return 
iAdrsBuffer:         .int sBuffer
/***************************************************/
/*   Generation random number                  */
/***************************************************/
/* r0 contains limit  */
genereraleas:
    push {r1-r4,lr}                   @ save registers 
    ldr r4,iAdriGraine
    ldr r2,[r4]
    ldr r3,iNbDep1
    mul r2,r3,r2
    ldr r3,iNbDep1
    add r2,r2,r3
    str r2,[r4]                       @ maj de la graine pour l appel suivant 
    cmp r0,#0
    beq 100f
    mov r1,r0                         @ divisor
    mov r0,r2                         @ dividende
    bl division
    mov r0,r3                         @ résult = remainder
  
100:                                  @ end function
    pop {r1-r4,lr}                    @ restaur registers
    bx lr                             @ return
/*****************************************************/
iAdriGraine: .int iGraine
iNbDep1:     .int 0x343FD
iNbDep2:     .int 0x269EC3 
/***************************************************/
/*      ROUTINES INCLUDE                           */
/***************************************************/
.include "../affichage.inc"
The first move is human move.
The running total is 0
Enter your choice (1,2, 3 or type (q)uit to exit): 4
invalid choice.
 Enter your choice (1,2, 3 or type (q)uit to exit): 1
Raspberry choice if  1
The running total is 2
Enter your choice (1,2, 3 or type (q)uit to exit): 3
Raspberry choice if  2
The running total is 7
Enter your choice (1,2, 3 or type (q)uit to exit): 1
Raspberry choice if  1
The running total is 9
Enter your choice (1,2, 3 or type (q)uit to exit): 2
Raspberry choice if  2
The running total is 13
Enter your choice (1,2, 3 or type (q)uit to exit): 3
Raspberry choice if  1
The running total is 17
Enter your choice (1,2, 3 or type (q)uit to exit): 1
Raspberry choice if  3
You lost.

New game (y/n) ?
y
The first move is Raspberry pi.

Arturo

print "-----------------------------"
print " Welcome to 21 Game"
print "-----------------------------"

players: ["A" "B"]
currentPlayer: sample players
nextPlayer: first players -- currentPlayer
runningTotal: new 0
num: 0

getNum: function [][
    result: strip input "Enter a number (1,2,3) / x to exit: "
    if result="x" -> exit
    return result
]

loop.forever @[currentPlayer nextPlayer] 'plays [
    print ["Running total:" runningTotal]
    print ["Player" plays "turn"]

    num: getNum

    while [or? [not? numeric? num][not? contains? 1..3 to :integer num]][
        num: getNum
    ]

    runningTotal: runningTotal + to :integer num

    print ""

    if runningTotal=21 [
        print ["Running total is 21. Player" plays "won!"]
        exit
    ]
]
Output:
-----------------------------
 Welcome to 21 Game
-----------------------------
Running total: 0 
Player B turn 
Enter a number (1,2,3) / x to exit: 1

Running total: 1 
Player A turn 
Enter a number (1,2,3) / x to exit: 2

Running total: 3 
Player B turn 
Enter a number (1,2,3) / x to exit: 1

Running total: 4 
Player A turn 
Enter a number (1,2,3) / x to exit: 3

Running total: 7 
Player B turn 
Enter a number (1,2,3) / x to exit: 4
Enter a number (1,2,3) / x to exit: 2

Running total: 9 
Player A turn 
Enter a number (1,2,3) / x to exit: 3

Running total: 12 
Player B turn 
Enter a number (1,2,3) / x to exit: 1

Running total: 13 
Player A turn 
Enter a number (1,2,3) / x to exit: 2

Running total: 15 
Player B turn 
Enter a number (1,2,3) / x to exit: 3

Running total: 18 
Player A turn 
Enter a number (1,2,3) / x to exit: 3

Running total is 21. Player A won!

AutoHotkey

Gui, font, S16
Gui, add, Radio, vRadioC , Cake
Gui, add, Radio, vRadioE x+0 Checked, Easy
Gui, add, Radio, vRadioH x+0, Hard
Gui, add, text, xs vT, Total : 00
Gui, add, text, xs vComputer, Computer Dealt 00
Gui, add, text, xs Section, Player 1
loop, 3
	Gui, add, button, x+5 vPlayer1_%A_Index% gTotal, % A_Index 
Gui, add, button, xs vReset gReset, reset
Gui show,, 21 Game
Winners := [1,5,9,13,17,21]
gosub, reset
return
;-----------------------------------
reset:
total := 0
GuiControl,, T, % "Total : " SubStr("00" Total, -1)
GuiControl,, Computer, % "Computer Waiting"
Loop 3
	GuiControl, Enable, % "Player1_" A_Index
Random, rnd, 0, 1
if rnd
{
	Loop 3
		GuiControl, Disable, % "Player1_" A_Index
	gosub ComputerTurn
}
return
;-----------------------------------
Total:
Added := SubStr(A_GuiControl, 9,1)
Total += Added
GuiControl,, T, % "Total : " SubStr("00" Total, -1)
if (total >= 21)
{
	MsgBox % "Player 1 Wins"
	gosub reset
	return
}
Loop 3
	GuiControl, Disable, % "Player1_" A_Index
gosub, ComputerTurn
return
;-----------------------------------
ComputerTurn:
Gui, Submit, NoHide
Sleep 500
if RadioE
{
	if (total < 13)
		RadioC := true
	else
		RadioH := true		
}
if RadioC
{
	Random, Deal, 1, 3
	total += Deal
}
if RadioH
{
	for i, v in Winners
		if (total >= v)
			continue
		else
		{
			Deal := v - total
			if Deal > 3
				Random, Deal, 1, 3
			total += Deal
			break
		}
}
GuiControl,, T, % "Total : " SubStr("00" Total, -1)
GuiControl,, Computer, % "Computer Dealt " Deal
if (total=21)
{
	MsgBox Computer Wins
	gosub, reset
}
else
	Loop 3
		GuiControl, Enable, % "Player1_" A_Index
return

AWK

#  Game 21 - an example in AWK language for Rosseta Code.

BEGIN {
    srand();
    GOAL = 21;
    best[0] = 1; best[1] = 1; best[2] = 3; best[3] = 2;

    print\
        "21 Game                                                          \n"\
        "                                                                 \n"\
        "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";

    newgame();
    prompt();

}

/[123]/ {
    move = strtonum($0);
    if (move + total <= GOAL) {
        update("human", strtonum($0));
        update("ai", best[total % 4]);
    }
    else
        invalid();
    prompt();
}
/[^123]/ {
    if ($0 == "quit") {
        print "goodbye";
        exit;
    }
    else {
        invalid();
        prompt();
    }
}

function prompt(){
    print "enter your choice (or type quit to exit): ";
}

function invalid(){
    print "invalid move";
}

function newgame() {
    print "\n---- NEW GAME ----\n";
    print "\nThe running total is currently zero.\n";
    total = 0;
    if (rand() < 0.5) {
        print "The first move is AI move.\n";
        update("ai", best[total % 4]);
    }
    else
        print "The first move is human move.\n";
}

function update(player, move) {
    printf "%8s:  %d = %d + %d\n\n", player, total + move, total, move;
    total += move;
    if (total == GOAL) {
        printf "The winner is %s.\n\n", player;
        newgame();
    }
}
Output:
gawk -f Game21.awk
21 Game

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.



---- NEW GAME ----


The running total is currently zero.

The first move is human move.

enter your choice (or type quit to exit):
1
   human:  1 = 0 + 1

      ai:  2 = 1 + 1

enter your choice (or type quit to exit):
2
   human:  4 = 2 + 2

      ai:  5 = 4 + 1

enter your choice (or type quit to exit):
3
   human:  8 = 5 + 3

      ai:  9 = 8 + 1

enter your choice (or type quit to exit):
1
   human:  10 = 9 + 1

      ai:  13 = 10 + 3

enter your choice (or type quit to exit):
2
   human:  15 = 13 + 2

      ai:  17 = 15 + 2

enter your choice (or type quit to exit):
1
   human:  18 = 17 + 1

      ai:  21 = 18 + 3

The winner is ai.


---- NEW GAME ----


The running total is currently zero.

The first move is human move.

enter your choice (or type quit to exit):
wow
invalid move
222
invalid move
enter your choice (or type quit to exit):
1
   human:  1 = 0 + 1

      ai:  2 = 1 + 1

enter your choice (or type quit to exit):
quit
goodbye

BASIC

BASIC256

Translation of: FreeBASIC
PLAYER = 1
COMP   = 0

sum = 0
total = 0
turn = int(rand + 0.5)
dim precomp = {1, 1, 3, 2}

while sum < 21
    turn = 1 - turn
    print "The sum is "; sum
    if turn = PLAYER then
        print "It is your turn."
        while total < 1 or total > 3 or total + sum > 21
            input "How many would you like to total? ", total
        end while
    else
        print "It is the computer's turn."
        total = precomp[sum mod 4]
        print "The computer totals ", total, "."
    end if
    print
    sum += total
    total = 0
end while

if turn = PLAYER then
    print "Congratulations. You win."
else
    print "Bad luck. The computer wins."
end if

Chipmunk Basic

Translation of: Commodore BASIC
Works with: Chipmunk Basic version 3.6.4
Works with: MSX BASIC
100 rem 21 game
110 rem for rosetta code
120 '
130 rem initialization
140 l$ = chr$(157) : rem left cursor
150 dim p$(2),hc(2),ca(4) : hc(1) = 0 : hc(2) = 0 : rem players
160 ca(0) = 1 : ca(1) = 1 : ca(2) = 3 : ca(3) = 2 : rem computer answers
170 dim cn$(6) : for i = 1 to 6 : read cn$(i) : next i : rem computer names
180 def fnm(X)=(X-(INT(X/4))*4):REM modulo function
190 '
200 rem optionally set screen colors here
210 cls
220 print "                21 GAME"
230 print : print " The goal of this game is to take turns"
240 print " adding the value of either 1, 2, or 3"
250 print " to a running total. The first player"
260 print " to bring the total to 21..."
270 print : print "          ... WINS THE GAME!"
280 print : gosub 1110
290 for p = 1 to 2
300 '
310  rem game setup and get players
320  for p = 1 to 2
330   print : print "Player ";p;l$;", [H]uman or [C]omputer? ";
340   k$ = inkey$ : if k$ <> "c" and k$ <> "h" then 340
350   print k$ : hc(p) = (k$ = "c")
360   print : print "Player";p;l$ "," : print "Enter your name"; : if hc(p) then goto 400
370   input p$(p)
380  next p
390  goto 420
400  gosub 1340 : print "? ";p$(p)
410 next p
420 print
430 for p = 1 to 2 : print p;l$;". ";p$(p) : next p
440 print : print "Is this correct (y/n)? ";
450 k$ = inkey$ : if k$ <> "y" and k$ <> "n" then 450
460 print k$ : if k$ = "n" then goto 290
470 print : print "Who will play first (1 or 2)? ";
480 k$ = inkey$ : if k$ < "1" or k$ > "2" then 480
490 fp = asc(k$)-48 : print k$ : print
500 print "Okay, ";p$(fp);" will play first." : print : gosub 1110
510 cls
520 '
530 rem start main game loop
540 pi = fp : rt = 0
550 print "Total so far:";rt
560 print : print p$(pi);"'s turn."
570 if hc(pi) then gosub 1240
580 if not hc(pi) then gosub 1170
590 rt = rt+ad
600 if rt = 21 then goto 680
610 if rt > 21 then print : print p$(pi);" loses by going over 21!!" : goto 700
620 pi = pi+1 : if pi > 2 then pi = 1
630 goto 550
640 print : print "          ... WINS THE GAME!"
650 print : gosub 1110
660 '
670 for p = 1 to 2
680  rem winner winner chicken dinner
690  print : print "21! ";p$(pi);" wins the game!!!"
700  print : print "Would you like to play again? ";
710  k$ = inkey$ : if k$ <> "n" and k$ <> "y" then 710
720  print k$
730  if k$ = "n" then print : print "Okay, maybe another time. Bye!" : end
740  goto 200
750  print k$ : hc(p) = (k$ = "c")
760  print : print "Player";p;l$ "," : print "Enter your name"; : if hc(p) then goto 800
770  input p$(p)
780 next p
790 goto 820
800 gosub 1340 : print "? ";p$(p)
810 next p
820 print : for p = 1 to 2 : print p;l$;". ";p$(p) : next p
830 print : print "Is this correct (y/n)? ";
840 k$ = inkey$ : if k$ <> "y" and k$ <> "n" then 840
850 print k$ : if k$ = "n" then goto 660
860 print : print "Who will play first (1 or 2)? ";
870 k$ = inkey$ : if k$ < "1" or k$ > "2" then 870
880 fp = asc(k$)-48 : print k$ : print
890 print "Okay, ";p$(fp);" will play first." : print : gosub 1110
900 '
910 rem start main game loop
920 pi = fp : rt = 0
930 print chr$(147);"Total so far: ";rt
940 print : print p$(pi);"'s turn."
950 if hc(pi) then gosub 1240
960 if not hc(pi) then gosub 1170
970 rt = rt+ad
980 if rt = 21 then goto 1030
990 if rt > 21 then print : print p$(pi);" loses by going over 21!!" : goto 1050
1000 pi = pi+1 : if pi > 2 then pi = 1
1010 goto 930
1020 '
1030 rem winner winner chicken dinner
1040 print : print "21! ";p$(pi);" wins the game!!!"
1050 print : print "Would you like to play again? ";
1060 k$ = inkey$ : if k$ <> "n" and k$ <> "y" then 1060
1070 print k$
1080 if k$ = "n" then print : print "Okay, maybe another time. Bye!" : end
1090 goto 490
1100 '
1110 rem pause for keypress
1120 z$ = " Press a key to continue. "
1130 print z$
1140 k$ = inkey$ : if k$ = "" then 1140
1150 return
1160 '
1170 rem human player move
1180 print : print "How much to add,"
1190 print "1, 2, or 3 (0 to quit)"; : input ad
1200 if ad < 0 or ad > 3 then print : print "Illegal amount. Try again." : goto 1180
1210 if ad = 0 then print : print "Game was ended by ";p$(pi);"." : end
1220 return
1230 '
1240 rem computer player move
1250 print : print "Thinking...";
1260 tt = int(rnd(1)*10)
1270 for t = 1 to tt : print "."; : for i = 1 to 250 : next i : next t : print
1280 rm = fn m(rt)
1290 ad = ca(rm)
1300 print : print p$(pi);" adds ";ca(rm);l$;"."
1310 for t = 1 to 1000 : next t
1320 return
1330 '
1340 rem pick a computer name
1350 pn = int(rnd(1)*6)+1 : t$ = cn$(pn)
1360 if t$ = p$(p-1) then goto 1350
1370 p$(p) = t$
1380 return
1390 '
1400 rem some computer names to pick from
1410 data "Commodore 64","VIC-20","Commodore 128"
1420 data "PET","Plus/4","Commodore 16"

IS-BASIC

100 PROGRAM "21Game.bas"
110 RANDOMIZE 
120 LET SUM,ADD=0
130 LET TURN=RND(2)-1
140 CLEAR SCREEN
150 PRINT "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."
160 PRINT "The running total starts at zero. One player will be the computer.":PRINT 
170 DO 
180   LET TURN=NOT TURN
190   SET #102:INK 3:PRINT "The sum is";SUM:SET #102:INK 1
200   IF TURN THEN
210     PRINT "It is your turn.":PRINT "How many would you like to add? (1-3): ";
220     LET ADD=READKEY
230     IF ADD>21-SUM THEN PRINT "You can only add";21-SUM
240   ELSE 
250     LET ADD=4-MOD(SUM-1,4)
260     IF ADD=4 THEN LET ADD=RND(3)+1
270     PRINT "It is the computer's turn.":PRINT "The computer adds";ADD
280   END IF 
290   PRINT :LET SUM=SUM+ADD
300 LOOP WHILE SUM<21
310 IF TURN THEN
320   PRINT "Congratulations. You win."
330 ELSE 
340   PRINT "Bad luck. The computer wins."
350 END IF 
360 END 
370 DEF READKEY
380   DO 
390     LET T$=INKEY$
400   LOOP WHILE T$>"3" OR T$<"1"
410   PRINT T$:LET READKEY=VAL(T$)
420 END DEF

MSX Basic

Works with: MSX BASIC version any

The Chipmunk Basic solution works without any changes.

PureBasic

Translation of: FreeBASIC
If OpenConsole()
  PLAYER.i = 1
  COMP.i   = 0
  
  sum.i = 0
  total.i = 0
  turn.i = Random(1) + 0.5
  Dim precomp.i(3)
  precomp(0) = 1 
  precomp(1) = 1
  precomp(2) = 3
  precomp(3) = 2
  
  While sum < 21
    turn = 1 - turn
    PrintN("The sum is " + Str(sum))
    If turn = PLAYER
      PrintN("It is your turn.")
      While total < 1 Or total > 3 Or total + sum > 21
        Print("How many would you like to total? ")
        total = Val(Input())
      Wend
    Else
      PrintN("It is the computer's turn.")
      total = precomp(sum % 4)
      PrintN("The computer totals " + Str(total) + ".")
    EndIf
    PrintN("")
    sum + total
    total = 0
  Wend
  
  If turn = PLAYER
    PrintN("Congratulations. You win.")
  Else
    PrintN("Bad luck. The computer wins.")
  EndIf
  
  Input()
EndIf

QBasic

Translation of: FreeBASIC
Works with: QBasic version 1.1
Works with: QuickBasic version 4.5
PLAYER = 1
COMP   = 0
RANDOMIZE TIMER

sum = 0
total = 0
turn = INT(RND + 0.5)
DIM precomp(0 TO 3)
precomp(0) = 1 : precomp(1) = 1
precomp(2) = 3 : precomp(3) = 2

WHILE sum < 21
    turn = 1 - turn
    PRINT "The sum is "; sum
    IF turn = PLAYER THEN
        PRINT "It is your turn."
        WHILE total < 1 OR total > 3 OR total + sum > 21
            INPUT "How many would you like to total? ", total
        WEND
    ELSE
        PRINT "It is the computer's turn."
        total = precomp(sum MOD 4)
        PRINT "The computer totals"; total; "."
    END IF
    PRINT
    sum = sum + total
    total = 0
WEND

IF turn = PLAYER THEN
    PRINT "Congratulations. You win."
ELSE
    PRINT "Bad luck. The computer wins."
END IF

True BASIC

Translation of: QBasic
LET player = 1
LET comp = 0
RANDOMIZE
LET sum = 0
LET total = 0
LET turn = INT(RND+0.5)
DIM precomp(0 TO 3)
LET precomp(0) = 1
LET precomp(1) = 1
LET precomp(2) = 3
LET precomp(3) = 2
DO WHILE sum < 21
   LET turn = 1-turn
   PRINT "The sum is "; sum
   IF turn = player THEN
      PRINT "It is your turn."
      DO WHILE total < 1 OR total > 3 OR total+sum > 21
         INPUT prompt "How many would you like to total? ": total
      LOOP
   ELSE
      PRINT "It is the computer's turn."
      LET total = precomp(remainder(round(sum),4))
      PRINT USING "The computer totals #.": total
   END IF
   PRINT
   LET sum = sum+total
   LET total = 0
LOOP
IF turn = player THEN PRINT "Congratulations. You win." ELSE PRINT "Bad luck. The computer wins."
END

PureBasic

Translation of: FreeBASIC
If OpenConsole()
  PLAYER.i = 1
  COMP.i   = 0
  
  sum.i = 0
  total.i = 0
  turn.i = Random(1) + 0.5
  Dim precomp.i(3)
  precomp(0) = 1 
  precomp(1) = 1
  precomp(2) = 3
  precomp(3) = 2
  
  While sum < 21
    turn = 1 - turn
    PrintN("The sum is " + Str(sum))
    If turn = PLAYER
      PrintN("It is your turn.")
      While total < 1 Or total > 3 Or total + sum > 21
        Print("How many would you like to total? ")
        total = Val(Input())
      Wend
    Else
      PrintN("It is the computer's turn.")
      total = precomp(sum % 4)
      PrintN("The computer totals " + Str(total) + ".")
    EndIf
    PrintN("")
    sum + total
    total = 0
  Wend
  
  If turn = PLAYER
    PrintN("Congratulations. You win.")
  Else
    PrintN("Bad luck. The computer wins.")
  EndIf
  
  Input()
EndIf

Yabasic

Translation of: FreeBASIC
PLAYER = 1
COMP   = 0

sum = 0 
total = 0
turn = int(ran() + 0.5)
dim precomp(3)
precomp(0) = 1 : precomp(1) = 1
precomp(2) = 3 : precomp(3) = 2

while sum < 21
    turn = 1 - turn
    print "The sum is ", sum
    if turn = PLAYER then
        print "It is your turn."
        while total < 1 or total > 3 or total + sum > 21
            input "How many would you like to total? " total
        wend
    else
        print "It is the computer's turn."
        total = precomp(mod(sum, 4))
        print "The computer totals ", total, "."
    fi
    print
    sum = sum + total
    total = 0
wend

if turn = PLAYER then
    print "Congratulations. You win."
else
    print "Bad luck. The computer wins."
fi

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

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.
 */


/*
 * Turn off MS Visual Studio panic warnings which disable to use old gold
 * library functions like printf, scanf etc. This definition should be harmless
 * for non-MS compilers.
 */
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/*
 * Define bool, true and false as needed. The stdbool.h is a standard header
 * in C99, therefore for older compilers we need DIY booleans. BTW, there is
 * no __STDC__VERSION__ predefined macro in MS Visual C, therefore we need
 * check also _MSC_VER.
 */
#if __STDC_VERSION__ >= 199901L || _MSC_VER >= 1800
#include <stdbool.h>
#else
#define bool int
#define true  1
#define false 0
#endif

#define GOAL               21
#define NUMBER_OF_PLAYERS   2
#define MIN_MOVE            1
#define MAX_MOVE            3
#define BUFFER_SIZE       256

#define _(STRING) STRING


/*
 * Spaces are meaningful: on some systems they can be visible.
 */
static char DESCRIPTION[] = 
    "21 Game                                                          \n"
    "                                                                 \n"
    "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";

static int total;


void update(char* player, int move)
{
    printf("%8s:  %d = %d + %d\n\n", player, total + move, total, move);
    total += move;
    if (total == GOAL)
        printf(_("The winner is %s.\n\n"), player);
}


int ai()
{
/*
 * There is a winning strategy for the first player. The second player can win
 * then and only then the frist player does not use the winning strategy.
 * 
 * The winning strategy may be defined as best move for the given running total.
 * The running total is a number from 0 to GOAL. Therefore, for given GOAL, best
 * moves may be precomputed (and stored in a lookup table). Actually (when legal
 * moves are 1 or 2 or 3) the table may be truncated to four first elements.
 */
#if GOAL < 32 && MIN_MOVE == 1 && MAX_MOVE == 3
    static const int precomputed[] = { 1, 1, 3, 2, 1, 1, 3, 2, 1, 1, 3, 2, 1, 1,
        3, 2, 1, 1, 3, 2, 1, 1, 3, 2, 1, 1, 3, 2, 1, 1, 3 };
    update(_("ai"), precomputed[total]);
#elif MIN_MOVE == 1 && MAX_MOVE == 3
    static const int precomputed[] = { 1, 1, 3, 2};
    update(_("ai"), precomputed[total % (MAX_MOVE + 1)]);
#else
    int i;
    int move = 1;
    for (i = MIN_MOVE; i <= MAX_MOVE; i++)
        if ((total + i - 1) % (MAX_MOVE + 1) == 0)
            move = i;
    for (i = MIN_MOVE; i <= MAX_MOVE; i++)
        if (total + i == GOAL)
            move = i;
    update(_("ai"), move);
#endif
}


void human(void)
{
    char buffer[BUFFER_SIZE];
    int move;
 
    while ( printf(_("enter your move to play (or enter 0 to exit game): ")),
            fgets(buffer, BUFFER_SIZE, stdin), 
            sscanf(buffer, "%d", &move) != 1 ||
            (move && (move < MIN_MOVE || move > MAX_MOVE || total+move > GOAL)))
        puts(_("\nYour answer is not a valid choice.\n"));
    putchar('\n');
    if (!move) exit(EXIT_SUCCESS);
    update(_("human"), move);
}


int main(int argc, char* argv[])
{
    srand(time(NULL));
    puts(_(DESCRIPTION));
    while (true)
    {
        puts(_("\n---- NEW GAME ----\n"));
        puts(_("\nThe running total is currently zero.\n"));
        total = 0;

        if (rand() % NUMBER_OF_PLAYERS)
        {
            puts(_("The first move is AI move.\n"));
            ai();
        }
        else
            puts(_("The first move is human move.\n"));

        while (total < GOAL)
        {
            human();
            ai();
        }
    }
}
Output:
21 Game

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.



---- NEW GAME ----


The running total is currently zero.

The first move is AI move.

      AI:  1 = 0 + 1

enter your move to play (or enter 0 to exit game): 1

   human:  2 = 1 + 1

      AI:  5 = 2 + 3

enter your move to play (or enter 0 to exit game): 3

   human:  6 = 5 + 1

      AI:  9 = 6 + 3

enter your move to play (or enter 0 to exit game): asd ?

Your answer is not a valid choice.

enter your move to play (or enter 0 to exit game): -1

Your answer is not a valid choice.

enter your move to play (or enter 0 to exit game): 4

Your answer is not a valid choice.

enter your move to play (or enter 0 to exit game): 2

   human:  10 = 9 + 1

      AI:  13 = 10 + 3

enter your move to play (or enter 0 to exit game): 3

   human:  14 = 13 + 1

      AI:  17 = 14 + 3

enter your move to play (or enter 0 to exit game): 2

   human:  18 = 17 + 1

      AI:  21 = 18 + 3

The winner is AI.


---- NEW GAME ----


The running total is currently zero.

The first move is AI move.

      AI:  1 = 0 + 1

enter your move to play (or enter 0 to exit game): 1

   human:  2 = 1 + 1

      AI:  5 = 2 + 3

enter your move to play (or enter 0 to exit game): 2

   human:  6 = 5 + 1

      AI:  9 = 6 + 3

enter your move to play (or enter 0 to exit game): 3

   human:  10 = 9 + 1

      AI:  13 = 10 + 3

enter your move to play (or enter 0 to exit game): 0

C++

Model View Controller

/**
 *  Game 21 - an example in C++ language for Rosseta Code.
 *
 *  This version is an example of MVC architecture. It seems be a little cleaner
 *  than MVP. The friendship has broken encapsulations to avoid getters.
 */

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

using namespace std;
#define _(STR) STR


class Model
{
public:
    static const int GOAL = 21;
    static const int NUMBER_OF_PLAYERS = 2;
    static const int MIN_MOVE = 1;
    static const int MAX_MOVE = 3;
    int bestMove();
    bool update(const char* player, int move);
    bool isGameBegin();
    bool isGameOver();
protected:
    friend class View;
    View* view = nullptr;
    const char* player = nullptr;
    int oldTotal = 0;
    int newTotal = 0;
    int lastMove = 0;
};


class View
{
protected:
    Model* model;
public:
    View(Model* model);
    void init(const char* player);
    void update();
};


class Controller
{
protected:
    Model* model;
    View* view;
public:
    Controller(Model* model, View* view);
    int input();
    void clear();
    void run();
};


int Model::bestMove()
{
    // This is not the fastest algorithm. There is possible to precompute
    // and memorize all answers before game begin or even hard code them.

    int move = MIN_MOVE;
    for (int i = MIN_MOVE; i <= MAX_MOVE; i++)
        if ((newTotal + i - 1) % (MAX_MOVE + 1) == 0)
            move = i;
    for (int i = MIN_MOVE; i <= MAX_MOVE; i++)
        if (newTotal + i == GOAL)
            move = i;
    return move;
}

bool Model::update(const char* player, int move)
{
    if (move >= MIN_MOVE && move <= MAX_MOVE && newTotal + move <= GOAL)
    {
        this->player = player;
        oldTotal = newTotal;
        newTotal = oldTotal + move;
        lastMove = move;
        view->update();
        return true;
    }
    else
        return false;
}

bool Model::isGameBegin()
{
    return oldTotal == 0;
}

bool Model::isGameOver()
{
    return newTotal == GOAL;
}


View::View(Model* model)
{
    this->model = model;
    model->view = this;
}

void View::init(const char* player)
{
    if (model->newTotal == 0)
        cout << _("----NEW GAME----\n\n")
             << _("The running total is currently zero.\n")
             << _("The first move is ") << player << _(" move.\n\n");
}

void View::update()
{
    cout << setw(8) << model->player << ": " << model->newTotal << " = "
         << model->oldTotal << " + " << model->lastMove << endl << endl;
    if (model->isGameOver())
        cout << endl << _("The winner is ") << model->player << _(".\n\n\n");
}


Controller::Controller(Model* model, View* view)
{
    this->model = model;
    this->view = view;
}

void Controller::run()
{
    if (rand() % Model::NUMBER_OF_PLAYERS == 0)
    {
        view->init("AI");
        model->update("AI", model->bestMove());
    }
    else
        view->init("human");

    while (!model->isGameOver())
    {
        while (!model->update("human", input()))
            clear();
        model->update("AI", model->bestMove());
    }
}

int Controller::input()
{
    int value;
    cout << _("enter a valid number to play (or enter 0 to exit game): ");
    cin >> value;
    cout << endl;
    if (!cin.fail())
    {
        if (value == 0)
            exit(EXIT_SUCCESS);
        else
            return value;
    }
    else
        return model->MIN_MOVE - 1;
}

void Controller::clear()
{
    cout << _("Your answer is not a valid choice.") << endl;
    cin.clear();
    cin.ignore((streamsize)numeric_limits<int>::max, '\n');
}


int main(int argc, char* argv)
{
    srand(time(NULL));

    cout << _(
        "21 Game                                                          \n"
        "                                                                 \n"
        "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");

    while (true)
    {
        Model* model = new Model();
        View* view = new View(model);
        Controller* controler = new  Controller(model, view);

        controler->run();

        delete controler;
        delete model;
        delete view;
    }
    return EXIT_SUCCESS; // dead code
}

Model View Presenter

/**
 *  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".
 */

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


using namespace std;

#define _(STR) STR


class Model
{
protected:

    int oldTotal;
    int newTotal;
    int lastMove;

public:

    static const int GOAL = 21;
    static const int NUMBER_OF_PLAYERS = 2;

    Model()
    {
        newTotal = 0;
        oldTotal = 0;
        lastMove = 0;
    }

    void update(int move)
    {
        oldTotal = newTotal;
        newTotal = oldTotal + move;
        lastMove = move;
    }

    int getOldTotal()
    {
        return oldTotal;
    }

    int getNewTotal()
    {
        return newTotal;
    }

    int getLastMove()
    {
        return lastMove;
    }

    bool isEndGame()
    {
        return newTotal == GOAL;
    }
};


class View
{
public:

    void update(string comment, Model* model)
    {
        cout << setw(8) << comment << ": "
            << model->getNewTotal()
            << " = "
            << model->getOldTotal()
            << " + "
            << model->getLastMove() << endl
            << endl;
    }

    void newGame(string player)
    {
        cout << _("----NEW GAME----") << endl
            << endl
            << _("The running total is currently zero.") << endl
            << endl
            << _("The first move is ") << player << _(" move.") << endl
            << endl;
    }

    void endGame(string name)
    {
        cout << endl << _("The winner is ") << name << _(".") << endl
            << endl
            << endl;
    }
};


class Controller
{
public:

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


class AI : public Controller
{
public:

    string getName()
    {
        return _("AI");
    }

    int getMove(Model* model)
    {
        int n = model->getNewTotal();
        for (int i = 1; i <= 3; i++)
            if (n + i == Model::GOAL)
                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
{
public:

    string getName()
    {
        return _("human");
    }

    int getMove(Model* model)
    {
        int n = model->getNewTotal();
        int value;
        while (true) {
            if (n == Model::GOAL - 1)
                cout << _("enter 1 to play (or enter 0 to exit game): ");
            else if (n == Model::GOAL - 2)
                cout << _("enter 1 or 2 to play (or enter 0 to exit game): ");
            else
                cout << _("enter 1 or 2 or 3 to play (or enter 0 to exit game): ");
            cin >> value;
            if (!cin.fail()) {
                if (value == 0)
                    exit(0);
                else if (value >= 1 && value <= 3 && n + value <= Model::GOAL)
                {
                    cout << endl;
                    return value;
                }
            }
            cout << _("Your answer is not a valid choice.") << endl;
            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()
    {
        int player = rand() % Model::NUMBER_OF_PLAYERS;
        view->newGame(controllers[player]->getName());

        while (true)
        {
            Controller* controller = controllers[player];
            model->update(controller->getMove(model));
            view->update(controller->getName(), model);
            if (model->isEndGame())
            {
                view->endGame(controllers[player]->getName());
                break;
            }
            player = (player + 1) % Model::NUMBER_OF_PLAYERS;
        }
    }
};


int main(int argc, char* argv)
{
    srand(time(NULL));

    while (true)
    {
        Model* model = new Model();
        View* view = new View();
        Controller* controllers[Model::NUMBER_OF_PLAYERS];
        controllers[0] = new Human();
        for (int i = 1; i < Model::NUMBER_OF_PLAYERS; i++)
            controllers[i] = 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
}
Output:
----NEW GAME----

The running total is currently zero.

The first move is human move.

enter 1 or 2 or 3 to play (or enter 0 to exit game): 1

   human: 1 = 0 + 1

      AI: 4 = 1 + 3

enter 1 or 2 or 3 to play (or enter 0 to exit game): 2

   human: 6 = 4 + 2

      AI: 9 = 6 + 3

enter 1 or 2 or 3 to play (or enter 0 to exit game): nope
Your answer is not a valid choice.
enter 1 or 2 or 3 to play (or enter 0 to exit game): 8
Your answer is not a valid choice.
enter 1 or 2 or 3 to play (or enter 0 to exit game): 3

   human: 12 = 9 + 3

      AI: 13 = 12 + 1

enter 1 or 2 or 3 to play (or enter 0 to exit game): 2

   human: 15 = 13 + 2

      AI: 17 = 15 + 2

enter 1 or 2 or 3 to play (or enter 0 to exit game): 3

   human: 20 = 17 + 3

      AI: 21 = 20 + 1


The winner is AI.


----NEW GAME----

The running total is currently zero.

The first move is human move.

enter 1 or 2 or 3 to play (or enter 0 to exit game): 1

   human: 1 = 0 + 1

      AI: 4 = 1 + 3

enter 1 or 2 or 3 to play (or enter 0 to exit game): 2

   human: 6 = 4 + 2

      AI: 9 = 6 + 3

enter 1 or 2 or 3 to play (or enter 0 to exit game): 3

   human: 12 = 9 + 3

      AI: 13 = 12 + 1

enter 1 or 2 or 3 to play (or enter 0 to exit game): 0

C#

// 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();
        }
    }
}

Commodore BASIC

Written for BASIC v2 on the Commodore 64, this should be mostly compatible with all Commodore machines running any version of BASIC v2 and above. This implementation allows for one or both players to be either human or computer players. Also note that the computer player's "Thinking..." routine is a purely random delay meant to allow any human user time to process the flow of the game.

1 rem 21 game
2 rem for rosetta code

3 rem initialization
4 l$=chr$(157):rem left cursor
5 dim p$(2),hc(2),ca(4):hc(1)=0:hc(2)=0:rem players
6 ca(0)=1:ca(1)=1:ca(2)=3:ca(3)=2:rem computer answers
7 dim cn$(6):for i=1 to 6:read cn$(i):next:rem computer names
8 def fn m(x)=(x-(int(x/4))*4):rem modulo function

10 rem optionally set screen colors here
20 print chr$(147);chr$(14):print spc(16);"21 GAME"
25 print:print " The goal of this game is to take turns"
26 print " adding the value of either 1, 2, or 3"
27 print " to a running total. The first player"
28 print " to bring the total to 21..."
29 print:print spc(10);"... WINS THE GAME!"
30 print:gosub 1000
35 print chr$(147):for p=1 to 2

40 rem game setup and get players
42 for p=1 to 2
44 print:print "Player";p;l$;", [H]uman or [C]omputer? ";
45 get k$:if k$<>"c" and k$<>"h" then 45
46 print k$:hc(p)=(k$="c")
50 print:print"Player";p;l$",":print"Enter your name";:if hc(p)then goto54
52 input p$(p):next p:goto 60
54 gosub 1500:print "? ";p$(p):next p
60 print:for p=1 to 2:print p;l$;". ";p$(p):next
65 print:print "Is this correct (y/n)? ";
70 get k$:if k$<>"y" and k$<>"n" then 70
75 print k$:if k$="n" then goto 35
80 print:print "Who will play first (1 or 2)? ";
85 get k$:if k$<"1" or k$>"2" then 85
90 fp=asc(k$)-48:print k$:print
95 print "Okay, ";p$(fp);" will play first.":print:gosub 1000

100 rem start main game loop
105 pi=fp:rt=0
110 print chr$(147);"Total so far:";rt
115 print:print p$(pi);"'s turn."
120 if hc(pi) then gosub 1200
125 if not hc(pi) then gosub 1100
130 rt=rt+ad
135 if rt=21 then goto 200
140 if rt>21 then print:print p$(pi);" loses by going over 21!!":goto 210
145 pi=pi+1:if pi>2 then pi=1
150 goto 110

200 rem winner winner chicken dinner
205 print:print "21! ";p$(pi);" wins the game!!!"
210 print:print "Would you like to play again? ";
215 get k$:if k$<>"n" and k$<>"y" then 215
220 print k$
225 if k$="n" then print:print "Okay, maybe another time. Bye!":end
230 goto 10

1000 rem pause for keypress
1010 z$=" Press a key to continue. "
1015 print spc(20-int(len(z$)/2));
1016 print chr$(18);z$;chr$(146)
1020 get k$:if k$="" then 1020
1025 return

1100 rem human player move
1105 print:print "How much to add,"
1110 print "1, 2, or 3 (0 to quit)";:input ad
1115 if ad<0 or ad>3 then print:print"Illegal amount. Try again.":goto1105
1120 if ad=0 then print:print "Game was ended by ";p$(pi);".":end
1125 return

1200 rem computer player move
1205 print:print "Thinking...";
1210 tt=int(rnd(1)*10)
1215 for t=1 to tt:print ".";:for i=1 to 250:next i,t:print
1220 rm=fn m(rt)
1225 ad=ca(rm)
1230 print:print p$(pi);" adds";ca(rm);l$;"."
1235 for t=1 to 1000:next t
1240 return

1500 rem pick a computer name
1505 pn=int(rnd(1)*6)+1:t$=cn$(pn)
1510 if t$=p$(p-1) then goto 1505
1515 p$(p)=t$
1520 return

2000 rem some computer names to pick from
2010 data "Commodore 64","VIC-20","Commodore 128","PET"
2020 data "Plus/4","Commodore 16"
Output:
                21 GAME                 
                                        
 The goal of this game is to take turns 
 adding the value of either 1, 2, or 3  
 to a running total. The first player   
 to bring the total to 21...            
                                        
          ... WINS THE GAME!            
                                        
        Press a key to continue.        


Player 1, [H]uman or [C]omputer? h      
                                        
Player 1,                               
Enter your name? Mike                   
                                        
Player 2, [H]uman or [C]omputer? c      
                                        
Player 2,                               
Enter your name? Commodore 64           
                                        
 1. Mike                                
 2. Commodore 64                        
                                        
Is this correct (y/n)? y                
                                        
Who will play first (1 or 2)? 1         
                                        
Okay, Mike will play first.             
                                        
        Press a key to continue.        


Total so far: 0

Mike's turn.                            
                                        
How much to add,                        
1, 2, or 3 (0 to quit)? 1               

Total so far: 1                         
                                        
Commodore 64's turn.                    
                                        
Thinking..........                      
                                        
Commodore 64 adds 1.       

Total so far: 2

Mike's turn.                            
                                        
How much to add,                        
1, 2, or 3 (0 to quit)? 3               

Total so far: 5                         
                                        
Commodore 64's turn.                    
                                        
Thinking..........                      
                                        
Commodore 64 adds 1.       



...[LATER IN THE GAME]...

Total so far: 17                        
                                        
Commodore 64's turn.                    
                                        
Thinking.........                       
                                        
Commodore 64 adds 1.

Total so far: 18

Mike's turn.                            
                                        
How much to add,                        
1, 2, or 3 (0 to quit)? 3 

21! Mike wins the game!!!               
                                        
Would you like to play again? n         
                                        
Okay, maybe another time. Bye!          
                                        
ready.                                  
█

Delphi

See Pascal[1]

EasyLang

The computer plays optimally

print "Who reaches 21, wins"
print "Do you want to begin (y/n)"
who = 1
if input = "n"
  who = 2
.
who$[] = [ "Human" "Computer" ]
repeat
  if who = 1
    repeat
      print ""
      print "Choose 1,2 or 3 (q for quit)"
      a$ = input
      n = number a$
      until a$ = "q" or (n >= 1 and n <= 3)
    .
  else
    sleep 1
    if sum mod 4 = 1
      n = random 3
    else
      n = 4 - (sum + 3) mod 4
    .
  .
  sum += n
  print who$[who] & ": " & n & " --> " & sum
  until sum >= 21 or a$ = "q"
  who = who mod 2 + 1
.
if a$ <> "q"
  print ""
  if who = 0
    print "Congratulation, you won"
  else
    print "Sorry, you lost"
  .
.

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
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
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!

F#

type Player =
    | Computer
    | Person
type Play = {runningTotal:int; nextTurn:Player}
type Win =
    | ByExact of Player
    | ByOtherExceeded of Player
type Status =
    | Start
    | Playing of Play
    | Winner of Win
    | Exit

let rnd = System.Random ()
let randomFirstPlayer () =
    let selection = rnd.Next 2
    if selection = 0 then Computer else Person
let computerChose current =
    if current > 17 then 21-current // win strategy
    else if current > 13 && current < 17 then 17-current // naive thwart opponent strategy
    else rnd.Next(1, (min 4 (21-current+1)))

let (|Exact|Exceeded|Under|) i = if i = 21 then Exact else if i > 21 then Exceeded else Under
let (|ValidNumber|InvalidInput|Quit|) (s:string) =
    let trimmed = s.Trim().ToLower()
    if trimmed = "1" || trimmed = "2" || trimmed = "3" then ValidNumber 
    else if trimmed = "q" then Quit
    else InvalidInput
    
let readable = function | Computer -> "Computer is" | Person -> "You are"

let rec looper = function
    | Start ->
        let firstPlayer = randomFirstPlayer ()
        printfn $"{readable firstPlayer} randomly selected to go first."
        looper (Playing {runningTotal=0; nextTurn=firstPlayer})
    | Playing play ->
        match play with
        | {runningTotal=Exact; nextTurn=Person} -> looper (Winner (ByExact Computer))
        | {runningTotal=Exact; nextTurn=Computer} -> looper (Winner (ByExact Person))
        | {runningTotal=Exceeded; nextTurn=player} -> looper (Winner (ByOtherExceeded player))
        | {runningTotal=r; nextTurn=player} ->
            match player with
            | Computer ->
                let computerChoice = computerChose r
                let total = r+computerChoice
                printfn $"Computer entered {computerChoice}. Current total now: {total}."
                looper (Playing {runningTotal=total; nextTurn=Person})
            | Person ->
                let input = printf "Enter number 1, 2, or 3 (or q to exit): "; System.Console.ReadLine ()
                match input with
                | ValidNumber ->
                    let playerChoice = System.Int32.Parse input
                    let total = r+playerChoice
                    printfn $"Player entered {playerChoice}. Current total now: {total}."
                    looper (Playing {runningTotal=total; nextTurn=Computer})
                | Quit -> looper Exit
                | InvalidInput -> printfn "Invalid input. Try again."; looper (Playing play)
    | Winner win ->
        match win with
        | ByExact player -> printfn $"{readable player} the winner by getting to 21."; looper Exit
        | ByOtherExceeded player -> printfn $"{readable player} the winner by not exceeding 21."; looper Exit
    | Exit -> printfn "Thanks for playing!"
    
let run () = looper Start
Output:
You are randomly selected to go first.
Enter number 1, 2, or 3 (or q to exit): 5
Invalid input. Try again.
Enter number 1, 2, or 3 (or q to exit): a
Invalid input. Try again.
Enter number 1, 2, or 3 (or q to exit): 3
Player entered 3. Current total now: 3.
Computer entered 3. Current total now: 6.
Enter number 1, 2, or 3 (or q to exit): 3
Player entered 3. Current total now: 9.
Computer entered 1. Current total now: 10.
Enter number 1, 2, or 3 (or q to exit): 2
Player entered 2. Current total now: 12.
Computer entered 2. Current total now: 14.
Enter number 1, 2, or 3 (or q to exit): 1
Player entered 1. Current total now: 15.
Computer entered 2. Current total now: 17.
Enter number 1, 2, or 3 (or q to exit): 2
Player entered 2. Current total now: 19.
Computer entered 2. Current total now: 21.
Computer is the winner by getting to 21.
Thanks for playing!

Forth

: READKEY
  1+ BEGIN
    KEY DUP 27 = ABORT" Bye!"
    48 - 2DUP > OVER 0 > AND IF
      DUP 48 + EMIT CR SWAP DROP EXIT
    THEN DROP
  REPEAT
;
: 21GAME CLS
  0 2 RND 1-
  ." 21 is a two player game." CR
  ." 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."
  BEGIN
    NOT
    CR ." The sum is " OVER . CR
    SWAP OVER IF
      ." How many would you like add?"
      ."  (1-3) " 3 READKEY
    ELSE
      ." It is the computer's turn."
      4 OVER 1- 4 MOD -
      DUP 4 = IF 3 RND 1+ MIN THEN
      DUP CR ." Computer adds " . CR
    THEN + SWAP
  OVER 21 < NOT UNTIL
  CR
  IF ." Congratulations. You win."
  ELSE ." Bad Luck. Computer wins."
  THEN CR DROP
;

Fortran

! game 21 - an example in modern fortran language for rosseta code.

subroutine ai
  common itotal, igoal
  if (itotal .lt. igoal) then
    move = 1
    do i = 1, 3
      if (mod(itotal + i - 1 , 4) .eq. 0) then
        move = i
      end if
    end do
    do i = 1, 3
      if (itotal + i .eq. igoal) then
        move = i
      end if
    end do
    print *, "      ai  ", itotal + move, " = ", itotal, " + ", move
    itotal = itotal + move
    if (itotal .eq. igoal) then
      print *, ""
      print *, "the winner is ai"
      print *, ""
    end if
  end if
end subroutine ai

subroutine human
  common itotal, igoal
  print *, ""
  do while (.true.)
    if (itotal + 1 .eq. igoal) then
      print *, "enter 1 (or 0 to exit): "
    else if (itotal + 2 .eq. igoal) then
      print *, "enter 1 or 2 (or 0 to exit): "
    else
      print *, "enter 1 or 2 or 3 (or 0 to exit)"
    end if
    read(*,*) move
    if (move .eq. 0) then
      stop
    else if (move .ge. 1 .and. move .le. 3 .and. move + itotal .le. igoal) then
      print *, "   human  ", itotal + move, " = ", itotal, " + ", move
      itotal = itotal + move
      if (itotal .eq. igoal) then
        print *, ""
        print *, "the winner is human"
        print *, ""
      end if
      return
    else
      print *, "a bad choice"
    end if
  end do
end subroutine human

program main
  common itotal, igoal
  print *,"game 21 - an example in fortran iv language for rosseta code."
  print *,""
  print *,"21 is a two player game, the game is played by choosing a number"
  print *,"(1, 2, or 3) to be added to the running total. the game is won"
  print *,"by the player whose chosen number causes the running total to reach"
  print *,"exactly 21. the running total starts at zero."
  print *,""
  i = irand(1)
  igoal = 21
  do while(.true.)
    print *, "---- new game ----"
    print *, ""
    print *, "the running total is currently zero."
    print *, ""
    itotal = 0
    if (mod(irand(0), 2) .eq. 0) then
      print *, "the first move is ai move."
      call ai
    else
      print *, "the first move is human move."
    end if
    print *, ""
    do while(itotal .lt. igoal)
      call human
      call ai
    end do
end do
end program main
Output:
game 21 - an example in fortran language for rosseta code.
 
 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.
 
 ---- new game ----
 
 the running total is currently zero.
 
 the first move is human move.
 
 
 enter 1 or 2 or 3 (or 0 to exit)
1
    human             1  =            0  +            1
       ai             2  =            1  +            1
 
 enter 1 or 2 or 3 (or 0 to exit)
3
    human             5  =            2  +            3
       ai             6  =            5  +            1
 
 enter 1 or 2 or 3 (or 0 to exit)
4
 a bad choice
 enter 1 or 2 or 3 (or 0 to exit)
3
    human             9  =            6  +            3
       ai            10  =            9  +            1
 
 enter 1 or 2 or 3 (or 0 to exit)
2
    human            12  =           10  +            2
       ai            13  =           12  +            1
 
 enter 1 or 2 or 3 (or 0 to exit)
3
    human            16  =           13  +            3
       ai            17  =           16  +            1
 
 enter 1 or 2 or 3 (or 0 to exit)
2
    human            19  =           17  +            2
       ai            21  =           19  +            2

 the winner is ai
 
 ---- new game ----
 
 the running total is currently zero.
 
 the first move is human move.
 
 
 enter 1 or 2 or 3 (or 0 to exit)
0

FreeBASIC

#define PLAYER 1
#define COMP   0
randomize timer

dim as uinteger sum, add = 0
dim as uinteger turn = int(rnd+0.5)
dim as uinteger precomp(0 to 3) = { 1, 1, 3, 2 } 

while sum < 21:
    turn = 1 - turn
    print using "The sum is ##"; sum
    if turn = PLAYER then
        print "It is your turn."
        while add < 1 orelse add > 3 orelse add+sum > 21
            input "How many would you like to add? ", add
        wend
    else
        print "It is the computer's turn."
        add = precomp(sum mod 4)
        print using "The computer adds #."; add
    end if
    print
    sum = sum + add
    add = 0
wend

if turn = PLAYER then
    print "Congratulations. You win."
else
    print "Bad luck. The computer wins."
end if

FutureBasic

A fairly simple game, but this FutureBasic code builds a full, stand-alone, interactive app for it in about 100 LOC. It includes three levels of play, selectable via a popup menu:

  • Min level has the computer play at random, choosing the optimal play only 1/3 of the time. Good for learning, or for kids.
  • Mid level gives the computer its optimal play 2/3 of the time, making for more varied and interesting play.
  • Max level always chooses the optimal play, meaning when the computer starts, it can't lose, which seems less fun.
begin globals
begin enum 1
  _btn1
  _btn2
  _btn3
  _sum
  _msg
  _popup
  _skill
  _human = 0
  _robot
end enum
str15 cr : cr = chr$(13)
str255 string(1) :  short who = _human
byte best(3), sum, num, skill = 1 //Medium skill
poke int @best(0), 33751297 //Initialize computer responses
end globals

local fn buildWindow
  subclass window 1,      @"21 Game in FutureBasic" , (  0,  0,640,200 )
  popupbutton _popup,, 1, @"Minimum;Medium;Maximum" , ( 80, 17,100, 32 )
  textlabel   _msg   ,    @"Original code: J. Reeve", ( 20,160,600, 32 )
  textlabel   _skill ,    @"Skill level:",            ( 20, 12, 75, 32 )
  textfield   _sum  ,,    @"0",( 530,85,90,60 )
  button      _btn1 ,,  , @"1",( 245,20,50,50 ),,NSBezelStyleRegularSquare
  button      _btn2 ,,  , @"2",( 295,20,50,50 ),,NSBezelStyleRegularSquare
  button      _btn3 ,,  , @"3",( 345,20,50,50 ),,NSBezelStyleRegularSquare
  ControlSetAlignment(    _msg, NSTextAlignmentCenter )
  ControlSetAlignment(    _sum, NSTextAlignmentCenter )
  TextFieldSetEditable(   _sum, no )
  ControlSetFontWithName( _msg,  @"Menlo", 15.0 )
  ControlSetFontWithName( _btn1, @"Menlo", 20.0 )
  ControlSetFontWithName( _btn2, @"Menlo", 20.0 )
  ControlSetFontWithName( _btn3, @"Menlo", 20.0 )
  ControlSetFontWithName( _sum,  @"Menlo", 32.0 )
  text @"menlo", 15.0
end fn

local fn show( add as byte )
  CFStringRef msg
  if (sum + add) > 21 then beep : exit fn
  button 1, who : button 2, who : button 3, who
  sum += add : cls
  ControlSetIntegerValue( _sum, sum )
  string( who ) +=  "+" + chr$( add or 48 ) + "    "
  print cr;cr;cr;string( _human );cr;cr;string( _robot )
  if sum < 21
    if who == _robot then msg = @"Your turn..." else msg = @"My turn..."
  else
    if who == _robot
      msg = @"Too bad. I win. Press any key to play again."
    else
      msg = @"Congratulations! YOU WIN! Press any key to play again."
    end if
  end if
  if peek( @string( who ) ) > 20 then textlabel _msg, msg
  who = who xor _robot
end fn

local fn play( add as byte )
  if sum + add > 21 then beep : exit fn
  fn show( add )       //Show human's play
  if sum < 21
    if (skill + maybe) > 0 then num = best(sum mod 4) else num = rnd(3)
    if sum + num > 21 then num = 21 - sum
    timerbegin 1.0, NO //wait a sec before playing
      fn show( num )   //Show computer's play
    timerend
  end if
end fn

local fn start
  sum = 0 : cls
  string( _human ) = "       You: " : string( _robot ) = "  Computer: "
  string( 1-who ) += "   "
  if who == _robot
    if skill == 2 then num = 1 else num = rnd( 3 )
    textlabel _msg, fn StringWithFormat( @"I'll start with %i.", num )
    fn show( num )
  else
    ControlSetIntegerValue( _sum, sum )
    textlabel _msg, @"You start. Type or click 1, 2, or 3."
  end if
end fn

local fn DoDialog( evt as long, tag as long)
  byte key
  select ( evt )
    case _windowKeyUp
      if sum == 21 then fn start : exit fn
      if who == _robot then exit fn
      key = intval( fn EventCharacters )
      if key > 0 && key < 4 then fn play( key )
    case _btnClick
      if tag < _popup then beep : fn play( tag ) : exit fn
      skill = popupbutton( _popup )
    case _windowWillClose : end
  end select
end fn

fn buildWindow
fn start
on dialog fn doDialog
handleevents
Output:

 

Gambas

' Gambas module file

Private gameConunt As Integer = 0
Private numRound As Integer = 1
Private winPlayer As Integer = 0
Private winComputer As Integer = 0


Public Sub Main()

  Dim entra As String

  Print "Enter q to quit at any time\nThe computer will choose first"

  While gameConunt < 21
    Print "ROUND: " & numRound & "\n"
    gameConunt += selectCount(gameConunt)
    Print Subst("Running total is now &1 \n ", gameConunt)
    If gameConunt = 21 Then
      Inc winComputer
      Print "The computer has won !\n"
      endGame()
    Endif
    gameConunt += humanCount(gameConunt)
    Print Subst("Running total is now &1", gameConunt)
    If gameConunt = 21 Then
      Inc winPlayer
      Print "Congratulations! You've won!\n"
      endGame()
    Endif
    Inc numRound
  Wend

End

Private Function selectCount(cou As Integer) As Integer

  Dim a As Integer

  Randomize

  If cou < 18 Then
    a = Int(Rnd(1, 4))
  Else
    a = 21 - cou
  Endif

  Print "The computer choose " & a

  Return a

End

Private Function humanCount(cou As Integer) As Integer

  Dim entra As String
  Dim a As Integer

  While True
    Print "Your choice 1 to 3"
    Input entra
    If entra = "q" Then
      Print "Good Bye!"
      Quit
    Endif
    Try a = CInt(entra)
    If Error Then
      Print "Invalid entry, try again"
      Continue
    Endif
    If a < 1 Or a > 3 Then
      Print "Out of range, try again"
      Continue
    Endif
    If a + cou > 21 Then
      Print "The sum is greater than 21, try again"
      Continue
    Endif    
    Return a
  Wend

End


Private Sub endGame()

  Dim entra As String

  Print Subst("Computer wins &1 games, human wins &2 games", winComputer, winPlayer)
  Print "\nPlay again? (y/n)"
  Input entra
  If entra = "y" Or entra = "Y" Then
    numRound = 1
    gameConunt = 0
    Print "Enter q to quit at any time\nThe computer will choose first"
  Else
    Print "Good Bye!"
    Quit
  Endif

End
Enter q to quit at any time
The computer will choose first
ROUND: 1

The computer choose 2
Running total is now 2 
 
Your choice 1 to 3
4
Out of range, try again
Your choice 1 to 3
0
Out of range, try again
Your choice 1 to 3
g
Invalid entry, try again
Your choice 1 to 3
1
Running total is now 3
ROUND: 2

The computer choose 2
Running total is now 5 
 
Your choice 1 to 3
3
Running total is now 8
ROUND: 3

The computer choose 2
Running total is now 10 
 
Your choice 1 to 3
3
Running total is now 13
ROUND: 4

The computer choose 2
Running total is now 15 
 
Your choice 1 to 3
3
Running total is now 18
ROUND: 5

The computer choose 3
Running total is now 21 
 
The computer has won !

computer wins 1 games, human wins 0 games

Play again? (y/n)

Good Bye!

GDScript

Works with: Godot version 4.0
extends MainLoop


enum Player {Human, Computer}

const target: int = 21


# -1 is returned to request quit
func get_player_choice(total: int) -> int:
	while true:
		printraw("Player's choice: ")
		var input: String = OS.read_string_from_stdin().strip_edges()

		if input == "quit":
			return -1

		if not input.is_valid_int():
			print("Please input an integer.")
			continue

		var input_int := int(input)
		if input_int < 1 or input_int > 3:
			print("Please pick a number from 1 to 3")
			continue

		if input_int + total > target:
			print("Please do not exceed a total of %d" % target)
			continue

		return input_int

	# This is required since Godot does not detect the unreachable code path
	OS.crash("unreachable")
	return 0


func get_computer_choice(total: int) -> int:
	# This can be shortened using max() if you do not have type checking OCD
	var remaining: int = target - total
	return randi_range(1, 3 if remaining > 3 else remaining)


func play() -> void:
	print("""\
Welcome to the 21 game.
Type quit to exit.""")
	var total: int = 0
	var player: Player
	match randi_range(1, 2):
		1:
			player = Player.Human
			print("You will go first.")
		2:
			player = Player.Computer
			print("The computer will go first.")

	while true:
		print("\nThe total is %d" % total)

		var choice: int
		match player:
			Player.Human:
				choice = get_player_choice(total)
				if choice == -1:
					return
			Player.Computer:
				choice = get_computer_choice(total)
				print("The computer plays %d" % choice)
		assert(1 <= choice and choice <= 3)

		total += choice
		if total == target:
			match player:
				Player.Human: print("You win!")
				Player.Computer: print("The computer won, you lose.")
			return
		assert(total < target)

		match player:
			Player.Human: player = Player.Computer
			Player.Computer: player = Player.Human


func _process(_delta: float) -> bool:
	randomize()
	play()
	return true

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.

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
        }
    }
}
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.

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"

J

The main definition g21 starts the game. J doesn't support niladic verbs. Although the y argument has no effect, it must be given. The empty literal vector '' types quickly, hence often used to trigger niladic verbs. g21 does not superfluously penalize final sums exceeding 21.

g21=: summarize@play@setup ::'g21: error termination'

Until=: {{u^:(0-:v)^:_}}

'score turn choice'=: 0 1 2

play=: move Until done
move=: [: update you`it@.(turn&{) 
done=: 21 <: score&{


update=: swap@display@add
add=: score}~ score&{ + choice&{
display=: [ 'sum: {}' echo@format~ score&{
swap=: turn}~ [: -. turn&{

it=: ([ 'It chose {}.' echo@format~ choice&{)@(choice}~ cb)
cb=: 1:`(>:@?@3)`3:`2:@.(4 | score&{)     NB. computer brain

you=: qio1@check@acquire@prompt
prompt=: [ echo@'your choice?'
acquire=: choice}~'123'i.0{' ',~read
check=: (choice}~ 665 - score&{)@([ echo@'g21: early termination')^:(3 = choice&{)
qio1=: choice}~ ([: >: choice&{)

setup=: ([ echo@'On your turn enter 1 2 or 3, other entries exit')@(?@2 turn} ])@0 0 0  NB. choose first player randomly

summarize=: ' won',~ ];._2@'it you '{~turn&{

read=: 1!:1@1
write=: 1!:2&4 NB. unused
format=: ''&$: : ([: ; (a: , [: ":&.> [) ,. '{}' ([ (E. <@}.;._1 ]) ,) ])
   g21''
On your turn enter 1 2 or 3, other entries exit
It chose 1.
sum: 1
your choice?
q
g21: early termination
sum: 666
you won

   
   g21''
On your turn enter 1 2 or 3, other entries exit
your choice?
2
sum: 2
It chose 3.
sum: 5
your choice?
1
sum: 6
It chose 3.
sum: 9
your choice?
2
sum: 11
It chose 2.
sum: 13
your choice?
3
sum: 16
It chose 1.
sum: 17
your choice?
1
sum: 18
It chose 3.
sum: 21
it  won

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.

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");
                    }
                }
            }
        }
    }

}
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.

JavaScript

The solution as a Javascript script inside a page written in HTML5. It should work with all modern browsers.

<!DOCTYPE html><html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="keywords"    content="Game 21">
    <meta name="description" content="
      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.
    ">
    <!--DCMI metadata (Dublin Core Metadata Initiative)-->
    <meta name="dc.publisher" content="Rosseta Code">
    <meta name="dc.date"      content="2020-07-23">
    <meta name="dc.created"   content="2020-07-23">
    <meta name="dc.modified"  content="2020-07-30">
    <title>
        21 Game
    </title>
    <!-- Remove the line below in the final/production version. -->
    <meta http-equiv="cache-control" content="no-cache">

    <style>
      .ui div   { width: 50%; display: inline-flex; justify-content: flex-end; }
      div.total { margin-bottom: 1ch; }
      label     { padding-right: 1ch; }
      button + button { margin-left: 1em; }
    </style>
</head>

<body>
  <h1>
    21 Game in ECMA Script (Java Script)
  </h1>

  <p>
    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.
  </p>

  <p><span id="first"></span> Use buttons to play.</p>

  <div class="ui">
    <div class="total">
      <label for="human">human last choice:</label>
      <input type="text" id="human" readonly>
    </div>
    <div class="total">
      <label for="AI">AI last choice:</label>
      <input type="text" id="AI" readonly>
    </div>
    <div class="total">
      <label for="runningTotalText">running total:</label>
      <input type="text" id="runningTotalText" readonly>
    </div>
    <div class="buttons">
      <button onclick="choice(1);" id="choice1"> one   </button>
      <button onclick="choice(2);" id="choice2"> two   </button>
      <button onclick="choice(3);" id="choice3"> three </button>
      <button onclick="restart();"> restart </button>
    </div>
  </div>

  <p id="message"></p>

  <noscript>
    No script, no fun. Turn on Javascript on.
  </noscript>

  <script>
    // I really dislike global variables, but in any (?) WWW browser the global
    // variables are in the window (separately for each tab) context space.
    //
    var runningTotal = 0;
    const human = document.getElementById('human');
    const AI = document.getElementById('AI');
    const runningTotalText = document.getElementById('runningTotalText');
    const first = document.getElementById('first')
    const message = document.getElementById('message');
    const choiceButtons = new Array(3);

    // An function to restart game in any time, should be called as a callback
    // from the WWW page, see above for an example.
    //
    function restart()
    {
      runningTotal = 0;
      runningTotalText.value = runningTotal;
      human.value = '';
      AI.value = '';
      for (let i = 1; i <= 3; i++)
      {
        let button = document.getElementById('choice' + i);
        button.disabled = false;
        choiceButtons[i] = button;
      }
      message.innerText = '';
      if (Math.random() > 0.5)
      {
        update(AI, ai());
        first.innerText = 'The first move is AI move.'
      }
      else
        first.innerText = 'The first move is human move.'
    }

    // This function update an (read-only for a user) two text boxes
    // as well as runningTotal. It should be called only once per a move/turn.
    //
    function update(textBox, n)
    {
      textBox.value = n;
      runningTotal = runningTotal + n;
      runningTotalText.value = runningTotal;
      for (let i = 1; i <= 3; i++)
        if (runningTotal + i > 21)
          choiceButtons[i].disabled = true;
    }

    // An callback function called when the human player click the button.
    //
    function choice(n)
    {
      update(human, n);
      if (runningTotal == 21)
        message.innerText = 'The winner is human.';
      else
      {
        update(AI, ai());
        if (runningTotal == 21)
          message.innerText = 'The winner is AI.';
      }
    }

    // A rather simple function to calculate a computer move for the given total.
    //
    function ai()
    {
      for (let i = 1; i <= 3; i++)
        if (runningTotal + i == 21)
          return i;

      for (let i = 1; i <= 3; i++)
        if ((runningTotal + i - 1) % 4 == 0)
          return i;

      return 1;
    }

    // Run the script - actually this part do only some initialization, because
    // the game is interactively driven by events from an UI written in HTML.
    //
    restart();
  </script>

</body>
</html>

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.

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()

Koka

import std/num/random
import std/os/readline

effect exit
  final ctl exit(): ()

type player
  Player
  Computer

fun other(p: player): player
  match p
    Player -> Computer
    Computer -> Player

fun show(p: player): string
  match p
    Player -> "Player"
    Computer -> "Computer"

fun get-selection(max-int: int)
  val i = readline().trim().parse-int()
  match i
    Just(x) | x >= 1 && x <= max-int -> x
    _ ->
      println("Please enter a number between 1 and " ++ max-int.show ++ " or press q to quit")
      get-selection(max-int)

fun play(p: player, total: int)
  println("Total: " ++ total.show)
  match total
    21 ->
      // The player who reaches 21 wins the game, which is the player in the last loop
      println(p.other.show ++ " wins!")
      return ()
    _ -> ()
  val max = if total + 3 > 21 then 21 - total else 3
  match p
    Player ->
      println("Player's turn")
      val selection = get-selection(max)
      play(p.other, total + selection)
    Computer ->
      println("Computer's turn")
      val selection = (random-int() % max) + 1
      println("Computer chooses " ++ selection.show)
      play(p.other, total + selection)

fun main()
  with final ctl exit() ()
  "21 is a two player game. The game is played by choosing a number (1, 2, 3) to".println
  "be added to the running total. The game is won by the player whose chosen number".println
  "causes the running total to reach exactly 21. The running total starts at zero.".println
  "".println
  "You can quit the game at any time by typing 'q'.".println
  val playerGoesFirst = random-bool()
  val p1 = if playerGoesFirst then Player else Computer
  play(p1, 0)

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

Mathematica / Wolfram Language

SeedRandom[1234];
ClearAll[ComputerChoose, HumanChoose]
ComputerChoose[n_] := If[n < 18, RandomChoice[{1, 2, 3}], 21 - n]
HumanChoose[] := ChoiceDialog["How many?", {1 -> 1, 2 -> 2, 3 -> 3, "Quit" -> -1}]
runningtotal = 0;
whofirst = ChoiceDialog["Who goes first?", {"You" -> 1, "Computer" -> 2}];
While[runningtotal < 21,
 If[whofirst == 1,
   choice = HumanChoose[];
   If[choice == -1, Break[]];
   Print["You choose = ", choice];
   runningtotal += choice;
   Print["Running total = ", runningtotal];
   If[runningtotal == 21, Print["You won!"]; Break[]];
   choice = ComputerChoose[runningtotal];
   Print["Computer choose = ", choice];
   runningtotal += choice;
   Print["Running total = ", runningtotal];
   If[runningtotal == 21, Print["Computer won!"]; Break[]];
   ,
   choice = ComputerChoose[runningtotal];
   Print["Computer choose = ", choice];
   runningtotal += choice;
   Print["Running total = ", runningtotal];
   If[runningtotal == 21, Print["Computer won!"]; Break[]];
   choice = HumanChoose[];
   If[choice == -1, Break[]];
   Print["You choose = ", choice];
   runningtotal += choice;
   Print["Running total = ", runningtotal];
   If[runningtotal == 21, Print["You won!"]; Break[]];
   ];
 ]
Output:
You choose = 2
Running total = 2
Computer choose = 3
Running total = 5
You choose = 3
Running total = 8
Computer choose = 1
Running total = 9
You choose = 3
Running total = 12
Computer choose = 1
Running total = 13
You choose = 1
Running total = 14
Computer choose = 2
Running total = 16
You choose = 1
Running total = 17
Computer choose = 3
Running total = 20
You choose = 1
Running total = 21
You won!


Nim

There is a simple strategy which allows the first player to win in any case. It consists to choose a value which gives a running total equal to 1, 5, 9, 13 or 17. So never let the computer starts as the program uses this strategy.

# 21 game.

import random
import strformat
import strutils

const
  Target = 21
  PossibleChoices: array[18..20, seq[string]] = [@["1", "2", "3"], @["1", "2"], @["1"]]
  Targets = [1, 5, 9, 13, 17, 21]     # Totals that a player must obtain to win.

#---------------------------------------------------------------------------------------------------

proc printTotal(total: int) =
  ## Print the running total.
  echo fmt"Running total is now {total}."

#---------------------------------------------------------------------------------------------------

proc computerPlays(total: var int) =
  ## Make the computer play.
  var choice: int
  if total in Targets:
    # No winning choice. Choose a random value.
    choice = rand(1..3)
  else:
    # Find the running total to get.
    for val in Targets:
      if val > total:
        choice = val - total
        break
  inc total, choice
  echo fmt"I choose {choice}."
  printTotal(total)

#---------------------------------------------------------------------------------------------------

proc prompt(message: string; answers: openArray[string]): int =
  ## Prompt a message and get an answer checking its validity against possible answers.

  while true:
    stdout.write(message & ' ')
    try:
      result = answers.find(stdin.readLine())
      if result >= 0:
        break
      echo fmt"Please answer one of: {answers.join("", "")}."
    except EOFError:
      echo ""
      return  # Quit.

#---------------------------------------------------------------------------------------------------

randomize()

echo "21 is a two player game. The game is played by choosing a number (1, 2, 3) to\n" &
     "be added to the running total. The game is won by the player whose chosen number\n" &
     "causes the running total to reach exactly 21. The running total starts at zero.\n"
echo "You can quit the game at any time by typing 'q'."

block mainLoop:

  while true:
    var total = 0

    # Choose the player who will play first.
    var answer = prompt("Who will play first ('you', 'me')?", ["q", "you", "me"])
    if answer == 0:
      echo "Quitting game."
      break
    elif answer == 1:
      computerPlays(total)

    # Internal game loop.
    while true:

      # Ask player its choice.
      let choices = if total > 18: PossibleChoices[total] else: PossibleChoices[18]
      let choice = prompt(fmt"Your choice ({choices.join("", "")})?", "q" & choices)
      if choice == 0:
        echo "Quitting game."
        break mainLoop

      # Update running total and check if player win.
      inc total, choice
      printTotal(total)
      if total == Target:
        echo "Congratulations, you win."
        break

      # Make computer play.
      computerPlays(total)
      if total == Target:
        echo "Sorry, I win."
        break

    # Ask player for another game.
    answer = prompt("Do you want to play another game (y, n)", ["q", "y", "n"])
    if answer != 1:
      echo "Quitting game."
      break
Output:
21 is a two player game. The game is played by choosing a number (1, 2, 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.

You can quit the game at any time by typing 'q'.
Who will play first (you, me)? x
Please, answer one of: q, you, me.
Who will play first ('you', 'me')? you
I choose 1.
Running total is now 1.
Your choice (1, 2, 3)? 4
Please answer one of: q, 1, 2, 3.
Your choice (1, 2, 3)? 3
Running total is now 4.
I choose 1.
Running total is now 5.
Your choice (1, 2, 3)? 3
Running total is now 8.
I choose 1.
Running total is now 9.
Your choice (1, 2, 3)? 3
Running total is now 12.
I choose 1.
Running total is now 13.
Your choice (1, 2, 3)? 3
Running total is now 16.
I choose 1.
Running total is now 17.
Your choice (1, 2, 3)? 3
Running total is now 20.
I choose 1.
Running total is now 21.
Sorry, I win.
Do you want to play another game (y, n) y
Who will play first (you, me)? me
Your choice (1, 2, 3)? 1
Running total is now 1.
I choose 2.
Running total is now 3.
Your choice (1, 2, 3)? 2
Running total is now 5.
I choose 2.
Running total is now 7.
Your choice (1, 2, 3)? 2
Running total is now 9.
I choose 1.
Running total is now 10.
Your choice (1, 2, 3)? 3
Running total is now 13.
I choose 3.
Running total is now 16.
Your choice (1, 2, 3)? 1
Running total is now 17.
I choose 2.
Running total is now 19.
Your choice (1, 2)? 2
Running total is now 21.
Congratulations, you win.
Do you want to play another game (y, n) n
Quitting game.

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;
  }
}

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.

Perl

Translation of: Raku
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"
}
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

Library: Phix/pGUI
Library: Phix/online

If the computer goes first you cannot win, it will say "(you've already lost)" as soon as you have.
You can run this online here. (As noted below, a few improvements are in order.)

--
-- demo\rosetta\21_Game.exw
-- ========================
--
with javascript_semantics -- DEV NORMALIZESIZE, CANFOCUS, "You" not checked, VALUE_HANDLE.
                          -- The radio_texts simply don't do anything at all in p2js.
constant title = "21 Game",
         help_text = """
Play by choosing 1, 2, or 3 to add to the running total (initially 0).

The first player to reach 21 wins.

If the computer goes first you cannot win.

If you leave your opponent on 18, 19, or 20, they will play {3,2,1} and win.
If you leave your opponent on 17, simply match their play {1,2,3} with {3,2,1} and win.
If you leave your opponent on 14, 15, or 16, they'll leave you on 17 and win.
So the aim is 21 (doh), and before that 17, 13, 9, 5, and 1. Anything else loses.
""",
         radio_texts = {"You","Computer","Random"},
         button_text = {"one","two","three","concede","new game","quit"}
integer total = 0

include pGUI.e
Ihandle dlg, vbox, frame, radios, playstate
sequence radioset, buttons

function show_help()
    IupMessage(title,help_text)
    return IUP_IGNORE -- (don't open browser help!)
end function

function play(integer n)
    if n!=0 then
        if n=6 then return IUP_CLOSE end if
        string title
        if n>3 then
            -- concede or new_game
            total = 0
            title = iff(n=4?"(conceded) ":"")
            title &= "Total is 0"
            Ihandle r = IupGetAttributePtr(radios,"VALUE_HANDLE")
            n = find(r,radioset)
            if n=2 or (n=3 and rand(2)=1) then
                title &= "," -- trigger a computer move
            end if
            IupSetInt(buttons[1..3],"ACTIVE",true)
        else
            -- n = 1..3
            title = sprintf("Total is %d",total)
            if total=21      -- (from key_cb)
            or total+n>21 then -- (invalid)
                return IUP_IGNORE
            end if
            total += n
            title &= sprintf(", you play %d (-> %d),",{n,total})
            if total=21 then title &= " you win" end if
        end if
        if find(',',title) and total!=21 then
            -- computer move
            sequence moves = {1,rand(3),3,2}
            n = moves[mod(total,4)+1]
            total += n
            title &= sprintf(" computer plays %d (-> %d)",{n,total})
            if total=21 then
                title &= ", computer wins"
            elsif mod(total,4)=1 then
                title &= ", (you've already lost)"
            end if
        end if      
        if total=21 then
            title &= " GAME OVER"
            IupSetInt(buttons[1..4],"ACTIVE",false)
        else
            if total>18 then
                IupSetInt(buttons[22-total..3],"ACTIVE",false)
            end if
            IupSetInt(buttons[4],"ACTIVE",total!=0)
        end if      
        IupSetFocus(dlg) -- (stops inactive button beeping)
        IupSetStrAttribute(playstate,"TITLE",title)
    end if
    return IUP_IGNORE
end function

function button_cb(Ihandle ih)
    string title = IupGetAttribute(ih,"TITLE")
    return play(find(title,button_text))
end function
constant cb_button = Icallback("button_cb")

function key_cb(Ihandle /*dlg*/, atom c)
    if c=K_ESC then return IUP_CLOSE end if -- (standard practice for me)
    if c=K_F5 then return IUP_DEFAULT end if -- (let browser reload work)
    if c=K_F1 then return show_help() end if
    return play(find(upper(c),"123CNQ"))
end function

IupOpen()
playstate = IupLabel("","EXPAND=HORIZONTAL, PADDING=10x10")
radioset = apply(true,IupToggle,{radio_texts,{"RIGHTBUTTON=YES, CANFOCUS=NO"}})
buttons = apply(true,IupButton,{button_text,cb_button,{"PADDING=5x5"}})
radios = IupRadio(IupHbox(radioset,"GAP=45"))
frame = IupHbox({IupLabel(`First Player:`),radios},"NORMALIZESIZE=VERTICAL")
vbox = IupVbox({frame,playstate,IupHbox(buttons,"GAP=10")},"MARGIN=20x10")
dlg = IupDialog(vbox,`TITLE="%s", MINSIZE=540x200`,{title})
IupShow(dlg)
IupSetCallback({dlg,buttons},"KEY_CB",Icallback("key_cb"))
IupSetAttributeHandle(NULL,"PARENTDIALOG",dlg)
{} = play(find("new game",button_text))
if platform()!=JS then
    IupMainLoop()
    IupClose()
end if

PHP

The solution as a server-side PHP7 script and HTML5.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="keywords"    content="Game 21">
    <meta name="description" content="
      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.
    ">
    <!--DCMI metadata (Dublin Core Metadata Initiative)-->
    <meta name="dc.publisher" content="Rosseta Code">
    <meta name="dc.date"      content="2020-07-31">
    <meta name="dc.created"   content="2020-07-31">
    <meta name="dc.modified"  content="2020-08-01">
    <title>
        21 Game
    </title>
    <!-- Remove the line below in the final/production version. -->
    <meta http-equiv="cache-control" content="no-cache">

    <style>
      .ui div   { width: 50%; display: inline-flex; justify-content: flex-end; }
      div.total { margin-bottom: 1ch; }
      label     { padding-right: 1ch; }
      button + button { margin-left: 1em; }
    </style>
</head>

<body>

  <h1>
    21 Game in PHP 7
  </h1>

  <p>
    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.
  </p>


  <?php

    const GOAL = 21;
    const PLAYERS = array('AI', 'human');

    function best_move($n)
    {
      for ($i = 1; $i <= 3; $i++)
        if ($n + $i == GOAL)
          return $i;
      for ($i = 1; $i <= 3; $i++)
        if (($n + $i - 1) % 4 == 0)
          return $i;
      return 1;
    }

    if (isset($_GET['reset']) || !isset($_GET['total']))
    {
      $first = PLAYERS[rand(0, 1)];
      $total = 0;
      $human = 0;
      $ai = 0;
      $message = '';
      if ($first == 'AI')
      {
        $move = best_move($total);
        $ai = $move;
        $total = $total + $move;
      }
    }
    else
    {
      $first   = $_GET['first'];
      $total   = $_GET['total'];
      $human   = $_GET['human'];
      $ai      = $_GET['ai'];
      $message = $_GET['message'];
    }

    if (isset($_GET['move']))
    {
      $move = (int)$_GET['move'];
      $human = $move;
      $total = $total + $move;
      if ($total == GOAL)
        $message = 'The winner is human.';
      else
      {
        $move = best_move($total);
        $ai = $move;
        $total = $total + $move;
        if ($total == GOAL)
          $message = 'The winner is AI.';
      }
    }

    $state = array();
    for ($i = 1; $i <= 3; $i++)
      $state[$i] = $total + $i > GOAL ? 'disabled' : '';

    echo <<< END
      <p>
        The first player is $first.
        Use buttons to play.
      </p>
      <form class="ui">
        <div>
          <input type='hidden' id='first' name='first' value='$first'>
          <input type='hidden' name='message' value='$message'>
        </div>
        <div class='total'>
          <label for='human'>human last choice:</label>
          <input type='text' name='human' readonly value='$human'>
        </div>
        <div class='total'>
          <label for='AI'>AI last choice:</label>
          <input type='text' name='ai' readonly value='$ai'>
        </div>
        <div class='total'>
          <label for='runningTotalText'>running total:</label>
          <input type='text' name='total' readonly value='$total'>
        </div>
        <div class='buttons'>
          <button type='submit' name='move' value='1' {$state[1]}> one   </button>
          <button type='submit' name='move' value='2' {$state[2]}> two   </button>
          <button type='submit' name='move' value='3' {$state[3]}> three </button>
          <button type='submit' name='reset' value='reset'> reset </button>
        </div>
      </form>
      <p>
        $message
      </p>
    END
  ?>

</body>

Picat

Translation of: Julia
import util.

main =>
    N = 0,
    Level = prompt("Level of play (1=dumb, 3=smart)"),
    Algo = choosewisely,
    if Level == "1" then
        Algo := choosefoolishly
    elseif Level == "2" then
        Algo := choosesemiwisely
    elseif Level != "3" then
        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' then
        N := apply(Algo, N)
    end,
    while (N < 21)
        N := playermove(N),
        if N == 21 then
            println("Player wins! Game over, gg!"),
            halt
        end,
        N := apply(Algo,N)
    end.
 
trytowin(N) =>
    if 21 - N < 4 then 
        printf("Computer chooses %w and wins. GG!\n", 21 - N),
        halt
    end.

choosewisely(N) = NextN =>
    trytowin(N),
    Targets = [1, 5, 9, 13, 17, 21],
    once ((member(Target, Targets), Target > N)),
    Bestmove = Target - N,
    if Bestmove > 3 then
        printf("Looks like I could lose. Choosing a 1, total now %w.\n", N + 1),
        NextN = N+1
    else 
        printf("On a roll, choosing a %w, total now %w.\n", Bestmove, N + Bestmove),
        NextN = N + Bestmove
    end.
 
choosefoolishly(N) = NextN =>
    trytowin(N),
    Move = random() mod 3 + 1,
    printf("Here goes, choosing %w, total now %w.", Move, N+Move),
    NextN = N+Move.
 
choosesemiwisely(N) = NextN =>
    trytowin(N),
    if frand() > 0.75 then
        NextN = choosefoolishly(N)
    else
        NextN = choosewisely(N)
    end.
 
prompt(S) = Input =>
    printf(S ++ ": => "),
    Input = strip(read_line()).
 
playermove(N) = NextN =>
    Rang = cond(N > 19, "1 is all", cond(N > 18, "1 or 2", "1, 2 or 3")),
    Prompt = to_fstring("Your choice (%s), 0 to exit", Rang),
    Nstr = prompt(Prompt),
    if Nstr == "0" then
        halt
    elseif Nstr == "1" then
        NextN = N+1
    elseif Nstr == "2" && N < 20 then
        NextN = N + 2
    elseif Nstr == "3" && N < 19 then
        NextN = N + 3
    else
        NextN = playermove(N)
    end.

Python

Python: Original, with output

Works with: Python 2.X and 3.X
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")
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

Python: using tkinter

''' Python 3.6.5 code using Tkinter graphical user interface.
    Starting player chosen randomly. ''' 
from tkinter import *
from tkinter import messagebox
import random

# ************************************************

class Game:
    def __init__(self, gw):
        self.window = gw
        self.won = 0
        self.lost = 0
        self.score = 0
        self.puter_turn = None
        self.var123 = IntVar()
        self.varsub = IntVar()

        # top frame:
        self.top_fr = Frame(gw,
                            width=600,
                            height=100,
                            bg='dodger blue')
        self.top_fr.pack(fill=X)

        self.hdg = Label(self.top_fr,
                         text='  21 Game  ',
                         font='arial 22 bold',
                         fg='navy',
                         bg='lemon chiffon')
        self.hdg.place(relx=0.5, rely=0.5,
                       anchor=CENTER)

        self.play_btn = Button(self.top_fr,
                               text='Play\nGame',
                               bd=5,
                               bg='navy',
                               fg='lemon chiffon',
                               font='arial 12 bold',
                               command=self.play_game)
        self.play_btn.place(relx=0.92, rely=0.5,
                            anchor=E)

        self.quit_btn = Button(self.top_fr,
                               text='Quit\nGame',
                               bd=5,
                               bg='navy',
                               fg='lemon chiffon',
                               font='arial 12 bold',
                               command=self.quit_game)
        self.quit_btn.place(relx=0.07, rely=0.5,
                            anchor=W)

        # bottom frame:
        self.btm_fr = Frame(gw,
                            width=600,
                            height=500,
                            bg='lemon chiffon')
        self.btm_fr.pack(fill=X)

        self.msg = Label(self.btm_fr,
                         text="(Click 'Play' or 'Quit')",
                         font='arial 16 bold',
                         fg='navy',
                         bg='lemon chiffon')
        self.msg.place(relx=0.5, rely=0.1,
                       anchor=CENTER)

        self.hdg = Label(self.btm_fr,
                         text="Scoreboard",
                         font='arial 16 bold',
                         fg='navy',
                         bg='lemon chiffon')
        self.hdg.place(relx=0.5, rely=0.2,
                       anchor=CENTER)

        self.score_msg = Label(self.btm_fr,
                               text="0",
                               font='arial 16 bold',
                               fg='navy',
                               bg='dodger blue',
                               width=8)
        
        self.score_msg.place(relx=0.5, rely=0.27,
                             anchor=CENTER)

        self.ch_fr = LabelFrame(self.btm_fr,
                                text='Choose a number',
                                bg='dodger blue',
                                fg='navy',
                                bd=8,
                                relief=RIDGE,
                                font='arial 16 bold')
        self.ch_fr.place(relx=0.5, rely=0.5,
                         anchor=CENTER)

        self.radio1 = Radiobutton(self.ch_fr,
                                  text='1',
                                  state='disabled',
                                  font='arial 16 bold',
                                  fg='navy',
                                  bg='dodger blue',
                                  variable=self.var123,
                                  value=1)
        self.radio1.pack()

        self.radio2 = Radiobutton(self.ch_fr,
                                  text='2',
                                  state='disabled',
                                  font='arial 16 bold',
                                  fg='navy', 
                                  bg='dodger blue',
                                  variable=self.var123,
                                  value=2)
        self.radio2.pack()

        self.radio3 = Radiobutton(self.ch_fr,
                                  text='3',
                                  state='disabled',
                                  font='arial 16 bold ',
                                  fg='navy', 
                                  bg='dodger blue',
                                  variable=self.var123,
                                  value=3)
        self.radio3.pack()

        self.submit_btn = Button(self.btm_fr,
                                 text='SUBMIT',
                                 state='disabled',
                                 bd=5,
                                 bg='navy',
                                 fg='lemon chiffon',
                                 font='arial 12 bold',
                                 command=self.submit)
        self.submit_btn.place(relx=0.5, rely=0.75,
                              anchor=CENTER)

        self.won_lbl = Label(self.btm_fr,
                             text="Won: 0",
                             font='arial 16 bold',
                             fg='navy',
                             bg='lemon chiffon')
        self.won_lbl.place(relx=0.85, rely=0.88,
                           anchor=W)

        self.lost_lbl = Label(self.btm_fr,
                              text="Lost: 0",
                              font='arial 16 bold',
                              fg='navy',
                              bg='lemon chiffon')
        self.lost_lbl.place(relx=0.85, rely=0.93,
                            anchor=W)

    # play one game:    
    def play_game(self):
        self.play_btn.config(state='disabled')
        # pick who goes first randomly:
        self.puter_turn = random.choice([True, False])
        self.score = 0
        self.score_msg.config(text=self.score)
        if not self.puter_turn:
            m = 'your turn'
            self.msg.config(text=m)
        # alternate turns until 21 is reached:
        while self.score != 21:
            if self.puter_turn:
                self.puter_plays()
            else:
                self.user_plays()
            self.puter_turn = not self.puter_turn
        self.play_btn.config(state='normal')
        return

    # computer picks a number:
    def puter_plays(self):
        if self.score == 20:
            x = 1
        elif self.score == 19:
                x = random.choice([1, 2])
        else:
            x = random.choice([1, 2, 3])
        self.score += x
        self.score_msg.config(text=self.score)
        if self.score == 21:
            m = 'Computer won!'
            self.lost += 1
            self.lost_lbl.config(text='Lost: ' + str(self.lost))
        else:
            m = 'Computer chose ' + str(x) + ', your turn'
        self.msg.config(text=m)
        return

    # user picks a number:
    def user_plays(self):
        self.set_user_state('normal')
        while True:
            # wait for submit button to be pressed:
            self.submit_btn.wait_variable(self.varsub)
            x = self.var123.get()
            if x + self.score > 21:
                m = 'Score cannot exceed 21, try again'
                messagebox.showerror('Error', m)
            elif x not in (1,2,3):
                m = 'No selection made'
                messagebox.showerror('Error', m)
            else:
                break
        self.score += x
        if self.score == 21:
            m = 'You won!'
            self.msg.config(text=m)
            self.score_msg.config(text=self.score)
            self.won += 1
            self.won_lbl.config(text='Won: ' + str(self.won))
        # reset and disable radio buttons:   
        self.var123.set(0)  
        self.set_user_state('disabled')                     
        return

    # set radio buttons to 'disabled' or 'normal':
    def set_user_state(self, state):
        self.radio1.config(state=state)
        self.radio2.config(state=state)
        self.radio3.config(state=state)
        self.submit_btn.config(state=state)
        return
        
                            
    def quit_game(self):
        self.window.destroy()

    # indicate that submit button was pressed:
    def submit(self):
        self.varsub.set(0)

# ************************************************

root = Tk()
root.title('21 Game')
root.geometry('600x600+100+50')
root.resizable(False, False)
g = Game(root)
root.mainloop()

Quackery

  [ say
    "Who goes first: Computer, Player"
    say " or Random?"  cr
    [ $ "Enter C, P or R: " input
      dup size 1 != iff drop again
      0 peek
      dup char C = iff [ drop 0 ] done
      dup char P = iff [ drop 1 ] done
      char R = iff [ 2 random ] done
       again ]
    cr
    dup iff [ say "You go first." ]
    else [ say "I will go first." ]
    cr ]                                  is chooseplayer (   --> n   )

                                  forward is player       ( n --> n x )

  [ [ dup 17 > iff 1 done
      4 over 4 mod
      dup 0 = if [ drop 3 ]
      - ]
    dup say "Computer chooses " echo
    say "." cr
    + ' player ]                          is computer     ( n --> n x )

  [ say "Choose 1 2 or 3 (running "
    $ "total must not exceed 21, Q to quit): " input
    dup $ "Q" = iff [ drop 21 999 ] done
    trim reverse trim reverse
    $->n not iff drop again
    dup 1 4 within not iff drop again
    2dup + 21 > iff drop again
    + ' computer ]                  resolves player       ( n --> n x )

  [ say "The player who makes 21 loses." cr
    0 chooseplayer
    iff [ ' player ] else [ ' computer ]
    [ say "Running total is "
      over echo say "." cr cr
      do
      over 21 = until ]
    cr
    dup 999 = iff
      [ drop 2drop say "Quitter!" ] done
    ' computer = iff
        [ say "The computer won!" ]
    else [ say "You won! Well done!" ]
    drop ]                                 is play        (   -->     )
Output:

As a dialogue in the Quackery shell.

/O> play
... 
The player who makes 21 loses.
Who goes first: Computer, Player or Random?
Enter C, P or R: C

I will go first.
Running total is 0.

Computer chooses 1.
Running total is 1.

Choose 1 2 or 3 (running total must not exceed 21, Q to quit): Q

Quitter!
Stack empty.

/O> play
... 
The player who makes 21 loses.
Who goes first: Computer, Player or Random?
Enter C, P or R: R

You go first.
Running total is 0.

Choose 1 2 or 3 (running total must not exceed 21, Q to quit): 2
Running total is 2.

Computer chooses 2.
Running total is 4.

Choose 1 2 or 3 (running total must not exceed 21, Q to quit): 3
Running total is 7.

Computer chooses 1.
Running total is 8.

Choose 1 2 or 3 (running total must not exceed 21, Q to quit): 1
Running total is 9.

Computer chooses 3.
Running total is 12.

Choose 1 2 or 3 (running total must not exceed 21, Q to quit): 4
Choose 1 2 or 3 (running total must not exceed 21, Q to quit): 1
Running total is 13.

Computer chooses 3.
Running total is 16.

Choose 1 2 or 3 (running total must not exceed 21, Q to quit): 3
Running total is 19.

Computer chooses 1.
Running total is 20.

Choose 1 2 or 3 (running total must not exceed 21, Q to quit): 2
Choose 1 2 or 3 (running total must not exceed 21, Q to quit): 1

The computer won!
Stack empty.

/O> 

R

game21<-function(first = c("player","ai","random"),sleep=.5){
  state = 0
  finished = F
  turn = 1
  if(length(first)==1 && startsWith(tolower(first),"r")){
    first = rbinom(1,1,.5)
  }else{
    first = (length(first)>1 || startsWith(tolower(first),"p"))
  }
  while(!finished){
    if(turn>1 || first){
      cat("The total is now",state,"\n");Sys.sleep(sleep)
      while(T){
        player.move = readline(prompt = "Enter move: ")
        if((player.move=="1"||player.move=="2"||player.move=="3") && state+as.numeric(player.move)<=21){
          player.move = as.numeric(player.move)
          state = state + player.move
          break
        }else if(tolower(player.move)=="exit"|tolower(player.move)=="quit"|tolower(player.move)=="end"){
          cat("Goodbye.\n")
          finished = T
          break
        }else{
          cat("Error: invaid entry.\n")
        }
      }
    }
    if(state == 21){
      cat("You win!\n")
      finished = T
    }
    if(!finished){
      cat("The total is now",state,"\n");Sys.sleep(sleep)
      while(T){
        ai.move = sample(1:3,1)
        if(state+ai.move<=21){
          break
        }
      }
      state = state + ai.move
      cat("The AI chooses",ai.move,"\n");Sys.sleep(sleep)
      if(state == 21){
        cat("The AI wins!\n")
        finished = T
      }
    }
    turn = turn + 1
  }
}
game21()
Output:
The total is now 0 
Enter move: 3
The total is now 3 
The AI chooses 2 
The total is now 5 
Enter move: 3
The total is now 8 
The AI chooses 3 
The total is now 11 
Enter move: 3
The total is now 14 
The AI chooses 2 
The total is now 16 
Enter move: 1
The total is now 17 
The AI chooses 3 
The total is now 20 
Enter move: 1
The total is now 21 
You win!
game21(first = "ai")
Output:
The total is now 0 
The AI chooses 2 
The total is now 2
Enter move: quit
Goodbye.

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?))]))
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.

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..."
}
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).

This REXX version allows the user to choose if the computer should go first.

/*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*/
$=0;                       goal= 21              /*the sum [or running total]  (so far).*/
    do j=1  while $<goal;  call g                /*obtain the user's number via a prompt*/
    if x\==0    then call tot x, 1               /*Not 0?   The user wants to go first. */
    if $==goal  then leave                       /*the user won the game with the last #*/
    call ?;     if y==.  then y= random(1, 3)    /*get computer's choice  or  a random #*/
    say sep 'The computer chooses '     y     " as its 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. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
?:   y=.;   do c=1  for 3  until y\==.;  if (c+$) // 4 == 1  then y= c;    end;     return
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; /*add; show $*/ return
/*──────────────────────────────────────────────────────────────────────────────────────*/
g: low = (j \== 1)                               /*allow user to have computer go first.*/
     do until \bad;   bad= 0;     say            /*prompt 'til user gives a good number.*/
     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.]'
     parse pull x _ . 1 ox;   upper x            /*obtain user's lowercase response(s). */
     if   x=''             then call ser "Nothing entered."
     if _\==''             then call ser "Too many arguments entered: "       ox
     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: "           ox
     if \datatype(x, 'W')  then call ser "Number isn't an integer: "          ox
     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>3                then call ser "Number is too large  (>3): "         x
     if bad                then iterate          /*Had an error? Then get another number*/
     x= x/1; if $+x>goal   then call ser "Number will cause the sum to exceed " goal': ' x
     end   /*until*/;           return
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  2  as its choice.
──────── The game total is now  ════════  5  ════════

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

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

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

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

──────── The computer has won the  21  game.

Ring

# 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()
	       }
        }

Output:
21 Game

Ruby

# 21 Game - an example in Ruby for Rosetta Code.

GOAL = 21
MIN_MOVE = 1
MAX_MOVE = 3

DESCRIPTION = "
*** Welcome to the 21 Game! ***
21 is a two player 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.
The running total starts at zero.

The players start the game in turn.
Enter q to quit at any time.
"

#
# Returns the best move to play.
#
def best_move(total)
  move = rand(1..3)
  MIN_MOVE.upto(MAX_MOVE) do |i|
    move = i if (total + i - 1) % (MAX_MOVE + 1) == 0
  end
  MIN_MOVE.upto(MAX_MOVE) do |i|
    move = i if total + i == GOAL
  end
  move
end

#
# Gets the move of the player.
#
def get_move
  print "Your choice between #{MIN_MOVE} and #{MAX_MOVE}: "
  answer = gets
  move = answer.to_i
  until move.between?(MIN_MOVE, MAX_MOVE)
    exit if answer.chomp == 'q'
    print 'Invalid choice. Try again: '
    answer = gets
    move = answer.to_i
  end
  move
end

#
# Asks the player to restart a game and returns the answer.
#
def restart?
  print 'Do you want to restart (y/n)? '
  restart = gets.chomp
  until ['y', 'n'].include?(restart)
    print 'Your answer is not a valid choice. Try again: '
    restart = gets.chomp
  end
  restart == 'y'
end

#
# Run a game. The +player+ argument is the player that starts:
# * 1 for human
# * 0 for computer
#
def game(player)
  total = round = 0
  while total < GOAL
    round += 1
    puts "--- ROUND #{round} ---\n\n"
    player = (player + 1) % 2
    if player == 0
      move = best_move(total)
      puts "The computer chooses #{move}."
    else
      move = get_move
    end
    total += move
    puts "Running total is now #{total}.\n\n"
  end
  if player == 0
    puts 'Sorry, the computer has won!'
    return false
  end
  puts 'Well done, you have won!'
  true
end

# MAIN
puts DESCRIPTION
run = true
computer_wins = human_wins = 0
games_counter = player = 1
while run
  puts "\n=== START GAME #{games_counter} ==="
  player = (player + 1) % 2
  if game(player)
    human_wins += 1
  else
    computer_wins += 1
  end
  puts "\nComputer wins #{computer_wins} games, you wins #{human_wins} game."
  games_counter += 1
  run = restart?
end
puts 'Good bye!'

Rust

use rand::Rng;
use std::io;

#[derive(Clone)]
enum PlayerType {
    Human,
    Computer,
}

#[derive(Clone)]
struct Player {
    name: String,
    wins: u32,  // holds wins
    level: u32, // difficulty level of Computer
    player_type: PlayerType, 
}

trait Choose {
    fn choose(&self, game: &Game) -> u8;
}

impl Player {
    fn new(name: &str, player_type: PlayerType, level: u32) -> Player {
        Player {
            name: String::from(name),
            wins: 0,
            level,
            player_type,
        }
    }
    fn get_name(&self) -> &str {
        &self.name[..]
    }
    fn get_level(&self) -> u32 {
        self.level
    }
    fn add_win(&mut self) {
        self.wins += 1
    }
    fn level_up(&mut self) {
        self.level += 1
    }
}

impl Choose for Player {
    fn choose(&self, game: &Game) -> u8 {
        match self.player_type {
            PlayerType::Human => loop {
                let max_choice = game.max_choice();
                match max_choice {
                    1 => println!("Enter a number 1 to win (or quit):"),
                    _ => println!("Enter a number between 1 and {} (or quit):", max_choice)
                }
                let mut guess = String::new();
                io::stdin()
                    .read_line(&mut guess)
                    .expect("Failed to read line");
                if guess.trim() == "quit" {
                    return 0
                }
                let guess: u8 = match guess.trim().parse() {
                    Ok(num) if num >= 1 && num <= max_choice => num,
                    Ok(_) => continue,
                    Err(_) => continue,
                };
                return guess;
            },
            PlayerType::Computer => match self.level {
                5 => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    total if total == 18 => 3,
                    _ => 1,
                },
                4 => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    total if total == 18 => 3,
                    _ => rand::thread_rng().gen_range(1, 3),
                },
                3 => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    total if total == 18 => 3,
                    _ => rand::thread_rng().gen_range(1, 4),
                },
                2 => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    _ => rand::thread_rng().gen_range(1, 3),
                },
                1 => 1,
                _ => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    total if total == 18 => 3,
                    _ => match game.get_remaining() % 4 {
                        0 => rand::thread_rng().gen_range(1, 4),
                        _ => game.get_remaining() % 4,
                    },
                },
            },
        }
    }
}

struct Game {
    players: Vec<Player>,
    turn: u8,  
    total: u8, 
    start: u8, // determines which player goes first
}

impl Game {
    fn init(players: &Vec<Player>) -> Game {
        Game {
            players: players.to_vec(),
            turn: 1,
            total: 0,
            start: rand::thread_rng().gen_range(0, 2),
        }
    }
    fn play(&mut self) -> &Player {
        loop {
            println!(
                "Total now {} (remaining: {})",
                self.get_total(),
                self.get_remaining()
            );
            {
                let player = self.whose_turn();
                println!("Turn: {} ({} turn)", self.get_turn(), player.get_name());
                let choice = player.choose(&self);
                if choice == 0 {
                    self.next_turn();
                    break; // quit
                }
                println!("{} choose {}", player.get_name(), choice);
                self.add_total(choice)
            }
            if self.get_total() >= 21 {
                break;
            }
            println!("");
            self.next_turn();
        }
        self.whose_turn()
    }
    fn add_total(&mut self, choice: u8) {
        self.total += choice;
    }
    fn next_turn(&mut self) {
        self.turn += 1;
    }
    fn whose_turn(&self) -> &Player {
        let index: usize = ((self.turn + self.start) % 2).into();
        &self.players[index]
    }
    fn get_total(&self) -> u8 {
        self.total
    }
    fn get_remaining(&self) -> u8 {
        21 - self.total
    }
    fn max_choice(&self) -> u8 {
        match self.get_remaining() {
            1 => 1,
            2 => 2,
            _ => 3
        }
    }
    fn get_turn(&self) -> u8 {
        self.turn
    }
}

fn main() {
    let mut game_count = 0;
    let mut players = vec![
        Player::new("human", PlayerType::Human, 0),
        Player::new("computer", PlayerType::Computer, 1),
    ];
    println!("21 Game");
    println!("Press enter key to start");
    {
        let _ = io::stdin().read_line(&mut String::new());
    }
    loop {
        game_count += 1;
        let mut game = Game::init(&players);
        let winner = game.play();
        {
            let mut index = 0;
            while index < players.len() {
                if players[index].get_name() == winner.get_name() {
                    players[index].add_win();
                }
                index += 1
            }
        }
        println!("\n{} won game {}\n", winner.get_name(), game_count);
        // limit game count
        if game_count >= 10000 {
            break;
        }
        // ask player if they want to keep on playing
        println!("Press enter key to play again (or quit):");
        let mut reply = String::new();
        io::stdin()
            .read_line(&mut reply)
            .expect("Failed to read line");
        if reply.trim() == "quit" {
            break;
        }
        // level up computer
        if winner.get_name() != "computer" {
            println!("Computer leveling up ...");
            players[1].level_up();
            println!("Computer now level {}!", players[1].get_level());
            println!("Beware!\n");
        }
    }
    println!("player: {} win: {}", players[0].get_name(), players[0].wins);
    println!("player: {} win: {}", players[1].get_name(), players[1].wins);
}

Scala

object Game21 {

  import scala.collection.mutable.ListBuffer
  import scala.util.Random

  val N = 21 // the same game would also work for N other than 21...

  val RND = new Random() // singular random number generator; add a seed number, if you want reproducibility

  /** tuple: name and a play function: (rest: remaining number, last: value of opponent's last move) => new move
    */
  type Player = (String, (Int,Int) => Int)

  // indeed, the following could also be written using a class and instances, I've choosen a
  // more functional and math way (using tuples)...
  val playerRandom:Player = ("RandomRobot", { (rest, last) =>
    if (rest <= 3) rest
    else 1 + RND.nextInt(3)
  })
  val playerBest:Player = ("BestRobot", { (rest, last) =>
    val i = rest % 4
    if (i > 0) i else 1 + RND.nextInt(3)
  })
  val playerHuman:Player = ("YOU", { (rest, last) =>
    println("Rest: "+rest)
    println("Last: "+last)
    var in = ""
    while (in!="1" && in!="2" && in!="3") {
      in = scala.io.StdIn.readLine("Your move (1,2,3,q)> ").trim
      if ("q" == in)
        throw new Exception("q => quit")
    }
    in.toInt
  })

  /** Execute a whole game. NOTE that we're counting DOWN from N to 0!
    * @param players
    * @return list of all moves
    */
  def play(players:Seq[Player]):Seq[Int] = {
    require(players.size == 2)
    var last = -1
    var rest = N
    var p = 0 // player 0 always starts
    val l = ListBuffer[Int]() // list of all moves
    while (rest > 0) {
      last = players(p)._2(rest,last)
      require(1<=last && last<=3,"Player must always play 1,2,3: "+last)
      l += last
      rest -= last
      p = 1 - p // other player's turn
    }
    l.toSeq
  }

  /** Evaluate a whole game.
    * @param game list of moves of one game
    * @param rest mainly for recursion
    * @return evaluation, for each move a tuple: (rest, what was played, whether this player won in the end)
    */
  def evaluate(game:Seq[Int],rest:Int=N):Seq[(Int,Int,Boolean)] = {
    if (game.size == 0) Seq()
    else Seq((rest,game.head,game.size%2 == 1)) ++ evaluate(game.tail,rest - game.head)
  }

  def main(args: Array[String]): Unit = {
    // here you can put whatever player combination you like
    val players = Seq(playerRandom,playerRandom) // random robot vs random robot
    //val players = Seq(playerRandom,playerBest) // random robot vs best robot
    //val players = Seq(playerHuman,playerBest) // You vs best robot
    var p0won = 0
    val n = 1000 // number of games to play
    var m = 0 // games actually played (a human player might quit before n)
    try {
      (1 to n).foreach { i =>
        val g = play(players)
        require(g.sum == N) // some validity checks
        val e = evaluate(g)
        require(e.size == g.size && e.last._3 && e(0)._3 != e(1)._3) // some validity checks
        if (e(0)._3) p0won += 1
        m += 1
        println(i + ": " + players(0)._1 + " " + (if (e(0)._3) "won" else "lost") + " against " + players(1)._1 + ". " + g + " => " + e)
      }
    } catch {
      case t:Throwable => println(t.getMessage)
    }
    println("Player0: "+players(0)._1)
    println("Player1: "+players(1)._1)
    println(f"Player0 won ${p0won} times out of ${m}, or ${p0won * 100.0 / m}%%")
  }
}

uBasic/4tH

Translation of: FreeBASIC
dim @p(4) 

push 1, 1, 3, 2
for i = 3 to 0 step -1 : @p(i) = pop() : next
a = 0 : t = rnd(2)
 
do while s < 21
  t = 1 - t
  print using "The sum is _#"; s
  if t = 1 then
    print "It is your turn."
    do 
      while (a < 1) + (a > 3) + (a+s > 21)
      input "How many would you like to add? ", a
    loop
  else
    print "It is the computer's turn."
    a = @p(s % 4)
    print using "The computer adds #."; a
  endif

  print
  s = s + a
  a = 0
loop
 
if t = 1 then
  print "Congratulations. You win."
else
  print "Bad luck. The computer wins."
endif

Visual Basic .NET

Platform: .NET

Works with: Visual Basic .NET version 2019

To compile this program you need to create a Visual Basic .NET project called Game21vb in Microsoft Visual Studio and paste (removing the previous content) to the MainWindow.xaml and MainWindow.xaml.vb files the source code given below.

The file MainWindow.xaml.vb with Visual Basic source code.

' Game 21 in VB.NET - an example for Rosetta Code

Class MainWindow
    Private Const GOAL As Integer = 21
    Private total As Integer = 0
    Private random As New Random

    Private Sub Update(box As TextBox, player As String, move As Integer)
        total = total + move
        box.Text = move
        boxTotal.Text = total
        If total + 1 > GOAL Then button1.IsEnabled = False
        If total + 2 > GOAL Then button2.IsEnabled = False
        If total + 3 > GOAL Then button3.IsEnabled = False
        If total = GOAL Then
            winner.Content = $"The winner is {player}."
        End If
    End Sub

    Private Sub Ai()
        Dim move As Integer = 1
        For i = 1 To 3
            If (total + i - 1) Mod 4 = 0 Then move = i
        Next i
        For i = 1 To 3
            If total + i = GOAL Then move = i
        Next i
        Update(boxAI, "AI", move)
    End Sub

    Private Sub Choice(sender As Object, e As RoutedEventArgs) _
            Handles button1.Click, button2.Click, button3.Click
        Update(boxHuman, "human", sender.Content)
        If total < GOAL Then Ai()
    End Sub

    ' StartGame method handles both OnLoad (WM_INIT?) event 
    ' as well as the restart of the game after user press the 'restart' button.
    '
    Private Sub StartGame(sender As Object, e As RoutedEventArgs) Handles restart.Click

        total = 0

        boxAI.Text = ""
        boxHuman.Text = ""
        boxTotal.Text = ""
        'first.Content = "" ' It is not necessary, see below.
        winner.Content = ""

        button1.IsEnabled = True
        button2.IsEnabled = True
        button3.IsEnabled = True

        ' The random.Next(2) return pseudorandomly either 0 or 1. Generally
        ' random.Next(n) Return a value from 0 (inclusive) To n - 1 (inclusive).
        '
        If random.Next(2) = 0 Then
            first.Content = "First player is AI player."
            Ai()
        Else
            first.Content = "First player is human player."
        End If
    End Sub
End Class

The file MainWindow.xaml with GUI source code written in XAML.

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Game21vb"
        mc:Ignorable="d"
        Title="Game 21" Height="292" Width="409" Loaded="StartGame">
    <Grid>
        <TextBlock 
            HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="64" Width="376">
            Game 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.
        </TextBlock>
        <Label Content="AI" HorizontalAlignment="Left" Margin="60,121,0,0" VerticalAlignment="Top" Width="49" HorizontalContentAlignment="Right"/>
        <Label Content="Human" HorizontalAlignment="Left" Margin="60,152,0,0" VerticalAlignment="Top" HorizontalContentAlignment="Right"/>
        <Label Content="Total" HorizontalAlignment="Left" Margin="60,183,0,0" VerticalAlignment="Top" Width="49" HorizontalContentAlignment="Right"/>
        <TextBox x:Name="boxAI" HorizontalAlignment="Left" Height="23" Margin="114,125,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" IsReadOnly="True"/>
        <TextBox x:Name="boxHuman" HorizontalAlignment="Left" Height="23" Margin="114,156,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" IsReadOnly="True"/>
        <TextBox x:Name="boxTotal" HorizontalAlignment="Left" Height="23" Margin="114,187,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" IsReadOnly="True"/>
        <Button x:Name="button1" Content="1" HorizontalAlignment="Left" Margin="114,220,0,0" VerticalAlignment="Top" Width="25"/>
        <Button x:Name="button2" Content="2" HorizontalAlignment="Left" Margin="144,220,0,0" VerticalAlignment="Top" Width="25"/>
        <Button x:Name="button3" Content="3" HorizontalAlignment="Left" Margin="174,220,0,0" VerticalAlignment="Top" Width="25"/>
        <Button x:Name="restart" Content="restart" HorizontalAlignment="Left" Margin="215,220,0,0" VerticalAlignment="Top" Width="75"/>
        <Label x:Name="winner" HorizontalAlignment="Left" Margin="245,184,0,0" VerticalAlignment="Top" Width="133"/>
        <Label x:Name="first" HorizontalAlignment="Left" Margin="10,79,0,0" VerticalAlignment="Top"/>
    </Grid>
</Window>

V (Vlang)

Translation of: Go
import os
import rand
import rand.seed
import strconv

fn get_choice(mut total &int) bool {
    for {
        text := os.input("Your choice 1 to 3 : ")
        if text == "q" || text == "Q" {
            return true
        }
        input := strconv.atoi(text) or {-1}
        if input == -1 {
            println("Invalid number, try again")
            continue
        }
        new_total := *total + input
        match true {
            input < 1 || input > 3 {
                println("Out of range, try again")
            }
            new_total > 21 {
                println("Too big, try again")
            }
            else {
                total = new_total
                println("Running total is now ${*total}")
                return false
            }
        }
    }
    return false
}
 
fn main() {
    rand.seed(seed.time_seed_array(2))
    mut computer := rand.intn(2) or {0} != 0
    println("Enter q to quit at any time\n")
    if computer {
        println("The computer will choose first")
    } else {
        println("You will choose first")
    }
    println("\nRunning total is now 0\n")
    mut choice := 0
    mut total := 0
    for round := 1; ; round++ {
        println("ROUND $round:\n")
        for i := 0; i < 2; i++ {
            if computer {
                if total < 18 {
                    choice = 1 + rand.intn(3) or {1}
                } else {
                    choice = 21 - total
                }
                total += choice
                println("The computer chooses $choice")
                println("Running total is now $total")
                if total == 21 {
                    println("\nSo, commiserations, the computer has won!")
                    return
                }
            } else {
                quit := get_choice(mut total)
                if quit {
                    println("OK, quitting the game")
                    return
                }
                if total == 21 {
                    println("\nSo, congratulations, you've won!")
                    return
                }
            }
            println('')
            computer = !computer
        }
    }
}
Output:

Sample game:

Enter 0 to quit at any time

You will choose first

Running total is now 0

ROUND 1:

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

The computer chooses 2
Running total is now 5

ROUND 2:

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

The computer chooses 1
Running total is now 9

ROUND 3:

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

The computer chooses 3
Running total is now 15

ROUND 4:

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

The computer chooses 3
Running total is now 20

ROUND 5:

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

So, congratulations, you've won!

Wren

Translation of: Go
Library: Wren-fmt
Library: Wren-ioutil
import "./fmt" for Conv
import "./ioutil" for Input
import "random" for Random

var total = 0
var quit = false

var getChoice = Fn.new {
    while (true) {
        var input = Input.integer("Your choice 1 to 3: ", 0, 3)
        if (input == 0) {
            quit = true
            return
        }
        var newTotal = total + input
        if (newTotal > 21) {
            System.print("Too big, try again")
        } else {
            total = newTotal
            System.print("Running total is now %(total)")
            return
        }
    }
}

var rand = Random.new()
var computer = Conv.itob(rand.int(2))
System.print("Enter 0 to quit at any time\n")
if (computer) {
    System.print("The computer will choose first")
} else {
    System.print("You will choose first")
}
System.print("\nRunning total is now 0\n")
var round = 1
while (true) {
    System.print("ROUND %(round):\n")
    for (i in 0..1) {
        if (computer) {
            var choice = (total < 18) ? 1 + rand.int(3) : 21 - total
            total = total + choice
            System.print("The computer chooses %(choice)")
            System.print("Running total is now %(total)")
            if (total == 21) {
                System.print("\nSo, commiserations, the computer has won!")
                return
            }
        } else {
            getChoice.call()
            if (quit) {
                System.print("OK, quitting the game")
                return
            }
            if (total == 21) {
                System.print("\nSo, congratulations, you've won!")
                return
            }
        }
        System.print()
        computer = !computer
    }
    round = round + 1
}
Output:

Sample game:

Enter 0 to quit at any time

You will choose first

Running total is now 0

ROUND 1:

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

The computer chooses 2
Running total is now 5

ROUND 2:

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

The computer chooses 1
Running total is now 9

ROUND 3:

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

The computer chooses 3
Running total is now 15

ROUND 4:

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

The computer chooses 3
Running total is now 20

ROUND 5:

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

So, congratulations, you've won!