MobileInteractive Fun and Games with WAP and WML

Interactive Fun and Games with WAP and WML

This series of articles describes how to provide Webcontent to mobile devices through WML (Wireless Markup Language). This articlecovers creating an interactive game for deployment on mobile WML devices.

Note: These articles cover WML and WMLScript version 1.1,which are supported by the majority of mobile devices in use today. Thearticles assume a working knowledge of HTML and general Web technologies, andfurther assume that you have read the previous article(s) in this series.

Uses for Mobile Devices

Mobile devices are the most useful when they are connected to data sources and have the ability to deliver various data whenever needed. However, mobile devices are also very useful for entertainment purposes-I’ve spent many hours in airports with only my PDA and phone for company. Although WML doesn’t lend itself to a complex gaming experience, it is fairly easy to create interactive entertainment. In this article I will lead you through the steps to create a rudimentary "hangman" game.

Project Specifications

Hangman is a straightforward game. For anyone unfamiliarwith the game, a word is shown as blanks and the player guesses letters thatmay be in the word. If a letter guessed is in the word, all instances of that letterare revealed. If a guessed letter is not in the word, part of a hanging stickfigure is drawn on a scaffold, at the end of a nooseusually starting with thehead, then the body, then the limbs (one at a time). The game ends when theword is guessed or the figure is completely drawn. In the former case theplayer wins, in the latter the player loses.

The functionality for our project is as simple as the pen-and-paper version of the game:

  1. A word is chosen and displayed as blanks.
  2. The player picks a letter.
  3. The letter is compared to the letters in the word; if the letter is in the word, the appropriate blanks are changed to display the letter. If the letter is not in the word, a piece is added to the hanging stick figure.
  4. The game ends when the user knows the word or the figure is complete.

Once the functionality has been determined, the interface must be drafted.

  • All output must fit on a display that is approximately 16 characters wide and 4-10 lines tall.
  • The stick figure will be represented by ASCII characters instead of graphics since we can’t ensure that the player will be using a graphics-capable device. The figure will be made of 6 segments, giving the player 6 letter selections.
  • Several cards will be used, one for each function. That will help segregate the functions and keep the display lean.
  • The words must be random and taken from a decent-sized list to keep the player engaged.
  • To simplify coding, all words will be in lowercase and the player’s input will be forced to lowercase.

Coding the Basics

This project will utilize WML for the input and output andWMLScript for the behind-the-scenes processing. Because debugging tools forWMLScript are limited, we will build this project in small stages, addingfeatures only after the current feature set is solid.

To start, we will create a simple deck of two cards. Thefirst card will call a WMLScript function to initialize the game’s variables.The second card will then be displayed, showing the word as blanks and as plaintext (for debugging purposes).

Our skeletal code looks like this:

WML

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
  3. "http://www.wapforum.org/DTD/wml_1.1.xml">

  1. <wml>
  1. <card id="card1" title="Hangman">
  2. <!– When this card is entered, call the init routine –>
  3. <onevent type="onenterforward">
  4. <go href="hangman.wmls#init()"/>
  5. </onevent>
  6. <p>
  7. <!– Tell the user we are initializing. When the
  8. init function is finished it will call card2 –>
  9. Initializing…
  10. </p>
  11. </card>
  1. <card id="card2" title="Hangman">
  2. <p>
  3. $word<br/>
  4. $blank
  5. </p>
  6. </card>
  1. </wml>

WMLS

  1. extern function initword() {
  2. // Define word list (pseudo array)
  3. var words = "animal announce banana doctor elephant giraffe";
  4. var idx,x = 0;
  5. var blank,word = "";
  1. // Randomize a word
  2. idx = Lang.random(6);
  3. word = String.elementAt(words,idx," ");
  1. // Add an "*" for every letter in the chosen word
  2. for (x = 1; x <= String.length(word); x++ ) {
  3. blank = blank + "*";
  4. }
  1. // Set the WML vars and call card2
  2. WMLBrowser.setVar("word", word);
  3. WMLBrowser.setVar("blank",blank);
  4. WMLBrowser.go("hangman.wml#card2");
  5. }

