Sunday, November 8, 2009


The Making of the Sudoku Gadget | Google Wave

If you watched the Google I/O keynote, you may have seen Lars and Stephanie playing each other in a furious game of competitive Sudoku - and Lars failing miserably (well, he has to fail at something). I originally wrote this as a single-player game, but when I joined the Google Wave team, I realized it would make a perfect fit for a Wave-enabled Gadget.

[sudoku.bmp]

To make a game or app take advantage of the real-time Wave experience, you need to have a good understanding of how the gadget data is shared and propagated among all the Wave participants. Shared gadget data is stored as states, which are simply name/values pairs - like an associative array or hash map. So, when designing the Sudoku Gadget, I first had to decide what data needed to be stored, how to store it, and how to detect their changes.

When the Sudoku game first begins, we need states to remember the sequences of numbers that represents the puzzle and the solution. Since state data can only be of type string, I elected to store the JavaScript array of the puzzle and solution as JSON literals:

var PUZZLE = "[0,8,2,4,0,5,7,0,3,0,1,0,6,8,3,5,0,0,5,3,9,7,0,0,6,4,8,0,7,4,3,5,6,1,8,9,8,0,0,9,0,7,0,0,6,9,6,3,1,2,8,4,7,0,7,4,5,0,0,9,2,3,1,0,0,8,5,3,4,0,6,0,3,0,6,2,0,1,8,5,0]";
var SOLUTION = "[6,8,2,4,9,5,7,1,3,4,1,7,6,8,3,5,9,2,5,3,9,7,1,2,6,4,8,2,7,4,3,5,6,1,8,9,8,5,1,9,4,7,3,2,6,9,6,3,1,2,8,4,7,5,7,4,5,8,6,9,2,3,1,1,2,8,5,3,4,9,6,7,3,9,6,2,7,1,8,5,4]"

You can easily generate or parse the JSON literals of JavaScript arrays or objects with the library from json.org. Then use the method submitDelta() to submit this data as gadget states to the server:

var states = {};
var states["PUZZLE"] = puzzle;
var states["SOLUTION"] = solutoin;
wave.getState().submitDelta(states);

As soon as this call is made, each Wave participant that has registered a state update callback via setStateCallback() will receive a callback. In the callback, the gadget can use the value of the "PUZZLE" state to display the same Sudoku puzzle to all its participants, and uses the value of the "SOLUTION" state to determine if an input is correct.

To make the game competitive, I also needed to maintain state data to keep track of the correct answers for each participant. When a correct answer is verified, the gadget will submit a "cell_INDEX" state, where INDEX is the position of the board, with the value set as the username. So, if I answered the third square on the first row of the sudoku correctly, I would set this gadget state:

var states = {};
var states["cell_2"] = "Austin Chau"
wave.getState().submitDelta(states);

The setting of this state would again trigger the state callback method for each participant. The callback has logic to detect state updates that have names beginning with "cell_" (using this RegEx: /^cell_([0-9]+)$/ to do that) and then updates the UI to reflect a square being correctly filled.

A similar approach can be applied to other types of collaborative gadgets. In fact, I know there are plenty of variants to make collaborative sudoku. How about "Speed Sudoku" where all users get to play with the same puzzle and see who can finish first? Or maybe "Team Sudoku" where you can really work with other participants to complete the puzzle? Please check out the documentation for the Google Wave Gadgets API, we can't wait to see what you guys can come up with!