Whitejack

if 0 {


Introduction

MiHa 2015-06-24: One of the example-programs on my HelloWorld-page creates a deck of cards,
so I had the idea to expand that into a simple cardgame, for the tcl-console.

Blackjack is quite simple, and with (debug-)options still in place to show the cards "in the open",
this would nicely double as a tcl-tutorial (for using string and list), and a blackjack-trainer.

With ideas and code from the following pages:

  • HelloWorld (MiHa) - the examples using foreach, while, gets, unicode and list
  • ...

}


Program

 # Whitejack.tcl - MiHa - 2015-06-25
 # https://wiki.tcl-lang.org/41565
 # http://ideone.com/jVxnHV

  puts "Whitejack:\n"
  
  proc help {}  {     ;# Todo: needs rewording
    puts "\nWhitejack - a cardgame much like simplified, self-service blackjack."
    puts {It is played with one or more decks of 52 cards. 
The object of the game is to beat the dealer:
* Get 21 points on the player's first two cards (called a blackjack), without a dealer blackjack;
* Reach a final score higher than the dealer without exceeding 21; 
* orLet the dealer draw additional cards until his or her hand exceeds 21.
The player is dealt an initial two-card hand and add together the value of their cards. 
Face cards (kings, queens, and jacks) count as ten points. 
An ace is worth 1 point or 11 points, owners choice. 
All other cards count as the numeric value shown on the card. 
After their initial two cards, players can get a "hit", i.e. take an additional card. 
In a given round, the player or the dealer wins by having a score of 21 or
by having the highest score that is less than 21. 
Scoring higher than 21 (called "busting" or "going bust") results in a loss.Commands:
---------
h   : help
q   : quit

n   : new cards: add a fresh pack of 52 cards to the deck.
f   = fill: move cards from discard-pile to the end of the deck.
t   = trash: empty both hands and the discard-pile.

v   = move the card from the front of the deck to the discard-pile
0-9 : move the card from position 0..9 of the deck to the end of the deck.
%   = swap the two cards at the front of the deck (positions 0 and 1).
s   = shuffle the deck (do a number of "1-9"-actions).

p,+ : player-draw: move first card from deck to player's hand
d,- : dealer-draw: move first card from deck to dealer's hand

b,l : bust: player loses the round
w   : win : player wins the round

x   = discard: move cards from player's hand to the discard-pile
y   = discard: move cards from dealer's hand to the discard-pile
*   : discard: move cards from both hands to the discard-pile
}

  }


  proc showStatus {}  {
      global deck discard playerHand dealerHand cWin cLoss

      puts "\nStatus:"
      puts -nonewline "Number of cards in "
      puts -nonewline       "deck: [llength $deck]  "
      puts -nonewline    "discard: [llength $discard]  "
      puts -nonewline "playerHand: [llength $playerHand]  "
      puts            "dealerHand: [llength $dealerHand]  "
      puts "Wins: $cWin  Losses: $cLoss"

     #puts "card #3 [lindex $::deck 2]"        ;# index is zero-based
     #showList "Deck" $deck 0 19
      showFirst "Deck" $deck 18
      showLast  "Deck" $deck 18
      
      puts "Player: $playerHand  Value: [cardValue $playerHand]" 
      puts "Dealer: $dealerHand  Value: [cardValue $dealerHand]"  
  }

  proc win {x}  {
    if { $x>0 } { 
      incr ::cWin  
      puts "Win!"            
    } else {
      puts "Lost!"
      incr ::cLoss
    }
  }
  
  proc makeCards {}  {
    global deck discard playerHand dealerHand 

    puts -nonewline "Making cards..."
    set i 0
    # For unicode, see also: https://wiki.tcl-lang.org/26403 : [HTML character entity references]

                     # Spades    Hearts    Diamonds  Clubs
    foreach {suit sym} { S \u2660  H \u2665  D \u2666  C \u2663 } {   
      foreach rank { A 2 3 4 5 6 7 8 9 10 J Q K } { ;# Ace 2 .. 10 Jack Queen King
        incr i
        set card "$rank$sym"
       #puts -nonewline " $rank$suit=$card "
        lappend deck $card                     ;# add card to list
      }
   #puts ""
    }
    set maxCard $i
    puts "done.\nPack of $maxCard cards added to the deck."
   #puts "\ndeck : $deck"                ;##
  }

  proc c1 {s} { set c [string range $s 0 0] }  ;# return first char of string
 
  proc cardValue {L}  {
  # calculate value of the cards in the list
  
   #puts "$tx $p1-$p2: [lrange $L $p1 $p2]"
   #set v 0
   
    for {set i 0; set sum 0} {$i < [llength $L]} {incr i} {
      set card [lindex $L $i] 
      set c [c1 $card]                   ;# get first char from card
      set v [string first $c "--234567891AJKQ"]
      if { $v< 0 } {set v  0 }           ;# not found
      if { $v>11 } {set v 10 }           ;# J,Q,K
     #puts -nonewline "(($i: $c = $v)) " ;##
      incr sum $v
    }  
    return $sum
  }
  
  proc showList {tx L p1 p2}  {
  # show elements in list L from position p1 to p2
    puts "$tx $p1-$p2: [lrange $L $p1 $p2]"
  }
  proc showFirst {tx L n}  {   ;# show the first n elements in list L
    puts "$tx 0-$n: [lrange $L 0 $n] ..."
  }
  proc showLast {tx L n}  {   ;# show the last n elements in list L
    set p2 [llength $L]
    set p1 $p2; incr p1 -$n
    puts "$tx $p1-$p2: ... [lrange $L $p1 $p2]"
  }
  
  proc deal_p {}  {
  # deal card from deck to player's hand:
     global deck discard playerHand dealerHand 
  
     set card [lindex $deck 0]               ;# get first card from deck
     lappend playerHand $card                ;# put it in his hand
                                             ;# delete it from the deck:
     set deck [lreplace $deck[set deck {}] 0 0 ]
     # see also: [lreplace] - "Modifying a List In-Place"

   ##puts "deck : $deck"
   ##showList "Deck" $deck 0 18
  }

  proc moveCard {p}  {
  # move card from position p at front of deck to end of deck:
     global deck discard playerHand dealerHand 
     
    #puts "move $p:"
  
     set card [lindex $deck $p]              ;# get first card from deck
     lappend deck $card                      ;# put it at end
     
                                             ;# delete it from the deck:
     set deck [lreplace $deck[set deck {}] $p $p ]
  }

  proc clearHands {}  {
  # move cards to discard-pile
    global deck discard playerHand dealerHand 
 
    set playerHand {}
    set dealerHand {}
  # Todo: move cards to the discard-pile
   #puts "--"
  }  
  
  proc deal_d {}  {
  # deal card from deck to dealer's hand:
     global deck discard playerHand dealerHand 
  
     set card [lindex $deck 0]               ;# get first card from deck
     lappend dealerHand $card                ;# put it in his hand
                                             ;# delete it from the deck:
     set deck [lreplace $deck[set deck {}] 0 0 ]
  }
  
  proc deal {to}  {
  # deal card from deck to player- or dealer's hand:
    #global deck discard playerHand dealerHand 
  
     set card [lindex $::deck 0]             ;# get first card from deck
     lappend $to $card                       ;# put it in his hand
                                             ;# delete it from the deck:
     set ::deck [lreplace $::deck[set ::deck {}] 0 0 ]
     # see also: [lreplace] - "Modifying a List In-Place"

   ##puts "deck : $::deck"
   ##showList "Deck" $::deck 0 18
  }
  
  set deck {}                                  ;# create empty list
  set discard {}
  set playerHand {}
  set dealerHand {}
  
  set cWin  0
  set cLoss 0
  set dealerCash 1000
  set playerCash  100

  puts "Start with 'n' to get a fresh pack of cards."

  set cmd "."                ;# try: n 1  + - + -  + - -  w * q
  while { $cmd ne "q" } {
    puts -nonewline "\nEnter command (h for help, q to quit): "
    set cmd [gets stdin]
    puts $cmd

    if {$cmd=="h"} { help }
    if {$cmd=="q"} { puts "Bye!"; exit }
    
    if {$cmd=="n"} { makeCards }
    if {$cmd=="0"} { moveCard 0 }
    if {$cmd=="1"} { moveCard 1 }
    if {$cmd=="2"} { moveCard 2 }
    if {$cmd=="3"} { moveCard 3 }
    if {$cmd=="4"} { moveCard 4 }
    if {$cmd=="5"} { moveCard 5 }
    if {$cmd=="6"} { moveCard 6 }
    if {$cmd=="7"} { moveCard 7 }
    if {$cmd=="8"} { moveCard 8 }
    if {$cmd=="9"} { moveCard 9 }
    
    if {$cmd=="+"} { deal_p }
    if {$cmd=="-"} { deal_d }
   #if {$cmd=="d"} { deal $dealerHand }
   #if {$cmd=="p"} { deal $playerHand }
   
   if {$cmd=="w"} { win  1 }
   if {$cmd=="b"} { win -1 }
   if {$cmd=="*"} { clearHands }
   
  # ...
    showStatus
  }

