As multicore CPUs and distributed applications become the norm rather than the exception, more and more programmers are discovering that concurrent programming is a lot harder than sequential programming---so much so, in fact, that traditional "hack and patch" approaches simply don't work. Race conditions and deadlocks are hellish hard to reproduce under controlled circumstances; if you don't design and analyze concurrent systems before putting them together, the odds are long that they'll never really work.
I therefore wasn't surprised that several of the contributors to Beautiful Code chose concurrency as their topic. While beauty is desirable elsewhere, I think it's essential in the brave new parallel world the industry is rushing toward. I'd therefore like to set a challenge to readers of this blog: we'll send a free copy of the book to the 20 people who submit the most elegant, correct, solution to the problem described below by this Halloween (October 31, 2007). Answers don't have to consist of running programs---sufficiently detailed pseudocode is fine---but you must explain (a) why you believe your solution works, and (b) what makes it beautiful. You can submit your entry at the Beautiful Code wiki site; the rules of the game are given there, and below.
Speed Scrabble is a game for two or more players that takes only a few minutes to play. The rules are simple:
- Spread the tiles face down on the table. Each player chooses 15 tiles at random, and puts them in front of herself; the rest are set aside.
- Someone says "Go!" Every player turns over 7 of her tiles and starts trying to make words with them. Words must crisscross in the standard Scrabble way, and the players' "boards" are independent---each player only uses her own characters.
- As soon as any player has made a board using all 7 of her characters, she shouts, "Go!" Every player turns over another character, so that all players now have 8 characters to use. Players may rearrange their pre-existing words when and as they please---they do not have to stick to their earlier choices.
- When any player has used all 8 of her characters, she shouts, "Go!", and everyone turns over their ninth tile. This repeats until everyone has turned over all 15 tiles. The winner is whoever makes a legal 15-character board first.
The challenge is to design the architecture of a "referee" program
for a computer Speed Scrabble tournament. Contestants will submit
classes that extend an abstract base class called
IPlayScrabble that the contest organizers will provide.
These classes will be loaded by a SpeedScrabble program,
which will create an instance of each one and pass it the dictionary
of words that can be used in the game, and its first seven tiles.
Each player will run in its own thread, in parallel with its competitors. As soon as it has made a board with the tiles it has, it will request another tile from the "referee". The first player to complete a 15-tile board wins.
This is trickier than it first seems because players have to be able handle "interrupts", i.e., notice when the referee is trying to give them another tile that they didn't ask for because some other player has taken the lead. (It's often the case that a set of $N$ tiles has no solution, but an $N+1$-tile extension does, so not taking tiles as soon as they're offered is a good way to come in last.) At the same time, when the central referee is handing out tiles, it must never wait for a player to accept one before moving on to the next player, because if it takes player P two seconds to say, "Yes, thank you, carry on," that's two seconds that player P+1 has lost.
So: how will the central referee hand out characters? What methods will you give players, and which ones will they be trusted to implement themselves? Most importantly, how will you convince sceptics that your synchronization and communication scheme can't deadlock, and won't ever starve one player because of something another player has done?


