Number guessing game

Adapted from Tony Collen's GettingStartedWithFlow page on the Cocoon wiki.

In this example, an HTML form is shown repeatedly, until the user guesses the correct number.

Start the game here.

The sitemap

At the sitemap level, Flowscript applications need four things:

The corresponding excerpts of our sitemap are shown below.

Flowscript declaration

Note that this already contains the declaration of the next example's Flowscript.

<map:flow id="flow" language="javascript">
<map:script src="number-guess/guess-number.js"/>
<map:script src="multi-page/multi-page.js"/>
<map:script src="java-shapes/java-shapes.js"/>
</map:flow>

map:call function

Here we use variables to allow any Flowscript function to be called, with a simple security restriction: the function name must start with the public_ prefix.

The maxValue parameter is used by our Flowscript public_startGuessNumber function, but doesn't hurt if it is passed to other functions (like in the next example).

<map:match id="start" pattern="*/start*">
<map:call function="public_start{2}">
<map:parameter name="maxValue" value="10"/>
</map:call>
</map:match>

map:call continue

This activates a Flowscript continuation when an URL ending with a continuation ID and the .continue suffix is received.

<map:match id="continue" pattern="**/*.continue">
<map:call continuation="{2}"/>
</map:match>

View pipeline using JXTemplageGenerator

To be able to include data provided by Flowscript in our forms and views, we use the Flowscript-aware JXTemplateGenerator

Here's the pipeline

<map:match id="views" pattern="*/views/*">
<map:generate src="{1}/{2}.xml" type="file"/>
<map:transform type="jx"/>
<map:call resource="html"/>
</map:match>

Which uses this resource to convert the page to HTML

<map:resource id="html" name="html">
<map:transform src="../intro/presentation/page2html.xsl"/>
<map:serialize type="html"/>
</map:resource>

And here's the JXTemplate component declaration

<map:generators id="generators" default="file">
<map:generator name="jx" src="org.apache.cocoon.generation.JXTemplateGenerator" label="content" logger="sitemap.generator.jx"/>
</map:generators>

Flowscript code

Here's the complete Flowscript code that drives the number-guessing game. (sorry about the lost indentation, the Slop block, which generates these listings, has been improved in the meantime).

guess-number.js
0001 /*
0002 * Licensed to the Apache Software Foundation (ASF) under one or more
0003 * contributor license agreements. See the NOTICE file distributed with
0004 * this work for additional information regarding copyright ownership.
0005 * The ASF licenses this file to You under the Apache License, Version 2.0
0006 * (the "License"); you may not use this file except in compliance with
0007 * the License. You may obtain a copy of the License at
0008 *
0009 * http://www.apache.org/licenses/LICENSE-2.0
0010 *
0011 * Unless required by applicable law or agreed to in writing, software
0012 * distributed under the License is distributed on an "AS IS" BASIS,
0013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014 * See the License for the specific language governing permissions and
0015 * limitations under the License.
0016 */

0018 // simple number guessing game in Flowscript
0019 // based on the Cocoon Flow tutorial:
0020 // http://cocoon.apache.org/2.1/userdocs/flow/tutor.html

0022 function public_startGuessNumber() {
0023 var max = cocoon.parameters["maxValue"];
0024 var toGuess = Math.round(Math.random() * max);
0025 if(toGuess == 0) toGuess = 1;
0026 var hint = "Guess a number between 1 and " + max;
0027 var tries = 0;

0029 // show and process input form, until correct answer is given
0030 while (true) {
0031 cocoon.sendPageAndWait("number-guess/views/guess", {"toGuess" : toGuess, "hint" : hint, "tries" : tries});
0032 var answer = parseInt( cocoon.request.get("answer") );

0034 tries++;

0036 if(answer) {
0037 if(answer > toGuess) {
0038 hint = "The number you entered (" + answer + ") is too big";
0039 } else if(answer < toGuess) {
0040 hint = "The number you entered (" + answer + ") is too small";
0041 } else {
0042 break;
0043 }
0044 }
0045 }

0047 cocoon.sendPage("number-guess/views/success", {"toGuess" : toGuess, "answer" : answer, "tries" : tries} );
0048 }

JXTemplate view

The JXTemplateGenerator makes Flowscript variables (like the toGuess and hint attributes of the sendPageAndWait call above) accessible using a simple substitution syntax.

Here's the page definition for our number-guessing form. JXTemplate codes like ${cocoon.continuation.id}, will by replaced by values provided in the Flowscript sendPageAndWait function call.

<page id="page">
<title> Flow example: Guess a number </title>
<content>
<h2> ${hint} </h2>
<form method="post" action="${cocoon.continuation.id}.continue">
<input type="text" name="answer"/>
<input type="submit"/>
</form>
<p class="footer">
<a href="../docs/index.html"> Flow examples </a>
</p>
</content>
</page>

That's it!

We have now seen the complete code of our Flowscript application:

We didn't have to write much code to implement this, and more importantly all the code that we wrote is application-specific. The Cocoon framework provides all the infrastructure.