#.

Output

Whitejack:

Start with 'n' to get a fresh pack of cards.

Enter command (h for help, q to quit): n
Making cards...done.
Pack of 52 cards added to the deck.

Status:
Number of cards in deck: 52  discard: 0  playerHand: 0  dealerHand: 0  
Wins: 0  Losses: 0
Deck 0-18: A♠ 2♠ 3♠ 4♠ 5♠ 6♠ 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ ...
Deck 34-52: ... 9♦ 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣
Player:   Value: 0
Dealer:   Value: 0

Enter command (h for help, q to quit): 1

Status:
Number of cards in deck: 52  discard: 0  playerHand: 0  dealerHand: 0  
Wins: 0  Losses: 0
Deck 0-18: A♠ 3♠ 4♠ 5♠ 6♠ 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ ...
Deck 34-52: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player:   Value: 0
Dealer:   Value: 0

Enter command (h for help, q to quit): +

Status:
Number of cards in deck: 51  discard: 0  playerHand: 1  dealerHand: 0  
Wins: 0  Losses: 0
Deck 0-18: 3♠ 4♠ 5♠ 6♠ 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ ...
Deck 33-51: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player: A♠  Value: 11
Dealer:   Value: 0

Enter command (h for help, q to quit): -

Status:
Number of cards in deck: 50  discard: 0  playerHand: 1  dealerHand: 1  
Wins: 0  Losses: 0
Deck 0-18: 4♠ 5♠ 6♠ 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ ...
Deck 32-50: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player: A♠  Value: 11
Dealer: 3♠  Value: 3