The first design decision is how to store the word list.Because standard WML technologies do not provide a convenient link to theoutside world (databases, etc.), the list has to be stored internally, withinthe script itself. The lack of true arrays in WMLScript contributes to theproblem as well. However, WMLScript’s string handling solves the problem wellenoughthe words will be stored in delimited form within one long string (WMLSline 3). The String.elementAt function could then be used to parse one of thewords from the list (WMLS line 8).

Next the "blank" template must be constructed forthe word (lines 9-12). At this point, the variable $word will contain theactual word, while $blank contains the blank representation. The variables arepushed out to the browser and the next card is displayed, showing all thevalues.

Note: Even this partof the script was developed in pieces, though for the sake of brevity in thisarticle I’ve chosen to start with this chunk of code. Initially, I created thestub WML code to call the init function, which simply set a variable. Then Icreated the word list and randomized a word, which was displayed by the WML.Finally, I created the blanking code to create the blank word.

Tip: The Openwave SDK provides a great environment todevelop applications in a stairstep method. Visit the developer site at www.openwave.com for more information or todownload the SDK.

Next we need to add the player entry code and the ability tocheck the entry against the word. The new code resembles the followinglistings:

WML

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
  3. "http://www.wapforum.org/DTD/wml_1.1.xml">
  1. <wml>
  1. <card id="card1" title="Hangman">
  2. <onevent type="onenterforward">
  3. <go href="hangman.wmls#initword()"/>
  4. </onevent>
  5. <p>
  6. Initializing…
  7. </p>
  8. </card>
  1. <card id="card2" title="Hangman">
  2. <do type="accept">
  3. <go href="hangman.wmls#guess()" />
  4. </do>
  5. <p>
  6. Guess: <input name="guess" maxlength="1" format="a" />
  7. $word<br/>
  8. $blank
  9. </p>
  10. </card>
  1. </wml>

WMLS

  1. extern function initword() {
  2. var words = "animal announce banana doctor elephant giraffe";
  3. var idx,x,hang = 0;
  4. var blank,word = "";
  1. idx = Lang.random(6);
  2. word = String.elementAt(words,idx," ");
  1. for (x = 1; x <= String.length(word); x++ ) {
  2. blank = blank + "*";
  3. }
  1. WMLBrowser.setVar("word", word);
  2. WMLBrowser.setVar("blank",blank);
  3. WMLBrowser.setVar("hang",hang);
  4. WMLBrowser.go("hangman.wml#card2");
  5. }
  1. extern function guess() {
  1. var x = 0;
  2. var temp,ch = "";
  1. var word = WMLBrowser.getVar("word");
  2. var blank = WMLBrowser.getVar("blank");
  3. var guess = WMLBrowser.getVar("guess");
  1. // Check each letter in word. Transfer letters/blanks
  2. // from blank, revealing any new matches.
  3. for (x = 0; x <= String.length(word); x++ ) {
  4.  ch = String.subString(word,x,1);
  5.  if ( ch == guess ) {
  6. temp = temp + guess;
  7.  } else {
  8. temp = temp + String.subString(blank,x,1);
  9.  }
  10. }
  1. WMLBrowser.setVar("blank",temp);
  2. WMLBrowser.go("hangman.wml#card2");
  1. }

The new code (mostly the guess function, WMLS lines 15-33)accepts user input (WML line 18) and compares it to the letters in the word. A"temporary" word is built, revealing each matching letter and leavingthe rest of the word as revealed or hidden as it was before the guess. Thetemporary variable ($temp) is then passed back to the browser as the new $blankvariable.

Image courtesy Openwave Systems Inc. (Openwave, the Openwave logo, Openwave SDK, Openwave SDK Universal Edition, Openwave SDK WAP Edition are trademarks of Openwave Systems Inc. All rights reserved.)

