Sudoku quietly appeared on the scene in the United States in the last couple of years, and then became a phenomenon, just as it had in Japan, where it originated. It's a simple game, similar to crossword puzzles in that it uses a grid, but different because it uses numbers, and requires no external clues. The idea is that you have a grid with several numbers supplied, such as shown in Figure 1.
Figure 1. A sample Sudoku game
The idea is to fill the board so that every row, column, and 3 x 3 box (as delimited by the lines in this figure) has each of the digits from one to nine exactly once, as shown in Figure 2.
Figure 2. A solved Sudoku puzzle
There are many ways to accomplish this, most of which are beyond the scope of this article, but the basic idea is to use process of elimination to decide where each of the numbers goes. (See the Resources section for some good sources of information.) But as all of the ads will tell you, there is no math required.
In this article, we are going to build a basic grid that displays a game and enables you to input your answers using select lists. Using XPath, you can determine whether or not the puzzle has been solved. In part two, you will enhance the game with loading and saving abilities, and perhaps tweak the interface a little bit.
To start with, you are going to need some data. The way to do this is to create a "grid" within the data itself. In other words, one "row" of data -- the contents of the r(ow) element -- maps to one row of the game board. Each number lives in its own box, with nine of them making up a row. Nine rows make up the game. The resulting structure looks something like Figure 3.
Figure 3. The data structure
Notice that you have the rows and columns closely approximating what they would look like on the board. In this case, the zero entries correspond to empty boxes the user will have to fill in. Because of the way XForms works, when the user fills in a box on the form, that data gets added to this XML document. You can then check the structure of the document using carefully constructed XPath statements to determine whether or or not the puzzle has been solved.
Let's start by creating a basic page.
The basic page needs to have several elements. These include the structure of the actual page, including namespaces and so on, as well as the XForms model (see Listing 1).
Listing 1. The basic page
<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:s="http://www.example.com/sudoku"
xmlns:b="http://www.example.com/board">
<head>
<title>Sudoku</title>
<xforms:model>
<xforms:instance id="content">
<s:game>
<s:row><s:box>6</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>9</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>1</s:box></s:row>
<s:row><s:box>0</s:box><s:box>8</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>7</s:box><s:box>2</s:box>
<s:box>5</s:box></s:row>
<s:row><s:box>0</s:box><s:box>7</s:box>
<s:box>0</s:box><s:box>3</s:box><s:box>8</s:box>
<s:box>0</s:box><s:box>9</s:box><s:box>0</s:box>
<s:box>0</s:box></s:row>
<s:row><s:box>4</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>6</s:box>
<s:box>1</s:box><s:box>8</s:box><s:box>0</s:box>
<s:box>0</s:box></s:row>
<s:row><s:box>9</s:box><s:box>6</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>5</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>1</s:box>
<s:box>2</s:box></s:row>
<s:row><s:box>0</s:box><s:box>0</s:box>
<s:box>8</s:box><s:box>7</s:box><s:box>2</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>3</s:box></s:row>
<s:row><s:box>0</s:box><s:box>0</s:box>
<s:box>6</s:box><s:box>0</s:box><s:box>3</s:box>
<s:box>2</s:box><s:box>0</s:box><s:box>7</s:box>
<s:box>0</s:box></s:row>
<s:row><s:box>1</s:box><s:box>3</s:box>
<s:box>2</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>6</s:box>
<s:box>0</s:box></s:row>
<s:row><s:box>7</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>6</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>4</s:box></s:row>
</s:game>
</xforms:instance>
<xforms:submission id="submitgame" action="" method="post"/>
</xforms:model>
</head>
<body>
<img src="images/showlayout.gif" style="float:left;height: 64px; width: 64px;" />
<h1 align="center">Sudoku</h1>
<br clear="left" />
<div>
</div>
</body>
</html>
|
Several namespaces that you'll be using later just to make things a little simpler have been left in. Also notice that the data has been created in its own instance, changing the XML structure to be a little more human readable. (The names are, of course, completely arbitrary.) A small graphic has also been included to remind the user of what they're trying to accomplish. What you're left with is a simple page (see Figure 4).
Figure 4. The empty page
Now let's look at adding some controls to that page.
In order to create the game board, you will need nine rows of drop-down lists, each of which contains the numbers one through nine and a blank space representing zero. Fortunately, this is not difficult to achieve. The easiest way is to create a second instance that provides a template (see Listing 2).
Listing 2. Using the template
...
</s:game>
</xforms:instance>
<xforms:instance id="templates">
<b:template>
<b:entry><b:sendvalue>0</b:sendvalue><b:display></b:display>
</b:entry>
<b:entry><b:sendvalue>1</b:sendvalue><b:display>1</b:display>
</b:entry>
<b:entry><b:sendvalue>2</b:sendvalue><b:display>2</b:display>
</b:entry>
<b:entry><b:sendvalue>3</b:sendvalue><b:display>3</b:display>
</b:entry>
<b:entry><b:sendvalue>4</b:sendvalue><b:display>4</b:display>
</b:entry>
<b:entry><b:sendvalue>5</b:sendvalue><b:display>5</b:display>
</b:entry>
<b:entry><b:sendvalue>6</b:sendvalue><b:display>6</b:display>
</b:entry>
<b:entry><b:sendvalue>7</b:sendvalue><b:display>7</b:display>
</b:entry>
<b:entry><b:sendvalue>8</b:sendvalue><b:display>8</b:display>
</b:entry>
<b:entry><b:sendvalue>9</b:sendvalue><b:display>9</b:display>
</b:entry>
</b:template>
</xforms:instance>
<xforms:submission id="submitgame" action="" method="post"/>
</xforms:model>
<style type="text/css">
div > * {display: inline;}
</style>
</head>
<body>
<img src="images/showlayout.gif" style="float:left;height: 64px; width: 64px;" />
<h1 align="center">Sudoku</h1>
<br clear="left" />
<div>
<xforms:repeat id="gamerow" nodeset="instance('content')/s:row">
<xforms:repeat id="gamebox" nodeset="s:box">
<xforms:select1>
<xforms:itemset nodeset="instance('templates')/b:entry">
<xforms:label ref="b:display"/>
<xforms:value ref="b:sendvalue"/>
</xforms:itemset>
</xforms:select1>
</xforms:repeat>
</xforms:repeat>
</div>
</body>
</html>
|
Despite its visual appearance, the template actually represents a single row, with each "box" having a value to be inserted into the main game instance and a separate display value. For each row in the game, you loop through each box in the row. For each box in a row, you create a select list by displaying each of the entries in the template. By manipulating cascading style sheets just a little bit (setting the contents of the div to be displayed inline, rather than each to its own line) you can get the basic game board (see Figure 5).
Figure 5. The basic game board
Now you need to set each of these lists to start with the supplied value, if there is one.
Because of the way these lists have been structured, setting an initial value is pretty straightforward (see Listing 3).
Listing 3. Setting the initial values
...
<xforms:repeat id="gamebox" nodeset="s:box">
<xforms:select1 ref=".">
<xforms:itemset nodeset="instance('templates')/b:entry">
<xforms:label ref="b:display"/>
<xforms:value ref="b:sendvalue"/>
</xforms:itemset>
</xforms:select1>
</xforms:repeat>
...
|
The form displays a select list for each box element, so all you need to do is have the list reference the current box element. You can see the results in Figure 6.
Figure 6. Setting initial values
Notice, however, that there's nothing to stop you from simply changing the original values if they are inconvenient. This, of course, defeats the whole purpose of the game.
Making the original numbers read only
In order to stop the user from simply changing the original values, you will make the supplied values "read-only." Now, it may seem that the easiest way to do that would be to simply tell the form not to let the user change any value that is not "0," but remember that once the user starts playing the game, he or she will be changing those zeroes into other values, and it's not a good idea for you to stop them from changing values that they themselves have selected.
To solve this problem, we will need to add a little bit of data to the instance (see Listing 4).
Listing 4. Adding information to the instance
...
<s:game>
<s:row><s:box ro="yes">6</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box ro="yes">9</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box ro="yes">1</s:box></s:row>
<s:row><s:box>0</s:box><s:box ro="yes">8</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box ro="yes">7</s:box><s:box
ro="yes">2</s:box>
<s:box ro="yes">5</s:box></s:row>
...
</s:game>
</xforms:instance>
<xforms:bind nodeset="//s:row//s:box[@ro='yes']" readonly="true()" />
<xforms:instance id="templates">
...
<style type="text/css">
div > * {display: inline;}
*:read-only { color: red }
</style>
...
|
Adding attributes to your original values gives you the opportunity to use a binding condition to specify that these values should be read only. In other words, the user will not be able to change the value of these select lists.
In an ideal world, you would be able to use "pseudo classes" to set a style for these values so users can easily see which numbers they themselves have chosen and which were originally supplied. I've added that code into the page, but unfortunately, it is not yet supported by Firefox.
If you reload the page, you will see that while you can change the blank values, you cannot change the original values.
All right, now that you've got the board, how do you know how you're doing? You can start the scoring with the easiest topic, finding rows that have been solved.
In order to determine whether a row has been solved, you simply need to determine whether it contains an instance of all nine digits. Because there are only nine spots, this will also ensure that there are no duplicates. You can do this with a simple XPath statement.
To start with, if you wanted to find at how many rows were in the game, you could use the XPath statement: count(/s:game/s:row).
This should give you the value of 9. However, you don't want all of the rows, you only want all of the rows that satisfy certain condition. For example, you can select only the rows that include a box with a value of 1: count(/s:game/s:row[s:box = '1']).
Of those rows, you only want the ones that also have a box with a value of 2: count(/s:game/s:row[s:box = '1'][s:box = '2']).
And so on for each of the nine digits. You can put it all together to check for all nine values to let you know where you stand (see Listing 5).
Listing 5. Checking for correct rows
...
<s:row><s:box ro="yes">7</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box ro="yes">6</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box ro="yes">4</s:box></s:row>
<s:correctRows>no</s:correctRows>
</s:game>
</xforms:instance>
<xforms:bind nodeset="//s:row//s:box[@ro='yes']" readonly="true()" />
<xforms:bind nodeset="//s:correctRows" calculate=
"count(/s:game/s:row[s:box = '1'][s:box = '2'][s:box = '3']
[s:box = '4'][s:box = '5'][s:box = '6'][s:box = '7'][s:box = '8']
[s:box = '9'])" />
<xforms:instance id="templates">
...
</xforms:repeat>
</div>
Correct Rows: <xforms:output ref="//s:correctRows" /><br />
</body>
</html>
|
By adding an element to the instance, you can easily see where you stand, because every time the value gets updated in the instance, it will be updated on the page. You can see this in Figure 7.
Figure 7. Looking for correct rows
The second row has exactly one instance of each digit, so you can see the value of 1 for correctRows.
Creating an XPath expression to check for correct columns is a little more complicated. Rather than looking for a single row that satisfies multiple conditions, where looking for a single column that includes a row with each digit. For example, you can count the root element only if it includes a row that includes the digit 1 in the first column: count(/s:game[s:row[s:box[1]='1']])
In other words, it is counting the game element if and only if it has a row that satisfies the condition of having a box element in position 1 with a value of "1." It doesn't matter which row it is, as long as the 1 is in that first position. Similarly, you can count the game element only if it also has the numeral 2 in the first position: count(/s:game[s:row[s:box[1]='1']][s:row[s:box[1]='2']])
And so on and so forth. Unfortunately, this statement only checks for the correctness of column 1, which means you're going to need a new count() function for each column, but that's not too bad (see Listing 6).
Listing 6. Looking for correct columns
...
<xforms:bind nodeset="//s:correctRows" calculate=
"count(/s:game/s:row[s:box = '1'][s:box = '2'][s:box = '3']
[s:box = '4'][s:box = '5'][s:box = '6'][s:box = '7'][s:box = '8']
[s:box = '9']) +
count(/s:game[s:row[s:box[1]='1']][s:row[s:box[1]='2']]
[s:row[s:box[1]='3']][s:row[s:box[1]='4']][s:row[s:box[1]='5']]
s:row[s:box[1]='6']][s:row[s:box[1]='7']][s:row[s:box[1]='8']]
[s:row[s:box[1]='9']]) +
count(/s:game[s:row[s:box[2]='1']][s:row[s:box[2]='2']]
[s:row[s:box[2]='3']][s:row[s:box[2]='4']][s:row[s:box[2]='5']]
[s:row[s:box[2]='6']][s:row[s:box[2]='7']][s:row[s:box[2]='8']]
[s:row[s:box[2]='9']]) +
count(/s:game[s:row[s:box[3]='1']][s:row[s:box[3]='2']]
[s:row[s:box[3]='3']][s:row[s:box[3]='4']][s:row[s:box[3]='5']]
[s:row[s:box[3]='6']][s:row[s:box[3]='7']][s:row[s:box[3]='8']]
[s:row[s:box[3]='9']]) +
count(/s:game[s:row[s:box[4]='1']][s:row[s:box[4]='2']]
[s:row[s:box[4]='3']][s:row[s:box[4]='4']][s:row[s:box[4]='5']]
[s:row[s:box[4]='6']][s:row[s:box[4]='7']][s:row[s:box[4]='8']]
[s:row[s:box[4]='9']]) +
count(/s:game[s:row[s:box[5]='1']][s:row[s:box[5]='2']]
[s:row[s:box[5]='3']][s:row[s:box[5]='4']][s:row[s:box[5]='5']]
[s:row[s:box[5]='6']][s:row[s:box[5]='7']][s:row[s:box[5]='8']]
[s:row[s:box[5]='9']]) +
count(/s:game[s:row[s:box[6]='1']][s:row[s:box[6]='2']]
[s:row[s:box[6]='3']][s:row[s:box[6]='4']][s:row[s:box[6]='5']]
[s:row[s:box[6]='6']][s:row[s:box[6]='7']][s:row[s:box[6]='8']]
[s:row[s:box[6]='9']]) +
count(/s:game[s:row[s:box[7]='1']][s:row[s:box[7]='2']]
[s:row[s:box[7]='3']][s:row[s:box[7]='4']][s:row[s:box[7]='5']]
[s:row[s:box[7]='6']][s:row[s:box[7]='7']][s:row[s:box[7]='8']]
[s:row[s:box[7]='9']]) +
count(/s:game[s:row[s:box[8]='1']][s:row[s:box[8]='2']]
[s:row[s:box[8]='3']][s:row[s:box[8]='4']][s:row[s:box[8]='5']]
[s:row[s:box[8]='6']][s:row[s:box[8]='7']][s:row[s:box[8]='8']]
[s:row[s:box[8]='9']]) +
count(/s:game[s:row[s:box[9]='1']][s:row[s:box[9]='2']]
[s:row[s:box[9]='3']][s:row[s:box[9]='4']][s:row[s:box[9]='5']]
[s:row[s:box[9]='6']][s:row[s:box[9]='7']][s:row[s:box[9]='8']]
[s:row[s:box[9]='9']])" />
<xforms:instance id="templates">
...
|
Now if you refresh the page, you'll see that the "correct rows" value updates when you complete a column, such as the third column in Figure 8.
Figure 8. Correcting a row
At this point, when you have solved all of the columns and rows, you should see a "correct rows" value of 18 (nine rows and nine columns). But you're not done yet.
That wasn't too bad, but you do have one more step. Remember, in addition to the rows and columns, the user can only use the digits once in each of the 3 x 3 boxes shown in Figure 1. Unfortunately, there is no simple XPath statement that will enable you to easily determine whether or not all of the digits are present, because any statement you construct will have to specify specific boxes and specific digits. Fortunately,there is an option.
What you can do is map each square to a row of data that you create explicitly for this purpose. For example, consider the XML element shown in Listing 7.
Listing 7. Box element
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box></s:square>
|
With nine box elements, it pretty much corresponds to a row. Try mapping the boxes with the following coordinates (see Listing 8).
Listing 8. Coordinates for boxes
<s:square><s:box>[1, 1]</s:box><s:box>
[1, 2]</s:box><s:box>[1, 3]</s:box>
<s:box>[2, 1]</s:box><s:box>
[2, 2]</s:box><s:box>[2, 3]</s:box>
<s:box>[3, 1]</s:box><s:box>
[3, 2]</s:box><s:box>[3, 3]</s:box></s:square>
|
You can see that the data fits, and you can use a simple XPath statement to determine how many squares are correct (see Listing 9).
Listing 9. XPath statement to determine how many squares are correct
count(/s:game/s:square[s:box = '1'][s:box = '2']
[s:box = '3'][s:box = '4'][s:box = '5'][s:box = '6']
[s:box = '7'][s:box = '8'][s:box = '9'])
|
Note that this is almost identical to the statement used to determine the original rows. The next thing you'll need to do is add all of this information to the existing game (see Listing 10).
Listing 10. Adding the mapping instance to the game
...
<s:row><s:box ro="yes">7</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box ro="yes">6</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box ro="yes">4</s:box></s:row>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:correctRows>no</s:correctRows>
</s:game>
</xforms:instance>
<xforms:bind nodeset="//s:row//s:box[@ro='yes']" readonly="true()" />
<xforms:bind nodeset="//s:correctRows" calculate=
"count(/s:game/s:row[s:box = '1'][s:box = '2'][s:box = '3']
[s:box = '4'][s:box = '5'][s:box = '6'][s:box = '7'][s:box = '8']
[s:box = '9']) +
count(/s:game[s:row[s:box[1]='1']][s:row[s:box[1]='2']]
[s:row[s:box[1]='3']][s:row[s:box[1]='4']][s:row[s:box[1]='5']]
[s:row[s:box[1]='6']][s:row[s:box[1]='7']][s:row[s:box[1]='8']]
[s:row[s:box[1]='9']]) +
...
[s:row[s:box[9]='9']]) +
count(/s:game/s:square[s:box = '1'][s:box = '2']
[s:box = '3'][s:box = '4'][s:box = '5'][s:box = '6']
[s:box = '7'][s:box = '8'][s:box = '9'])" />
<xforms:instance id="templates">
...
|
Of course, just adding the instance doesn't really do anything, which you can prove to yourself by completing a square and noticing that the "correct rows" value does not change. To make it work, you will have to complete the mapping.
Various ways to set one element equal to the value of another element are built into XForms, but in this case, you are going to take direct control of the process. You are going to create a button that explicitly sets individual values when the user clicks it (see Listing 11).
Listing 11. Setting values
...
Correct Rows: <xforms:output ref="//s:correctRows" /><br />
<xforms:trigger style="display:block">
<xforms:label>Check it!</xforms:label>
<xforms:action ev:event="DOMActivate">
<xforms:setvalue ref="/s:game/s:square[1]/s:box[1]"
value="/s:game/s:row[1]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[2]"
value="/s:game/s:row[1]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[3]"
value="/s:game/s:row[1]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[4]"
value="/s:game/s:row[2]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[5]"
value="/s:game/s:row[2]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[6]"
value="/s:game/s:row[2]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[7]"
value="/s:game/s:row[3]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[8]"
value="/s:game/s:row[3]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[9]"
value="/s:game/s:row[3]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[1]"
value="/s:game/s:row[1]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[2]"
value="/s:game/s:row[1]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[3]"
value="/s:game/s:row[1]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[4]"
value="/s:game/s:row[2]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[5]"
value="/s:game/s:row[2]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[6]"
value="/s:game/s:row[2]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[7]"
value="/s:game/s:row[3]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[8]"
value="/s:game/s:row[3]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[2]/s:box[9]"
value="/s:game/s:row[3]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[1]"
value="/s:game/s:row[1]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[2]"
value="/s:game/s:row[1]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[3]"
value="/s:game/s:row[1]/s:box[9]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[4]"
value="/s:game/s:row[2]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[5]"
value="/s:game/s:row[2]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[6]"
value="/s:game/s:row[2]/s:box[9]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[7]"
value="/s:game/s:row[3]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[8]"
value="/s:game/s:row[3]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[3]/s:box[9]"
value="/s:game/s:row[3]/s:box[9]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[1]"
value="/s:game/s:row[4]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[2]"
value="/s:game/s:row[4]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[3]"
value="/s:game/s:row[4]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[4]"
value="/s:game/s:row[5]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[5]"
value="/s:game/s:row[5]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[6]"
value="/s:game/s:row[5]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[7]"
value="/s:game/s:row[6]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[8]"
value="/s:game/s:row[6]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[4]/s:box[9]"
value="/s:game/s:row[6]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[1]"
value="/s:game/s:row[4]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[2]"
value="/s:game/s:row[4]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[3]"
value="/s:game/s:row[4]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[4]"
value="/s:game/s:row[5]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[5]"
value="/s:game/s:row[5]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[6]"
value="/s:game/s:row[5]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[7]"
value="/s:game/s:row[6]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[8]"
value="/s:game/s:row[6]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[5]/s:box[9]"
value="/s:game/s:row[6]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[1]"
value="/s:game/s:row[4]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[2]"
value="/s:game/s:row[4]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[3]"
value="/s:game/s:row[4]/s:box[9]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[4]"
value="/s:game/s:row[5]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[5]"
value="/s:game/s:row[5]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[6]"
value="/s:game/s:row[5]/s:box[9]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[7]"
value="/s:game/s:row[6]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[8]"
value="/s:game/s:row[6]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[6]/s:box[9]"
value="/s:game/s:row[6]/s:box[9]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[1]"
value="/s:game/s:row[7]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[2]"
value="/s:game/s:row[7]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[3]"
value="/s:game/s:row[7]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[4]"
value="/s:game/s:row[8]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[5]"
value="/s:game/s:row[8]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[6]"
value="/s:game/s:row[8]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[7]"
value="/s:game/s:row[9]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[8]"
value="/s:game/s:row[9]/s:box[2]" />
<xforms:setvalue ref="/s:game/s:square[7]/s:box[9]"
value="/s:game/s:row[9]/s:box[3]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[1]"
value="/s:game/s:row[7]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[2]"
value="/s:game/s:row[7]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[3]"
value="/s:game/s:row[7]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[4]"
value="/s:game/s:row[8]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[5]"
value="/s:game/s:row[8]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[6]"
value="/s:game/s:row[8]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[7]"
value="/s:game/s:row[9]/s:box[4]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[8]"
value="/s:game/s:row[9]/s:box[5]" />
<xforms:setvalue ref="/s:game/s:square[8]/s:box[9]"
value="/s:game/s:row[9]/s:box[6]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[1]"
value="/s:game/s:row[7]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[2]"
value="/s:game/s:row[7]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[3]"
value="/s:game/s:row[7]/s:box[9]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[4]"
value="/s:game/s:row[8]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[5]"
value="/s:game/s:row[8]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[6]"
value="/s:game/s:row[8]/s:box[9]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[7]"
value="/s:game/s:row[9]/s:box[7]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[8]"
value="/s:game/s:row[9]/s:box[8]" />
<xforms:setvalue ref="/s:game/s:square[9]/s:box[9]"
value="/s:game/s:row[9]/s:box[9]" />
</xforms:action>
</xforms:trigger>
</body>
</html>
|
Now when the user clicks the Check it! button, all of these values will be set, which will trigger the "correct rows" calculation to be updated. You can see this when you reload the page and once more complete a square. The "correct rows" value will not change until you click the button, as seen in Figures 9 (before) and 10 (after).
Figure 9. Before clicking the button
Figure 10 shows the "correct rows" value after clicking the button.
Figure 10. After clicking the button
So now, with the addition of nine squares to be completed, the number of "correct rows" before the game is complete must be 27.
Finally, you need to add the submission button. Because you want this button to appear only one when the game is ready to submit, you need to set its relevance (see Listing 12).
Listing 12. Setting the submit button's relevance
...
<xforms:instance id="content">
<s:game>
<!-- Test set of correct data -->
<s:row><s:box>1</s:box><s:box>4</s:box><s:box>7</s:box>
<s:box>2</s:box><s:box>5</s:box><s:box>8</s:box><s:box>3
</s:box>
<s:box>6</s:box><s:box>9</s:box></s:row>
<s:row><s:box>2</s:box><s:box>5</s:box><s:box>8</s:box>
<s:box>3</s:box><s:box>6</s:box><s:box>9</s:box><s:box>1
</s:box>
<s:box>4</s:box><s:box>7</s:box></s:row>
<s:row><s:box>3</s:box><s:box>6</s:box><s:box>9</s:box>
<s:box>1</s:box><s:box>4</s:box><s:box>7</s:box><s:box>2
</s:box>
<s:box>5</s:box><s:box>8</s:box></s:row>
<s:row><s:box>4</s:box><s:box>7</s:box><s:box>1</s:box>
<s:box>5</s:box><s:box>8</s:box><s:box>2</s:box><s:box>6
</s:box>
<s:box>9</s:box><s:box>3</s:box></s:row>
<s:row><s:box>5</s:box><s:box>8</s:box><s:box>2</s:box>
<s:box>6</s:box><s:box>9</s:box><s:box>3</s:box><s:box>4
</s:box>
<s:box>7</s:box><s:box>1</s:box></s:row>
<s:row><s:box>6</s:box><s:box>9</s:box><s:box>3</s:box>
<s:box>4</s:box><s:box>7</s:box><s:box>1</s:box><s:box>5
</s:box>
<s:box>8</s:box><s:box>2</s:box></s:row>
<s:row><s:box>7</s:box><s:box>1</s:box><s:box>4</s:box>
<s:box>8</s:box><s:box>2</s:box><s:box>5</s:box><s:box>9
</s:box>
<s:box>3</s:box><s:box>6</s:box></s:row>
<s:row><s:box>8</s:box><s:box>2</s:box><s:box>5</s:box>
<s:box>9</s:box><s:box>3</s:box><s:box>6</s:box><s:box>7
</s:box>
<s:box>1</s:box><s:box>4</s:box></s:row>
<s:row><s:box>9</s:box><s:box>3</s:box><s:box>6</s:box>
<s:box>7</s:box><s:box>1</s:box><s:box>4</s:box><s:box>8
</s:box>
<s:box>2</s:box><s:box>5</s:box></s:row>
<!-- <s:row><s:box ro="yes">6</s:box><s:box>0</s:box>
...
<s:box ro="yes">4</s:box></s:row> -->
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
...
<s:box>0</s:box><s:box>0</s:box></s:square>
<s:submitButtonElement>Submit</s:submitButtonElement>
<s:correctRows>no</s:correctRows>
</s:game>
</xforms:instance>
<xforms:bind nodeset="//s:row//s:box[@ro='yes']" readonly="true()" />
<xforms:bind nodeset="//s:submitButtonElement"
relevant="/s:game/s:correctRows = 27" />
<xforms:bind nodeset="//s:correctRows" calculate=
"count(/s:game/s:row[s:box = '1'][s:box = '2'][s:box = '3']
[s:box = '4'][s:box = '5'][s:box = '6'][s:box = '7'][s:box = '8']
[s:box = '9']) +
...
</xforms:action>
</xforms:trigger>
<xforms:submit ref="/s:game/s:submitButtonElement" submission="submitgame">
<xforms:label>Submit</xforms:label>
</xforms:submit>
</body>
</html>
|
Starting at the bottom, the submit button has been added and a new element referenced, the submitButtonElement element. A binding statement has also been added so that element does not appear unless the correctRows element is equal to 27. To test this, a "correct" data set has been temporarily added. When you first load the page, you see that all of the rows and columns are complete (see Figure 11).
Figure 11. Loading the correct data
Once you click the Check it! button, however, the correctRows value gets updated to 27, and the submit button appears, as you can see in Figure 12.
Figure 12. The submit button appears
At the moment, you don't have anywhere to submit the data to, but that will change in part 2.
Because Sudoku is based on very specific data patterns, you can use XPath expressions to evaluate the progress the user has made in a specific game. You can also use these patterns to easily generate a game board that enables the user to play. In Part 1, you created the form and the mechanism of play; in Part 2, you will look at the data end, loading and saving games and performing other enhancements.
| Description | Name | Size | Download method |
|---|---|---|---|
| Part 1 sample code | sudoku1_source.zip | 3KB | HTTP |
Information about download methods
Learn
-
Sudoku.com offers tips for beginners just learning how to play the game, while Michael Mepham, who provides puzzles for the Daily Telegraph and the Los Angeles Times http://www.sudoku.org.uk provides analysis for solving more challenging puzzles. If you just want to get your feet wet and solve a few more while you're waiting for Part 2, WebSudoku has puzzles of various difficulties you can play online.
-
Get a basic introduction to XForms in Introduction to XForms, Part 1: The new Web standard for forms (developerWorks, September 2006).
-
Learn more about XForms in the IBM developerWorks XML zone.
-
Learn more about XForms submission events in XForms tip: Using form submission events (developerWorks, November 2006).
-
Find out how to accept XForms data in Java (developerWorks, October 2006), Perl (developerWorks, October 2006), and PHP (developerWorks, October 2006).
-
IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
-
XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- Visit XForms.org, a clearinghouse of XForms-related information.
-
developerWorks technical events and webcasts: Stay current with technology in these sessions.
-
Learn all about XML at the developerWorks XML zone.
-
Hear the author discuss his obsession with Sudoku in this developerWorks podcast.
Get products and technologies
-
Python Sudoku both generates and solves Sudoku puzzles. We'll be using this engine in Part 2 to generate new puzzles.
-
The XForms Recommendation is maintained by the W3C.
-
For more information on XPath, check out the XPath Recommendation.
-
Get MozzIE, an open-source control that allows you to render XForms in Internet Explorer.
Discuss
- Participate in the discussion forum.
- developerWorks
blogs: Get involved in the developerWorks community.
Nicholas Chase has been involved in Web site development for companies such as Lucent Technologies, Sun Microsystems, Oracle, and the Tampa Bay Buccaneers. Nick has been a high school physics teacher, a low-level radioactive waste facility manager, an online science fiction magazine editor, a multimedia engineer, an Oracle instructor, and the Chief Technology Officer of an interactive communications company. He is the author of several books, including XML Primer Plus (Sams).