Enter command (h for help, q to quit): +

Status:
Number of cards in deck: 49  discard: 0  playerHand: 2  dealerHand: 1  
Wins: 0  Losses: 0
Deck 0-18: 5♠ 6♠ 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ ...
Deck 31-49: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player: A♠ 4♠  Value: 15
Dealer: 3♠  Value: 3

Enter command (h for help, q to quit): -

Status:
Number of cards in deck: 48  discard: 0  playerHand: 2  dealerHand: 2  
Wins: 0  Losses: 0
Deck 0-18: 6♠ 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ J♥ ...
Deck 30-48: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player: A♠ 4♠  Value: 15
Dealer: 3♠ 5♠  Value: 8

Enter command (h for help, q to quit): +

Status:
Number of cards in deck: 47  discard: 0  playerHand: 3  dealerHand: 2  
Wins: 0  Losses: 0
Deck 0-18: 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ J♥ Q♥ ...
Deck 29-47: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player: A♠ 4♠ 6♠  Value: 21
Dealer: 3♠ 5♠  Value: 8

Enter command (h for help, q to quit): -

Status:
Number of cards in deck: 46  discard: 0  playerHand: 3  dealerHand: 3  
Wins: 0  Losses: 0
Deck 0-18: 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ J♥ Q♥ K♥ ...
Deck 28-46: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player: A♠ 4♠ 6♠  Value: 21
Dealer: 3♠ 5♠ 7♠  Value: 15

Enter command (h for help, q to quit): -

Status:
Number of cards in deck: 45  discard: 0  playerHand: 3  dealerHand: 4  
Wins: 0  Losses: 0
Deck 0-18: 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ J♥ Q♥ K♥ A♦ ...
Deck 27-45: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player: A♠ 4♠ 6♠  Value: 21
Dealer: 3♠ 5♠ 7♠ 8♠  Value: 23

Enter command (h for help, q to quit): w
Win!

Status:
Number of cards in deck: 45  discard: 0  playerHand: 3  dealerHand: 4  
Wins: 1  Losses: 0
Deck 0-18: 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ J♥ Q♥ K♥ A♦ ...
Deck 27-45: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player: A♠ 4♠ 6♠  Value: 21
Dealer: 3♠ 5♠ 7♠ 8♠  Value: 23

Enter command (h for help, q to quit): *

Status:
Number of cards in deck: 45  discard: 0  playerHand: 0  dealerHand: 0  
Wins: 1  Losses: 0
Deck 0-18: 9♠ 10♠ J♠ Q♠ K♠ A♥ 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ J♥ Q♥ K♥ A♦ ...
Deck 27-45: ... 10♦ J♦ Q♦ K♦ A♣ 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ 2♠
Player:   Value: 0
Dealer:   Value: 0

Enter command (h for help, q to quit): q
Bye!

Comments

MiHa 2015-06-26: The most basic functionality is done, the largest missing parts now are the discard-pile, and shuffling.

I think I need a much better look at upvar, and how params to proc work,
with regard to changing values outside the proc.
As in call-by-value, vs. call-by-reference.


See also: