Computer Sciences Department logo

CS 368-4 (2011 Fall) — Day 14 Homework

Due Thursday, December 15, at the start of class.

Goal

Play a simple card game… a lot, to estimate the likelihoods of the possible outcomes.

Background Information

There is a very simple single-deck solitaire card game that I know. I cannot find a name for this game — if you happen to know a name for it, let me know!

Anyway, it is not much of a “game”, in that the outcome is completely determined by the order of cards and by the rules. Call it a pastime.

Here is how it works. Shuffle a complete 52-card deck. Play one card at a time, in order. We will call the most recently played card the “top”. Examine the top-most four cards (if there are that many yet). If the first (top) and fourth cards have the same face value, remove all four cards. If the first and fourth cards have the same suit, remove only the intervening two cards. If there are still four or more cards left, repeat this removal procedure with the new top-most four cards, until either there are fewer than four cards left or until it is not possible to remove any more cards. Then, play the next card and repeat the whole procedure. The game is won if, after dealing all 52 cards and removing ones as above, there are no more cards left in the pile.

Sample of Play

In the examples below, cards are shown as played from left to right. The “top” or first card is always the furthest to the right. You may need special fonts to see the suit characters.

Suppose the first four cards to be played are:

3♠ 7♦ J♠ 2♠

Comparing the first (“top”) card, 2♠, with the fourth card, 3♠, we see that they are not the same face value, but they are the same suit. Hence, we can remove the middle two cards, leaving the pile as follows:

3♠ 2♠

Playing two more cards, suppose we have:

3♠ 2♠ 8♣ 8♦

The first and fourth cards do not have the same face nor suit, so we keep playing. Nothing happens until we get here:

3♠ 2♠ 8♣ 8♦ K♥ 3♦ 2♦

Now, the first (2♦) and fourth (8♦) cards match suit, so we remove the middle cards, leaving:

3♠ 2♠ 8♣ 8♦ 2♦

And then, without playing another card yet, we see that the new first (2♦) and fourth (2♠) cards have the same face value, so we can remove all four top cards, leaving just one:

3♠

And so on, until we have played all 52 cards.

For what it’s worth, this game can be played with real cards in a way that does not require a table or anything like that. Great for long car, bus, train, airplane rides! See me sometime if you want a quick demo.

The Software

I wrote a little program in C that plays this card game. It creates a random shuffling of the card deck, plays through the whole game, and reports on the outcome. Here is a sample of running the program:

% /usr/local/bin/cards
HTyfzbDLEKqhvGrJgmlZMiCoAVatWBSXIdkYOwcjRsepxFUnuNPQ  4 HTPQ

(The % is the shell prompt, not something I typed.)

What is going on in the output? Well, there are 52 cards in a deck, and there are 52 letters in the alphabet, if you count upper- and lowercase separately. So, for compactness, I mapped each card to a letter: A = A♣, B = 2♣, …, N = A♦, …, a = A♥, …, n = A♠. When finished, the program prints out the entire shuffled deck as a sequence of letters. Then comes the total number of cards remaining (4 in this case), and then the exact remaining cards. One could imagine analyzing the shuffled sequences for repeats or randomness, but all we care about for this exercise is the number of cards remaining.

The program takes a single optional argument, which is an integer that is used to seed the random-number generator. If you run the program with the same seed, you get the same results:

% /usr/local/bin/cards 42
xSYRiwgHCMpuWrkIDhKjnVXcvmObFdsLAoNQtaqlGeJTzBUEZfPy 12 xSYRiwgHCMpy
% /usr/local/bin/cards 42
xSYRiwgHCMpuWrkIDhKjnVXcvmObFdsLAoNQtaqlGeJTzBUEZfPy 12 xSYRiwgHCMpy

It is fine to use the cards program without the seed. By default, it initializes the random sequence using the computer’s built-in clock, which should have very fine-grained resolution. But, we can use the seed to make sure that we get a certain number of unique random-number sequences.

For fun, you can run this program a bunch of times from the shell (not Python!). The following command (just copy and paste it verbatim into the command-line prompt on submit-368) will run the card game 1000 times and just show the winning games:

for i in {0..999}; do /usr/local/bin/cards; done | grep '  0 '

Be kind to your submit machine! Do not run the program more than 1000 times in a row on the submit machine itself. For more runs, let’s use our execute machines!

Tasks

This program is very fast, but we want to run it lots of times. Think of it this way: There are 52! ways to shuffle a card deck, which is something like 8×1067. Even factoring out symmetric deals, there are still a lot of unique games. So we want to play 10–100 million games or so, to get a good feel for the overall statistics.

Part I: Design

Obviously, this is a case for a batching wrapper script. Consider the following requirements:

Part II: Implementation

Implementation should be fairly routine, by this point. I suggest building up features in this order:

  1. Start your Python wrapper script with code that can run the card game once, with a fixed seed value, and collect and print its output. Make sure this works before going on.
  2. Add the ability to run the card game more than once, one time for each seed number in a fixed range (say, 1 through 10).
  3. Add one or more command-line arguments to specify the range of seed numbers to use, and make the main loop use those numbers. Again, on the submit machine, limit yourself to 10 to 100 seed numbers at a time.
  4. Now, instead of printing the output, tally the number of games per number of remaining cards. Print out the tallies instead of the actual output from cards.
  5. Your wrapper is done! Test it for a few different seed number ranges, make sure it is OK, and then you are ready to move on.
  6. Write a single submit file that uses the wrapper to run cards maybe 1,000 to 10,000 times. Make sure you can get a single job with the wrapper to work correctly before scaling up.
  7. Now you should be ready to implement the full set of jobs.

This is your last assignment, and so it combines lots of elements that we have done previously. In many ways, a wrapper script is nothing new (see, e.g., Homework #8 and Homework #10). Nor is running lots of jobs with precise arguments and output files and so forth. Nonetheless, start early and give yourself plenty of time to make a few mistakes before finishing! And keep in mind that the final run will take a little while — my final run of 100 million runs took about 32 minutes, start to finish.

Extra Challenges

Some ideas for extra learning:

Reminders

Do the work yourself, consulting reasonable reference materials as needed. Any resource that provides a complete solution or offers significant material assistance toward a solution not OK to use. Asking the instructor for help is OK, asking other students for help is not. All standard UW policies concerning student conduct (esp. UWS 14) and information technology apply to this course and assignment.

Hand In

A printout of your wrapper script, and any required support files (templates, Condor submit files, whatever), ideally on a single sheet of paper (double-sided is great!). Be sure to put your own name clearly on each piece of paper, regardless; identifying your work is important, or you may not receive appropriate credit.