The new code allows for input and displays debugging output.Note that "a" was guessed in the previous round, revealing the 3 a‘s in "banana." Because the$guess variable is persistent, the value sticks in the input field. We willhave to fix that eventually.

Adding the Scoring (Hangman)

As Figure 8.1 shows, we are quickly running out of screenreal estate for our game. Since we are using text to depict our hanging man, wewill need 4-6 more lines to display him – and we obviously do not have enoughlines. Time for an interface change.

Instead of putting the input on the same page as the status,we will move it to a "guess" card. It means more work for the user(he or she must select a "guess" button to display the input), butcleans up our interface.

For the hanging man, we will use the following ASCIIrepresentation:

 0-|-/ 

He is crude but recognizable. We can break the man down intosix pieces: the head, torso, two arms, and two legs. If we get creative andtreat each piece as a string, we can add appropriate line breaks and print eachstring in sequence, effectively "building" the man. Consider thefollowing:

Head: "<space>0<linebreak>"

Arm1: "-"

Torso: "|"

Arm2: "-<linebreak>"

Leg1: "/"

Leg2: "<space>"

When printed in sequence, the hanging man is displayed. Ifwe print only the first three strings, only three pieces of the man (head, arm,torso) are displayed.

Again, because WMLS lacks real arrays, we will build adelimited string that contains all the pieces:

" , 0r,-,|,-r,/,r"

Note that a comma is used as the delimiting character, andthat we put a blank piece (a space) in the beginning to give the first realpiece an index of 1 instead of 0. Alsonote that we use escape codes for line breaks (r = newline) and we must escapethe backslash. Because WML doesn’t parse variables, using WML code for linebreaks would only result in the tags (such as "<br />") beingdisplayed as text instead of being interpreted as line breaks.

Finishing Up

Although it seems like there is a lot left to do, theproject is mopped up pretty quickly by doing the following:

  • Adding a variable and code to count the incorrect guesses and determine loss
  • Checking whether the word has been totally revealed (win)
  • Adding appropriate cards for win/loss
  • Building a string variable to display the hanging man
  • Various housekeeping and cleanup chores (renaming some functions, cleaning up some variables, adding comments, etc.)

Most of these tasks revolve around counting the number ofguesses. The last task is the usual housekeeping that takes place at the end ofa project. The final code looks like this:

WML

<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN""http://www.wapforum.org/DTD/wml_1.1.xml"><!-- hangman.wml - WML hangman game --><wml><!-- Game initialization - only calls init script --><card id="init" title="Hangman"><onevent type="onenterforward"><go href="hangman.wmls#init()"/></onevent><p>Initializing...</p></card><!-- Status card. Shows hanged man and revealed word --><!-- User presses Accept key to make a guess --><card id="status" title="Hangman"><do type="accept" label="Guess"><go href="#guess" /></do> <p>&nbsp;|<br/>$man<br/><br/>$blank</p></card><!-- Accepts user's guess (one character, lowercase) --><card id="guess" title="Hangman"><do type="accept"><go href="hangman.wmls#guess()" /></do> <p> $blank<br/> Guess: <input name="guess" maxlength="1" format="a" /> </p> </card> <!-- Game over, player has been hanged. --> <card id="hung" title="Hangman"> <do type="accept" label="Restart"> <go href="#init" /> </do>  <p> You've been hanged!<br/><br/> Word was:<br/> &nbsp;$word<br/> Your guess:<br/> $blank </p> </card> <!-- Game over, player wins..--> <card id="win" title="Hangman"> <do type="accept" label="Restart"> <go href="#init" /> </do>  <p> You win!<br/><br/> Word was:<br/> &nbsp;$word<br/> </p> </card> </wml>

WMLS

// hangman.wmls // Functions for hangman game // Variables: // word = word to be guessed// blank = blank representation of 'word', letters// begin as asterisks ("*") and are revealed as guessed// hang = current count of incorrect guesses// man = current ASCII representation of hanging man// guess = current character guessed// Initialize gameextern function init() { // Init vars. Change words every so often (or add to them) var words = "animal announce banana doctor elephant giraffe"; var idx,x,hang = 0; var blank,word,man = ""; // Pick a random word from list idx = Lang.random(6); word = String.elementAt(words,idx," "); // Build a blank string (letters all "*") that //  is the same length as our word for (x = 1; x <= String.length(word); x++ ) {    blank = blank + "*"; } // Pass all values to browser WMLBrowser.setVar("word",word); WMLBrowser.setVar("blank",blank); WMLBrowser.setVar("hang",hang); WMLBrowser.setVar("man",man); // Display status card WMLBrowser.go("hangman.wml#status");}// Evaluate current guessextern function guess() {// Init varsvar x = 0;var temp = "";var correct = false;var man = "";// Pieces for the hanging man,comma delimitedvar manpieces = " , 0r,-,|,-r,/, r";// Get current valuesvar word = WMLBrowser.getVar("word");var hang = WMLBrowser.getVar("hang");var blank = WMLBrowser.getVar("blank");var guess = WMLBrowser.getVar("guess");// Walk one character at a time through word// If guess = character, reveal character// If guess != character, keep current value// (revealed character or blank)// Also, set "correct" if at least one char foundfor (x = 0; x <= String.length(word); x++ ) {  if ( String.subString(word,x,1) == guess ) {    temp = temp + guess;    correct = true;  } else {     temp = temp + String.subString(blank,x,1);   }}// If letter wasn't found, add one to hanging counterif (! correct) { hang++; }// Build our hanging manif (hang > 0) {  for (x = 1; x <= hang; x++ ) {   man = man + String.elementAt(manpieces,x,",");  }}// Blank the guess so <input> is blankguess = "";// Pass current values to browserWMLBrowser.setVar("blank",temp);WMLBrowser.setVar("guess",guess);WMLBrowser.setVar("hang",hang);WMLBrowser.setVar("man",man);// Determine whether player has won (no more// "*" in blank), has lost (6 pieces of man// displayed, or keep playing (else).if ( String.find(temp,"*") == -1 ) { WMLBrowser.go("hangman.wml#win");} else { if (hang >= 6) {   WMLBrowser.go("hangman.wml#hung"); } else {  WMLBrowser.go("hangman.wml#status"); }}}

The starting screen of the game, complete with hangman’snoose.

Pressing the Accept key brings up the guessing card.

As the player progresses, correct guesses are shown byrevealing characters in the word; incorrect guesses add to the hanging man.

Images are courtesy Openwave Systems Inc. (Openwave, the Openwave logo, Openwave SDK, Openwave SDK Universal Edition, Openwave SDK WAP Edition are trademarks of Openwave Systems Inc. All rights reserved.)

If the player guesses six incorrect letters, the game ends.Guessing all characters in the word, conversely, shows a "win"screen. Note the "Restart" function mapped to Accept.

Things to Add

There’s always room for improvement, especially inprogramming and interfaces. Given time, I’d add the following to the game:

  • A graphic version of the hanging man as a default, allowing users to switch to text if necessary.
  • Tracking and displaying the incorrect characters guessed
  • A splash screen (introductory graphical screen), help text, and a cheat function (displays one character not yet revealed)

I’d also lengthen the word list and dynamically change itfrom time to time. One idea would be to run a script on the server that swapsdifferent versions of the WMLS file into place, each with a different wordlist. Another idea is to implement the entire game using PHP or another, morerobust scripting language that could tie into a massive word database.

What Do You Want from WML?

I’m interested in hearing what you need/want to do with WML.I’ll use some of the more challenging or common ideas in upcoming articles.Send your ideas to the address below.

About the Author

Steve Schafer is president and CEO of Progeny Linux Systems,a Linux-based consulting company in Indianapolis, Indiana. He has writtenseveral technical books and articles and can be reached at sschafer@synergy-tech.com.

# # #

Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.
Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.

Latest Posts

Related Stories