PREVIOUS  TABLE OF CONTENTS  NEXT 

Solitaire 500 Results

David Nicol

URL: http://tipjar.com/games/solitaire/wheels/

On May 1, 1999, an air-conditioned room deep within the University of Missouri Kansas City's Computing Services department was transformed for a brief eight hours into the mission control center for what may very well have been the first ever programming contest of its kind: the Solitaire 500, described in TPJ #13

As you might recall, all programs entered in the Solitaire 500 ran simultaneously on the same computer. They had to make network connections to a server running on a different computer and solve an identical series of five hundred problems presented by the server. The first to complete the series of problems won

Nineteen entries reached the contest's e-mail inbox before the morning of May first.
ID   Name
1   Theo Van Dinter
2   Joseph A. Diverdi
3   Steven W. McDougall
4   Eero Pajarre
5   Mark Kvale
6   Dave Shield
7   Andreas Gross
8   James P. Williams
9   David Brandt
10   Stephen M. Moraco
11   John Whitmore
12   Edward M. Perry
13   Jack Applin
14   Robert Au
15   Ira Joseph Woodhead
16   Erle Schuyler
17   Yanick Champoux
18   Jeff Norden
19   Peter J. Jones

Seventeen qualified for the competition.

The winner was #4, Eero Pajarre's entry, which averaged only 9.7 rounds per deck in the qualifying trial and earned Eero $100 plus a two year extension on his TPJ subscription. His entry re-optimized for speed in the main contest, and won with an average time per deck of slightly more than three seconds. Developing with precompiled binaries of ActiveState Perl and GNU Emacs under Microsoft Windows 95, Pajarre optimized pickup order for most discards available next round with the help of a precompiled table of eight and twelve card situations. Then with the help of the Benchmark module, he realized that switching to string representations would save time

His canonic() subroutine converts decks represented as strings of card rank letters into patterns for the pre-solved table:

sub canonic ($) {
  my ($d)   = @_;
  my ($res) = "";
  my ($ind) = "";
  my ($c, $i);

  for ($i = 0; $i < length($d); $i++){
      $c="substr($d," $i, 1);
      if ( 0 > index($ind,$c) ) {
          $ind .= $c;
      }
      $res .= chr( ord('0') + index($ind, $c) );
      }

  return $res;
}

Steven W. McDougall's entry #3, a completely commentless six page program that nevertheless reads like a chess match, came in second, taking the $50 prize and a one-year extension to TPJ. By using arrays and array references for his internal representations, McDougall's program was able to generate workable long solutions faster than the next four finishers could generate short solutions. His Partition() subroutine uses bitmasks to determine if the lengths of arrays are divisible by four, to quickly choose good restack orders.

sub Partition {
   my ($stacks, $block4) = @_;
   my $s1 = scalar @{$Tab[$stacks->[1]]};
   my $s2 = scalar @{$Tab[$stacks->[2]]};
   my $s3 = scalar @{$Tab[$stacks->[3]]};
   $block4               & 3 or return @$stacks;
   ($block4 + $s1)       & 3 or return @$stacks[1, 0, 2, 3];
   ($block4 + $s2)       & 3 or return @$stacks[2, 0, 1, 3];
   ($block4 + $s1 + $s2) & 3 or return @$stacks[1, 2, 0, 3];
   ($block4 + $s3)       & 3 or return @$stacks[3, 0, 1, 2];
   ($block4 + $s1 + $s3) & 3 or return @$stacks[1, 3, 0, 2];
   ($block4 + $s2 + $s3) & 3 or return @$stacks[2, 3, 0, 1];
   ($block4 + $s1 + $s2 + $s3) 
                         & 3 or return @$stacks[1, 2, 3, 0];
}

The Wheels game is based on a game of solitaire known as "Perpetual Motion", and some years ago, McDougall wrote a C program to play Perpetual Motion with an animated display of the game state. His second place client was based on this earlier work.

After debugging the loop avoidance strategy, McDougall's program solves approximately eighteen decks per second on his 266 MHz Pentium II PC/NT.

With an algorithm that appeared to win every hand, McDougall focused on minimizing the socket I/O by tacking the instruction to receive the next deck onto the tail of the instruction to win the current deck. At that point his program could process about three hands per second in concert with the demonstration server program.

Jim Williams's third place Expert.pm, #8, was the only entry to have more than one file of program code, and was also the only entry to have full pod documentation. After he settled on an algorithm, Williams made extensive use of the Benchmark and Devel::DProf modules to guide his creation, inlining several pieces of code that had been separate subroutines. His %permutations hash, a hash of arrays of arrays (a HoLoL) to hold the dealt cards, allowed him to quickly construct the decks so as to choose one that would provide at least one discard the following round. Using "early pickups" to prevent missed discards, Williams's entry has the second lowest total number of moves in the contest, after Norden's #18.

Honorable Mentions: Dave Shield's #6 entry made extensive use of regular expressions. Mark Kvale's #5, the winner of the qualifying round, used a two-ply search with an external database of twelve-card situations. Kvale's Simulate() subroutine provides the same functionality as Williams's array-reference based @{$Permutations{$piles}} with this verbose slice:

# create the deck after pickup
@order = split //, $perm;
@deck = ( @{$pile[$order[0]]},
          @{$pile[$order[1]]},
          @{$pile[$order[2]]},
          @{$pile[$order[3]]} );

Jeff Norden's "RACE CAR" (#18), also using a full two-ply heuristic search, achieved the best round count of the contest. However, Norden spent more CPU time than any other entry:

foreach $order (@Pickups) {
  @deck = map ( @{$_,
          @stack[@$pickup_list{$order}]} );
  play_and_score();
}

Theo Van Dinter's #1 entry was the last of the entries that supplied multiple move commands at a time.

From Ira Joseph Woodhead (#15), we learned that $ENV{'USER'} is a more portable construction than 'whoami', and from Stephen M. Moraco (#10) we see the advantage of BEGIN { $Exporter::Verbose = 1; } as a package debugging technique.

Joseph A. DiVerdi (#2) used heuristic search to achieve the second-best move count, but his subroutine sent frequent instructions and therefore used a lot of time waiting for the server to respond.

One entry froze, and a spelling error prevented another entry from qualifying. The other seventeen entries successfully completed the qualifying round, earning their creators Perl Mongers T-shirts. The competition results:

> grep 'OK FI' competition_log
925611991 entry4 aai OK FI NI SH ED time 1527 round 6653 move 81951
925612196 entry3 aac OK FI NI SH ED time 1732 round 9345 move 108969
925612235 entry8 aaf OK FI NI SH ED time 1771 round 8376 move 72820
925612518 entry11 aab OK FI NI SH ED time 2054 round 8721 move 84869
925612702 entry6 aal OK FI NI SH ED time 2238 round 8105 move 96577
925612904 entry7 aaj OK FI NI SH ED time 2440 round 8861 move 101499
925612999 entry14 aak OK FI NI SH ED time 2535 round 10268 move 141576
925614674 entry19 aac OK FI NI SH ED time 4210 round 27908 move 312841
925614893 entry5 aap OK FI NI SH ED time 4429 round 7208 move 84750
925615025 entry12 aai OK FI NI SH ED time 4561 round 6725 move 79026
925615396 entry18 aaa OK FI NI SH ED time 4931 round 5566 move 64495
925615434 entry1 aah OK FI NI SH ED time 4970 round 5948 move 77961
925633669 entry15 aal OK FI NI SH ED time 23205 round 10871 move 91375
925634034 entry10 aab OK FI NI SH ED time 23570 round 9353 move 114787
925635423 entry2 aan OK FI NI SH ED time 24959 round 8932 move 78288
925641828 entry9 aan OK FI NI SH ED time 31364 round 6727 move 93842
925650130 entry13 aar OK FI NI SH ED time 39666 round 41316 move 317819

This is the final ps listing before Eero finished, having used the least CPU time of all the entries.

Sat May 1 21:25:00 CDT 1999
USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND
entry1 17264 6.6 1.8 2416 1804 p3 R 20:54 2:03 perl entry.pl
entry2 17253 0.6 2.0 2560 1936 pd S 20:54 0:12 perl entry.pl
entry3 17235 4.5 1.9 2464 1852 q1 R 20:53 1:26 perl entry.pl
entry4 17237 4.4 2.0 2600 1984 pf R 20:53 1:24 perl entry.pl
entry5 17247 6.4 2.4 2916 2320 pe R 20:53 2:02 perl entry.pl
entry6 17245 4.8 1.9 2500 1884 q0 R 20:53 1:32 perl entry.pl
entry7 17241 5.5 2.3 2824 2216 q3 R 20:53 1:45 perl entry.pl
entry8 17239 4.7 1.9 2480 1860 q2 R 20:53 1:30 perl entry.pl
entry9 17255 4.1 2.1 2632 2024 q4 R 20:54 1:16 perl entry.pl
entry10 17266 0.6 2.4 2968 2360 p4 S 0:54 0:12 perl entry.pl
entry11 17262 5.1 1.9 2424 1820 p5 R 20:54 1:35 perl entry.pl
entry12 17251 6.5 1.9 2488 1872 pa S 20:53 2:02 perl entry.pl
entry13 17259 0.1 1.9 2508 1892 p7 S 20:54 0:03 perl entry.pl
entry14 17257 5.3 2.0 2556 1944 pb R 20:54 1:40 perl entry.pl
entry15 17261 0.3 2.0 2528 1920 p6 R 20:54 0:06 perl entry.pl
entry18 17249 7.0 3.2 3684 3072 p9 R 20:53 2:13 perl entry.pl
entry19 17243 4.3 1.8 2404 1792 pc R 20:53 1:23 perl entry.pl

__END__


David Nicol (gratuitoushyphens@tipjar.com) no longer wonders what grading papers at the end of the semester must be like.


PREVIOUS  TABLE OF CONTENTS  NEXT