Work the Shell - <emphasis>Yahtzee</emphasis> as a Shell Script? When Will It End?
We seem to spend a lot of time talking about games and how to program them as shell scripts, don't we? From Blackjack to Baccarat, we're in danger of having to rename this column “game programming in the shell”. But, that'd be crazy; who in the heck would write multiple games as shell scripts?
So, this month, I thought it would be fun to look at a dice game and see how the basic set of playing card functions we've written previously compare to the necessary functions to play a dice game.
Yahtzee was first introduced by Hasbro in 1956 as Yacht (having been invented by a wealthy couple on their fancy boat) and has been one of its best-selling titles since, spawning many variants, including hand-held electronic games and more. At its heart though, it's basically five-card draw poker played with dice. The wrinkle is that there are a set number of possible hands you can roll, and you attempt to achieve them all to maximize your score.
For example, roll a 3 4 4 4 5, and you might well pick up the 3 and the 5, hoping for either “your fours” (which you can get only once and want to choose when you have the maximum number of fours showing), or if you get five of a kind, a “Yahtzee”, which is a big-points bonus but obviously difficult to achieve.
Like five-card draw, you can pick up zero to five dice and reroll them, but unlike five-card draw, you can do this twice on your turn, not once. So, perhaps the 3 4 4 4 5 rerolls as a 1 4 4 4 4. The second roll would then be to reroll the one and hope for another four. Either way, it's a good roll (unless you've already marked your fours).
Dice are quite easy to create in a script—so easy it reveals how straightforward a script like liar's dice would be to write:
function rollDie()
{
dieroll=$(( ( $RANDOM % 6 ) + 1 ))
}
If it's this easy to roll a die, though (dice, by the way, is plural of die), it'd be darn easy to write a quick Dungeons and Dragons dice roller too, as shown:
function rollDie()
{
sides=${1:-6}
echo "testing with a $sides-sided die...."
dieroll=$(( ( $RANDOM % $sides ) + 1 ))
}
All you need to do is call rollDie with the number of sides you want on the dice it needs to roll. Using a 20-sided die? Try rollDie 20 to see what rolls.
This also can quickly and easily be converted into a command-line function, so you could be a real D&D nerd by having a laptop adjacent and typing in roll 20 every time you're actually supposed to roll the die.
But back to Yahtzee, yes? The easy part of modeling the game is the dice rolls. We need to have five dice, and that easily can be done with an array:
rollDie ; dice[0]=$dieroll rollDie ; dice[1]=$dieroll rollDie ; dice[2]=$dieroll rollDie ; dice[3]=$dieroll rollDie ; dice[4]=$dieroll
There, that's your first roll of the five dice. Displaying the results also is easy:
echo "You rolled " ${dice[0]} ${dice[1]} ${dice[2]} ${dice[3]} ${dice[4]}
Note carefully where I do and don't need to use the curly braces to get the array to work properly in the shell. Try this to see how it differs:
echo "You rolled " $dice[0] $dice[1]
Quite different results, as you can see. (And, as usual with shell programming, there's no useful warnings or error messages to clue you in to what might be wrong.)
Rolling the dice to get an initial hand is pretty straightforward, so let's take the next step and write the code to let you reroll any or all of the five dice twice to get your final hand.
There are a number of ways to ask for this sort of input, but to make it a bit chatty, let's simply present each die in ordinal value and let the player enter the appropriate number to indicate that it should be rerolled. Um, let me show you what I mean:
echo -n "Reroll which dice? " read answer for reroll in $answer do echo "Requested: $reroll" done
Here, you might specify that you want die 1 and 3 rerolled by typing in 1 3. Tweaking this just a bit, the for loop then can test for the validity of each entry:
for reroll in $answer
do
if [ $reroll -lt 1 -o $reroll -gt 5 ] ; then
echo "Invalid entry: $reroll. Please enter 1-5"
else
echo "Requested: $reroll"
fi
done
Now, of course, it's time for some actual logic here, not merely a rudimentary test. I've simplified things just a wee bit by using array indices 1–5 rather than 0–4, sacrificing the slot of entry 0 so that it's easier to work with the values. This means if you ask to reroll die 4, for example, it's just a reassignment of dice[4].
Here's the new, improved for loop:
for reroll in $answer
do
if [ $reroll -lt 1 -o $reroll -gt 5 ] ; then
echo "Invalid entry: $reroll. Please enter 1-5"
else
rollDie
dice[$reroll]=$dieroll
fi
done
You can see that it's quite simple, and if we're not afraid of the code stretching out a bit, we simply can copy and paste some of it to show our before and after rolls:
echo -n "Your new roll: [${dice[1]}], "
echo -n "[${dice[2]}], [${dice[3]}], "
echo "[${dice[4]}] and [${dice[5]}]"
Let's run it once to see what's happening, and then next month, we'll start working on the actual game itself, rather than just the dice rolls:
$ ./yahtzee.sh You rolled [2], [6], [5], [2] and [1] Reroll which dice? 2 3 5 Your new roll: [2], [2], [4], [2] and [5]
Yes, I snuck in the notation of having the dice values shown within square brackets just for visual appearance. It makes the echo statements a bit more confusing, as you can see just a bit earlier, but the output is more attractive.
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
| 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 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
- RSS Feeds
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- Dynamic DNS—an Object Lesson in Problem Solving
- New Products
- Validate an E-Mail Address with PHP, the Right Way
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Download the Free Red Hat White Paper "Using an Open Source Framework to Catch the Bad Guy"
- A Topic for Discussion - Open Source Feature-Richness?
- Tech Tip: Really Simple HTTP Server with Python
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!
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?




3 hours 5 min ago
5 hours 20 min ago
5 hours 49 min ago
6 hours 47 min ago
8 hours 15 min ago
9 hours 24 min ago
10 hours 11 min ago
16 hours 46 min ago
22 hours 25 min ago
1 day 4 hours ago