Building Apps with WMLScript by Kevin Sharp A simple game of magic squares illustrates common WAP design issues, including the interaction between a WML decks and WMLScript functions. In previous articles on WMLScript, I have concentrated on individual elements of the language from calling procedures to standard library functions. In this final installment I'll pull together the elements and add some typical interaction between the WMLScript function and a WML deck through which the user interacts with the application. -------------------------------------------------------------------------------- WMLScript doesn't provide indirection, which means array processing can get ugly. -------------------------------------------------------------------------------- To illustrate the interactions I'll develop a simple game of magic squares, a puzzle where the player must enter values into a matrix such that when a particular mathematical function is applied, each row and column yields the same result. To recap, we're representing each row in the playing matrix by a string array. The techniques developed in "WMLScript Conversions and Conversations" will allow the user to insert a value in the array and generate a sum of all the values in the array. The next challenge is to assemble and process a set of arrays to represent the playing matrix. To support a 4 by 4 playing matrix we'll need five arrays, the first four contain four user-selected elements each plus a sum, with the fifth array containing four column totals. All elements are initialized to 0: We'll also need five variables to contain the formatted rows for display: To skip ahead and look at the complete WML deck, click here: < Code Insert 1>

To play magic squares, make all row and columns equal. Select game type.

Rows and columns will be added. Press start to begin.

Rows and columns will be multiplied. Press start to begin.

Row (1 - 4):

Column (1 - 4):

Value (1 - 100):

$disrow1
$disrow2
$disrow3
$disrow4
$disrow5

For the complete WMLScript function, click here: FormRow: This function takes one new value/coordinate pair, tests it, inserts it in its proper line, and calculates the new game array. extern function FormRow () { var gametype = WMLBrowser.getVar ("gametype"); var row = WMLBrowser.getVar ("row"); var col = WMLBrowser.getVar ("col"); var val = WMLBrowser.getVar ("value"); var row1vals = WMLBrowser.getVar ("row1"); var row2vals = WMLBrowser.getVar ("row2"); var row3vals = WMLBrowser.getVar ("row3"); var row4vals = WMLBrowser.getVar ("row4"); var colindex = (Lang.parseInt(col) - 1); var localdisrow; var localrowvals; // Test for appropriate values var validinputs = false; while (validinputs == false) { if (Lang.parseInt(row) > 4) { row = Dialogs.prompt("Re-enter row value 1 - 4",""); continue; } if (Lang.parseInt(col) > 4) { col = Dialogs.prompt("Re-enter column value 1 - 4",""); colindex = (Lang.parseInt(col) - 1); continue; } val = Lang.parseInt(val); if (val > 100) { val = Dialogs.prompt("Re-enter value 0 - 100",""); continue; } validinputs = true; } // Inputs tested, time to process. row = Lang.parseInt (row); if ( row == 1 ) { localrowvals = row1vals; } else if ( row == 2 ) { localrowvals = row2vals; } else if ( row == 3 ) { localrowvals = row3vals; } else if ( row == 4 ) { localrowvals = row4vals; } localrowvals = String.removeAt ( localrowvals, colindex, ","); localrowvals = String.insertAt ( localrowvals, val, colindex, ","); // Sum each row and format the output. localdisrow = ">"; var rowsum = 0; if (gametype == "prod") rowsum = 1; for ( var i = 0; i < 4; i++ ) { var newelement = String.elementAt(localrowvals, i, ","); newelement = Lang.parseInt(newelement); if (gametype == "sum") { rowsum = rowsum + newelement; } if (gametype == "prod") { rowsum = rowsum * newelement; } newelement = String.format("%3.1d", newelement); localdisrow = (localdisrow + newelement); } rowsum = String.format("%3.1d", rowsum); localdisrow = (localdisrow + "|" + rowsum); if ( row == 1 ) { WMLBrowser.setVar ("disrow1", localdisrow); WMLBrowser.setVar("row1", localrowvals); row1vals = localrowvals; } else if ( row == 2 ) { WMLBrowser.setVar ("disrow2", localdisrow); WMLBrowser.setVar("row2", localrowvals); row2vals = localrowvals; } else if ( row == 3 ) { WMLBrowser.setVar ("disrow3", localdisrow); WMLBrowser.setVar("row3", localrowvals); row3vals = localrowvals; } else if ( row == 4 ) { WMLBrowser.setVar ("disrow4", localdisrow); WMLBrowser.setVar("row4", localrowvals); row4vals = localrowvals; } // Sum each column and format the last row. var discolsums = "-"; var colchars; for (i = 0; i < 4; i++) { var colsum = 0; if (gametype == "prod") colsum = 1; colchars = String.elementAt(row1vals, i, ","); if (gametype == "sum") colsum = colsum + Lang.parseInt(colchars); if (gametype == "prod") colsum = colsum * Lang.parseInt(colchars); colchars = String.elementAt(row2vals, i, ","); if (gametype == "sum") colsum = colsum + Lang.parseInt(colchars); if (gametype == "prod") colsum = colsum * Lang.parseInt(colchars); colchars = String.elementAt(row3vals, i, ","); if (gametype == "sum") colsum = colsum + Lang.parseInt(colchars); if (gametype == "prod") colsum = colsum * Lang.parseInt(colchars); colchars = String.elementAt(row4vals, i, ","); if (gametype == "sum") colsum = colsum + Lang.parseInt(colchars); if (gametype == "prod") colsum = colsum * Lang.parseInt(colchars); discolsums = (discolsums + String.format("%3.1d", colsum)); } WMLBrowser.setVar ("disrow5", discolsums); WMLBrowser.go ( "magic.wml#DisplayResult" ); WMLBrowser.refresh(); } Once the user provides a row, column and value to be inserted in the array, the WML deck calls the script function, which first checks the validity of each input. Note the loop that allows the user to keep trying until they get it right: // Test for appropriate values var validinputs = false; while (validinputs == false) { if (Lang.parseInt(row) > 4) { row = Dialogs.prompt("Re-enter row value 1 - 4",""); continue; } if (Lang.parseInt(col) > 4) { col = Dialogs.prompt("Re-enter column value 1 - 4",""); colindex = (Lang.parseInt(col) - 1); continue; } val = Lang.parseInt(val); if (val > 100) { val = Dialogs.prompt("Re-enter value 0 - 100",""); continue; } validinputs = true; } With inputs validated, summing the row and formatting the output is straightforward, localdisrow = ">"; var rowsum = 0; if (gametype == "prod") rowsum = 1; for ( var i = 0; i < 4; i++ ) { var newelement = String.elementAt(localrowvals, i, ","); newelement = Lang.parseInt(newelement); if (gametype == "sum") { rowsum = rowsum + newelement; } if (gametype == "prod") { rowsum = rowsum * newelement; } newelement = String.format("%3.1d", newelement); localdisrow = (localdisrow + newelement); } rowsum = String.format("%3.1d", rowsum); localdisrow = (localdisrow + "|" + rowsum); , but dealing with the columns brings out a limitation in the WMLScript language. There is no indirection provided within WMLScript, so processing the ith element of the jth row gets ugly. I found the code most legible by just explicitly processing each row for (i = 0; i < 4; i++) { var colsum = 0; colchars = String.elementAt(row1vals, i, ","); colsum = colsum + Lang.parseInt(colchars); colchars = String.elementAt(row2vals, i, ","); colsum = colsum + Lang.parseInt(colchars); colchars = String.elementAt(row3vals, i, ","); colsum = colsum + Lang.parseInt(colchars); colchars = String.elementAt(row4vals, i, ","); colsum = colsum + Lang.parseInt(colchars); discolsums = (discolsums + String.format("%3.1d", colsum)); } but it is possible to build up a variable name in a loop before retrieving an array from the WML deck, in essence providing indirection: for (var i = 0; i < 4; i++) { var colsum = 0; var rowvals = ""; for (var j = 1; j < 5; j++) { rowvals = WMLBrowser.getVar ("row" + String.format("%1d", j)); colchars = String.elementAt(rowvals, i, ","); colsum = colsum + Lang.parseInt(colchars); } discolsums = (discolsums + String.format("%3.1d", colsum)); } The full example adds a few playing enhancements to the game that illustrate other aspects of interaction between WML decks and WMLScript functions. The user can select whether rows and columns will be summed or multiplied. The initial card presented to the player uses an option element to guide the selection, and separate cards to initialize the game based on the selection. There is also a "reset game" option presented when the calculated game is displayed (card DisplayResult). It is also possible to present this game reset capability throughout the interaction by binding the do element to the entire deck using the template element. However, this results in a menu of options (OK, Reset) being presented to the user each time they enter a value during the game. I found these extra key strokes more annoying than helpful, so I bound the appropriate do element to just one card. By binding it to a card, the user agent binds each option (OK, reset) to different keys. TechCrawler -------------------------------------------------------------------------------- Want more information on WAP? Search the Web. There are of course other enhancements that could be added to the game if one wished. A guess counter could track how many tries it took a player to complete a square; it would be nice if the user could select an array size less than the code currently fills and have the display of empty squares suppressed. It would even be possible to allow two players to collaborate (or challenge each other) on the same game. Whatever enhancements might be added, though, will use the same basic principles of design. WML decks provide the user interface and data interaction in a declarative environment, WMLScripts provide logic with an imperative structure, and standard libraries provide much of the real power. -------------------------------------------------------------------------------- About the author: Kevin Sharp is a registered professional engineer, writer, and yoga teacher living in Tucson, Arizona. His engineering outlets include web consulting for ID Systems Magazine focusing on the fulfillment side of electronic commerce. His writing interests have produced books and articles on the economic impact of technology on manufacturing and distribution organizations. Personal milestones include the 20-year anniversary of the beginning of a most amazing relationship and the recent embarkation on a yoga teaching path in the ashtanga style.