Comments (13)
I cant seem to find enough people that appreciate beautiful code... people than can hear the code softly click as all the pieces come together to fit into a nice, small, smooth sphere. Elegance instantiated. Sigh...
Inspiring to see there's a book by people who appreciate beauty. And nice site too.
Wishing you many readers and a million pageviews,
ES.
Posted by EverShine | August 9, 2007 11:49 AM
so an entry needs to implement both the referee and the player classes? where can we get the IPlayScrabble interface?
Posted by anonymous | August 9, 2007 1:40 PM
The game description is incomplete. What happens if no one can shout "Go!"?
I suggest we add another rule: a player can shout "Pass!" If all players have shouted "Pass!", they all get to turn over a new tile.
In a game where the last round is all "Pass!", the winner is the last person who shouted "Go!". If no one ever shouted "Go!", there is no winner.
In addition to giving the players the dictionary and initial tiles, a callback interface must also be passed to them to shout "Go!" or "Pass!"
Posted by Moh | August 9, 2007 2:33 PM
Concurrency may be difficult as it's commonly practiced, but there's no particular reason it has to be. Most languages in wide use today simply weren't designed with concurrency in mind (nor were most of their libraries), and it shows in the pain of implementation. The threading libraries most commonly used also make use of poorly-conceived models.
Try concurrent programming in a language with a sensible concurrency model built in: CSP. Personally, Limbo's my favorite, but Erlang gets most of the press (and good reports from people I trust about such things). These languages make concurrent design fun, elegant, and frequently just the simplest way to solve the problem.
A nice introduction to Bell Labs' CSP history is available at http://swtch.com/~rsc/thread/ and the best implementation paper I've read on the topic is Doug McIlroy's "Squinting at Power Series", available at http://swtch.com/~rsc/thread/squint.pdf .
Posted by Anthony Sorace | August 9, 2007 2:57 PM
The contest sounds like a fantastic idea. One minor nit: it would be really nice to force working code. Its easier to get by with pseudo-code that looks pretty straight forward but when you try to implement it, the resulting code looks like crap. Any language would be fine, the goal would be to write beautiful, *working* code that solves the given problem.
Posted by Rushabh | August 9, 2007 7:42 PM
From your wording, it seems that Object-orientation is implied, as you talk of "classes" and "instances". What about languages that don't use/have OO features? Why should OO be implied here, when you don't specify any language?
Posted by Anonymous | August 10, 2007 4:15 PM
Can we write it in Erlang?
Posted by joe | August 10, 2007 5:01 PM
Just add an Erlangish module to Ruby with these three functions:
pid=spawn(function_definition)
msg=listen(/regexp/)
send(pid,msg)
Keep timestamps for each round on when client messages are recieved. Spawn a new checker process for each client board request that isn't stale, and send a kill message to any checker messages the has process outstanding.
Then you have to process incoming checker messages against the timestamp table. Continue until the oldest timestamp contains a valid result, in which case you can broadcast a "go" message, except in the last round broadcasting "client x" won.
I would say 2 hours for a multicore Ruby erlangish module, and 30 min for the code.
Probably 2 more hours if you want the erlangish module to process and send requests over http.
Posted by Chad Brewbaker | August 10, 2007 6:07 PM
Is it just me or would this be trivial in erlang?
Posted by Adam Wendt | August 10, 2007 7:36 PM
Anthony Sorace is spot on, above. Read the Bell Labs' CSP history link he gives. All this mutex and semaphores stuff is the wrong solution most of the time and overly complex. Limbo is a joy to do this kind of thing with. I've seen libraries adding this kind of function to Python too.
Posted by Ralph Corderoy | August 11, 2007 8:12 AM
Having read the blog post and the wiki twice now, with great care, I cannot for the life of me understand the assignment. We are to implement the "referee"? The game-playing algorithm? Both? Where is the IPlayScrabble interface?
Posted by Jonathan Feinberg | August 11, 2007 9:05 PM
The problem with this game is there is no incentive for players to try to win any round except the very last round. You should make the game so that the goal is to win as many rounds as possible.
Posted by Anonymous | August 13, 2007 1:16 AM
Hi everyone,
Sorry for the long lag in responding --- to answer questions in the order they arrived:
the central referee and the players. That means defining the
IPlayScrabble interface (or your favorite language's equivalent).
pattern with the letters they have. There are two possible
solutions:
a certain number of seconds whether someone's shouted "Go!" or
not.
has, it shouts "Pass!". Once everyone has shouted "Pass!", the
referee gives everyone a new letter.
The second is problematic: players could lie (i.e., shout "Pass!"
even though they haven't exhausted their possibilities, then keep
working), and a poorly-implemented player might take a *long* time
to realize it can't make a pattern.
learn enough Erlang, Concurrent Prolog, and what-not to check all
the solutions ;-)
hard requirement is that the referee (that's me) has to be able to
understand it.
lot easier to extend a solution than to create one from scratch. If
you have a 14-tile pattern, you'll probably be able to create your
final 15-tile pattern faster than someone who's just been shouting
"Pass!" the whole time.
Posted by Greg Wilson | August 16, 2007 2:22 PM