Work the Shell - Keeping Score in <emphasis>Yahtzee</emphasis>

 in
Push an array sort out to the sort function instead of writing a sort routine.

Last month, I started talking about how to use some simple shell scripting techniques to create a computer simulation of the popular dice game Yahtzee. I'm not going to write the entire game (the computer player would be darn complicated for a shell script, for one thing), but let's spend one more column looking at some of the basics of scoring before we move on to other topics.

One nice thing about Yahtzee as a computer game is that there really isn't much work for the computer to do, because the game works fine as a solitaire variation: you simply can play to try to maximize your score and see how you do, competing against your previous high score.

The basic idea of Yahtzee is that you roll five dice up to three times to get the best possible set of values. Those are then scored against a fixed score pad that offers specific points for specific combinations, ranging from one point per “one” in your roll up to 50 points for five of a kind (a “Yahtzee”, in game parlance).

A quick visit to Google identifies an on-line Yahtzee score sheet; take a look at it before we proceed: www.gamingcorner.nl/images/sheets/yahtzee-3.pdf.

In many ways, it's five-card draw poker with dice, so the first section of the score sheet is for sets: ones, twos, threes and so on, where your score for each is the sum of that value on the dice. The second section is other poker hands, including three of a kind, four of a kind, small straight (four of the five dice have sequential values), large straight (all five are sequential), a full house and one or more Yahtzee rolls.

You can get bonuses for achieving certain levels, including a very nice 35-point bonus for attaining at least 63 on the top section, but the score sheet itself is straightforward.

The key to the game, then, is to figure out how to score a given roll. If you roll four ones, do you want to score that as your four of a kind or your ones? What if they're both filled in already? Fortunately, we're going to defer to the player for that, but that still leaves us with the data question of how to model the different boxes on the score sheet and the interface question of how to prompt the user to select which box to score. Let's take a look.

Modeling the Score Sheet as an Array

As with most of these types of data structures, we'll use an array to model the score sheet. Count the boxes on the score sheet, and you'll see 13 boxes total, including the special Yahtzee box where you can roll—and get credit for—more than one (so a game typically has 13 rolls, but it could have more).

If we initialize the game by filling in all the array values with a known stop value, say -1, then the test for whether a given box has been filled in is easy:

if [ scoresheet[1] != -1 ] ; then
  echo "1: Your one's" ; fi

The trick is that we also want to pre-qualify these options. There's no point in prompting players to select their roll as a Yahtzee if they didn't get five of a kind. This proves to be a bit tricky, so as a first step, I'm going to tweak the code to order the dice in the array in ascending order after each roll automatically.

It might be a long trip to accomplish the task, but rather than write a sorting routine in the script, I'm just going to push out the task to the sort function, then read the results back in and fit them into the individual slots of the dice array. Sound complicated? It is, rather:

function sortDice()
{
   sorted="$( ( echo ${dice[1]} ; echo ${dice[2]}
     echo ${dice[3]} ; echo ${dice[4]}
     echo ${dice[5]} ) | sort )"

   index=1
   for value in $sorted ; do
     dice[$index]=$value
     index=$(( $index + 1 ))
   done
}

You can see here that I'm using a temp variable called sorted to store the resultant values, and that I'm using a subshell—that's the $( ) notation—to do the actual work. The hardest part of this little function is to figure out how to put the values back into the array once everything's sorted properly, and that's accomplished with the for loop.

Notice that, by a lucky coincidence, for loops automatically step through fields separated by white space (spaces and carriage returns), so it's perfect for breaking the resultant sorted sequence back into individual values.

We're running low on space this month, and I'm afraid I've ended up spending quite a bit of time talking, rather than coding. I'll make it up to you, dear reader, next month!

Dave Taylor is a 26-year veteran of UNIX, creator of The Elm Mail System, and most recently author of both the best-selling Wicked Cool Shell Scripts and Teach Yourself Unix in 24 Hours, among his 16 technical books. His main Web site is at www.intuitive.com, and he also offers up tech support at AskDaveTaylor.com.

______________________

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.

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState