/home/wpollock1/public_html/restricted/ShellScripting/bj
#!/bin/bash
# bj - Black-Jack Game Shell Script Project Model Solution.
#
# Written 3/2006 by Wayne Pollock, Tampa Florida USA
# $Id: bj,v 1.3 2006/07/06 03:03:58 wpollock Exp $
# TODO: modify getKey to be passed a list of valid choices, so
# illegal values display error message and retry.
#set -x
PATH=/bin:/usr/bin
# This shell script should play an interactive game of black-jack.
# This is a simplified version of the game. The rules:
#
function rules
{
cat <<'EOF'
A "shoe" containing some number of standard decks of (52) cards
is shuffled. Cards are dealt one at a time from the shoe. When
the shoe is nearly (or completely) empty, a new shoe is used.
(Actually the old shoe is re-stocked with cards and is shuffled.)
The dealer and player are each dealt two cards. The player can
only see their two cards and the first card of the dealer's hand.
Each hand has a value obtained by summing the value of each
card held, with picture cards worth 10 points. An ace is worth
either one or 11 points.
The purpose of the game is to build a hand worth more than the
dealer's hand, without going over 21 points. The player can
draw as many cards as desired, called taking "hits". When
taking a hit, if the hand value exceeds 21 the player loses.
When satified the player "stands pat" and it is the dealer's
turn. The "house rules" require the dealer to draw a card with a
hand value of 16 or less and to stand pat with 17 or more. If
the dealer's hand value exceeds 21 the player wins. Otherwise
the player or dealer with the highest value hand wins, a tie
going to the dealer.
EOF
}
# "Initialze global REPLY that holds the user input:
REPLY=""
# Number of decks of cards per shoe:
NUMDECS=4
# Function that reads in one character without waiting for
# the user to type "enter". The single character is put into $REPLY:
function getKey
{
stty -icanon
REPLY=`dd count=1 2>/dev/null`
stty icanon
}
# Create an empty "shoe" (array), to hold NUMDECKS decks of 52 cards:
shoe=()
# This function shuffles the (global) array "shoe":
function shuffle
{
local i j temp
for (( i=${#shoe[@]}; i ; --i ))
do
j=$(( $RANDOM % i ))
temp=${shoe[i]}
shoe[i]=${shoe[j]}
shoe[j]=$temp
done
}
# This function deals and returns (in "$?") one card, then
# removes it from the shoe. When the shoe is empty, a new
# deck is created and shuffled:
function dealCard
{
if [ ${#shoe[@]} -eq 0 ]
then
let 'numCards = NUMDECS * 51'
shoe=(`seq 0 $numCards`)
shuffle
fi
local card=${shoe[0]}
# The next two lines remove the first card from the shoe:
unset shoe[0]
shoe=(${shoe[@]})
# Function return values must be -128 <= value <= 127, but with
# multiple decks the card number might be much larger. So:
let 'card = card % 52'
return $card
}
# This function nicely displays a player's hand, which
# is really an array of ints 0..51 representing cards,
# and passed to this function as $*:
function show_hand
{
local card rank suit
for card
do
let 'suit = card / 13'
let 'rank = card % 13 + 1'
case "$rank" in
1) echo -n 'A' ;;
11) echo -n 'J' ;;
12) echo -n 'Q' ;;
13) echo -n 'K' ;;
*) echo -n "$rank" ;;
esac
case "$suit" in
0) echo -n 'C ' ;;
1) echo -n 'D ' ;;
2) echo -n 'H ' ;;
3) echo -n 'S ' ;;
esac
done
}
# This function returns the value of a player's hand.
# Note picture cards count as 10, an Ace counts as 1
# unless the total is <= 11, in which case one Ace
# counts as 11:
function value
{
local value=0 has_ace=0
for card
do
let 'card = card % 13 + 1'
[ $card = 1 ] && has_ace=1
[ $card -gt 10 ] && card=10
let 'value += card'
done
if [ $value -le 11 -a $has_ace -eq 1 ]
then
let 'value += 10'
fi
return $value
}
# This function displays the dealer's and player's hands. If any
# argument is passed, show only first of dealer's cards:
function print_hands
{
echo
if [ $# != 0 ]
then echo -e "\tDealer's hand: `show_hand ${dealers_hand[0]}` ?"
else echo -e "\tDealer's hand: `show_hand ${dealers_hand[@]}`"
fi
echo -e "\tPlayer's hand: `show_hand ${players_hand[@]}`"
}
# This function plays one game:
function playGame
{
# Deal two cards to the dealer:
dealCard
dealers_hand=($?)
dealCard
dealers_hand=(${dealers_hand[@]} $?)
# Deal two cards to the player:
dealCard
players_hand=($?)
dealCard
players_hand=(${players_hand[@]} $?)
value ${players_hand[@]}
player_value=$?
REPLY=h
while [ "$REPLY" = 'h' -a "$player_value" -le 21 ]
do
print_hands hide dealer card
echo -n "Enter h for a 'hit', s to 'stand pat', or q to quit: "
getKey
if [ "$REPLY" = 'h' ]
then
dealCard
players_hand=(${players_hand[@]} $?)
value ${players_hand[@]}
player_value=$?
fi
done
[ "$REPLY" = 'q' ] && { echo; exit 0; }
# Anything other than "s" or "h" is an error, so abondon this hand:
[ "$REPLY" != 's' -a "$REPLY" != 'h' ] && { echo; return 0; }
if [ $player_value -gt 21 ]
then
print_hands
echo Sorry, you lost this time. Why not play again!
return 0
fi
# House rules: dealers hits on 16, stands pat on 17:
value ${dealers_hand[@]}
dealer_value=$?
while [ $dealer_value -lt 17 ]
do
dealCard
dealers_hand=(${dealers_hand[@]} $?)
value ${dealers_hand[@]}
dealer_value=$?
done
print_hands
if [ $dealer_value -gt 21 ]
then
echo 'Drat! I lost! Play again and gimmie a chance to get even!'
return 0
fi
# Note a tie score goes to the dealer:
if [ $player_value -gt $dealer_value ]
then
echo 'Congratulations, you won! Why not play again?'
else
echo 'Too bad, you lost. Better luck next time!'
fi
}
# Main program starts here:
echo -n "Welcome to Black-Jack! Would you like to see the rules? (y/n) "
getKey
if [ "$REPLY" = "y" ]
then rules
fi
while :
do
playGame
echo -n 'Play Again? (y/n) '
getKey
[ "$REPLY" != "y" ] && { echo; exit 0; }
done