Work the Shell - Calling All Functions, and Some Math Too!
If you've been keeping track of my column, you'll know that we're building a Blackjack game as a shell script. Why? Because most shell scripts are far too boring to study without nodding off, so considering all the nuances of shell script programming within the context of a game just sounds more fun!
My last column talked about how to shuffle a deck of cards, as portrayed in a program with a simple array of 52 values, 1-52. There are some interesting nuances to the shuffle problem. Let's start there. Then we'll look at how to turn an arbitrary 1-52 value into a familiar rank and suite from a deck of cards.
If you've been writing shell scripts since the dawn of UNIX, you might not have realized that modern shells now support functions and procedures, just like a “real” programming language. For any block of code that you plan on executing more than once, it's the way to go.
Here's the shuffle code, written as a shell function:
function shuffleDeck
{
count=1
while [ $count -le 52 ]
do
pickCard
newdeck[$count]=$picked
count=$(( $count + 1 ))
done
}
This builds an array called newdeck, which is actually the shuffled deck (in the last column we showed deck, which was an array of cards in linear order), and it uses some basic shell math with the $(( )) notation to save spawning a subshell to invoke expr for each increment of the count variable.
I said that shell scripts are robust programming environments and that might be a tiny bit of hyperbole, really. Eagle-eyed readers will notice that the pickCard function returns its value by setting a global variable, picked, which isn't really optimal programming strategy. But it works, and pragmatism is an important part of any good approach to software development, isn't it?
The full pickCard function is key to making this work, but it is too long to include here, so grab it from the LJ FTP site for your reading pleasure (ftp.linuxjournal.com/pub/lj/listings/8774.tgz).
With the shuffleDeck function written and an initializeDeck function, as shown here:
function initializeDeck
{
card=1 while [ $card -le 52 ] do
deck[$card]=$card card=$(( $card + 1 ))
done
}
it's easy to do the rudiments of shuffling the deck and dealing out two cards for the player and two for the dealer:
initializeDeck shuffleDeck
echo "** Player's hand: ${newdeck[1]}, ${newdeck[3]}"
echo "** Dealer's hand: ${newdeck[2]}, ${newdeck[4]}"
Let's run this and see what kind of results we get:
$ ./blackjack.sh ** Player's hand: 22, 49 ** Dealer's hand: 11, 8 $ ./blackjack.sh ** Player's hand: 19, 32 ** Dealer's hand: 49, 10 $ ./blackjack.sh ** Player's hand: 44, 23 ** Dealer's hand: 46, 11
Displaying cards as a numeric value from 1-52 is not the most friendly, so let's turn our attention to the display of the card values in the familiar rank and suite of a traditional deck of playing cards.
A deck of cards is composed of 52 cards, split evenly into four suites of 13 cards. The order of the suites doesn't matter (in Blackjack, at least), but the rank does. Indeed, the goal of the game is have a summary rank value of 21 points without going any higher.
The rank of a card is the remainder of the numeric card value divided by 13. In math terms, this is called the modulus and can be computed thusly:
rank = cardvalue % 13
To put this into proper shell notation, we'll again use the $(( )) shortcuts and end up with:
rank=$(( $card % 13 ))
Getting the suite should be simple too; it's the other half of the division. In other words, if the card value is 17, then 17/13 = 1, meaning it's suite #1, and 17%13 = 4. Since we'd like to have our suites in the range of 1-4, rather than 0-3, however, we'll need to add one to the equation. Further, the 13th of each card is the same suite as the earlier 12, so we'll also have to subtract one before we do the division (card #13 would be suite #1 if we just calculated 13/13, but if we calculate 12/13 for that, we'll correctly identify it as part of suite #0).
This is very confusing, so here's the equation instead:
suite="$(( ( ( $card - 1) / 13 ) + 1))"
Much clearer, right? Seriously, you can experimentally verify that this works correctly. The important edge cases are value=1, value=12, value=13 and value=14. If you can get those right, you're good for all values in the deck.
Once we've identified the rank and suite of a card, we just have to do a bit of fancy footwork to turn numbers into words:
case $suite in 1 ) suite="Hearts" ;; 2 ) suite="Clubs" ;; 3 ) suite="Spades" ;; 4 ) suite="Diamonds" ;; * ) echo "Bad suite value: $suite"; exit 1 esac
Dave Taylor has been hacking shell scripts for over thirty years. Really. He's the author of the popular "Wicked Cool Shell Scripts" and can be found on Twitter as @DaveTaylor and more generally at www.DaveTaylorOnline.com.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Designing Electronics with Linux | May 22, 2013 |
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
- I once had a better way I
4 hours 56 min ago - Not only you I too assumed
5 hours 13 min ago - another very interesting
7 hours 6 min ago - Reply to comment | Linux Journal
9 hours 8 sec ago - Reply to comment | Linux Journal
15 hours 54 min ago - Reply to comment | Linux Journal
16 hours 10 min ago - Favorite (and easily brute-forced) pw's
18 hours 1 min ago - Have you tried Boxen? It's a
23 hours 53 min ago - seo services in india
1 day 4 hours ago - For KDE install kio-mtp
1 day 4 hours ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?




Comments
SUIT/SUITE
AARGH - four SUITs of cards... a SUITE of rooms... where's your editor!