Game Of Life

This example contains an implementation of the Game Of Life entirely written in SXML. On every form post a new generation in the game of life is calculated and send back to the client. The current state of the game is stored in a hidden 'world' input element and send back and forth between server and client. The initial state of the game is defined in the body of the item and read using the itemdata viper.

The example relies heavily on buffers and the se:for macro to implement the game logic. Please note that this example is just a proof of concept that it's possible to implement fairly complex business logic in SXML. In the real world the game logic would be implemented in a custom macro in order to achieve the best performance.

About Game Of Life

The Game Of Life is a 'cellular automaton', and was invented by Cambridge mathematician John Conway. It consists of a collection of cells which, based on a few mathematical rules, can live, die or multiply. Depending on the initial conditions, the cells form various patterns throughout the course of the game.

For more information see http://www.bitstorm.org/gameoflife/.

Smartsite SXML CopyCode image Copy Code
<se:buffer scope="page" name="initialworld" trim="both">
........
O.O.....
.OO.....
.O......
........
........
........
........
</se:buffer>
<form method="post">
    <!--// get world from form post -->
    {buffer.set(worldData, string.isempty(request.form.list(world), buffer.get(initialworld, page)))}
    {buffer.set(generation, convert.toint(string.isempty(request.form.list(generation), 0)) + 1)}
    {buffer.set(world, string.split($worldData, char.crlf()))}
    <se:text whitespace="remove">
        {buffer.set(newWorld, string.split(buffer.get(initialworld, Page), char.crlf()))}
        <!--// suppress output -->
        <se:text resulttype="none">
            <!--// remove rem and empty lines from world -->
            {datatable.delete($world, "col1 = '' or col1 like '!*'")}
            <!--// empty new world -->
            {datatable.delete($newWorld, "")}
        </se:text>
        
        <!--// calculate next generation -->
        <se:for from="1" to="{datatable.getrowcount($world)}" resulttype="none">
            {buffer.set(line, datatable.getvalue($world, this.index(), 1))}
            {buffer.set(newLine, "")}
            <se:for from="1" to="{string.length($line)}">
                <!--// calculate new value for each cell -->
                {buffer.set(x, this.index()-1)}
                {buffer.set(y, this.parent.index())}
        
                {buffer.set(populated, string.substring($line, $x, 1) == "O")}
                {buffer.set(neighbors, 0)}
                <se:if expression="$populated">
                    <!--// exclude myself from neighbors -->
                    {buffer.set(neighbors, -1)}
                </se:if>
                
                <!--// find neighbors-->
                <se:for from="{math.max($y-1, 1)}" to="{math.min($y+1, datatable.getrowcount($world))}">
                    {buffer.set(currentLine, datatable.getvalue($world, this.index(), 1))}
                    
                    <se:for from="{math.max($x-1, 0)}" to="{math.min($x+1, string.length($currentLine) - 1)}">
                        <se:if expression="string.substring($currentLine, this.parent.index(), 1) == 'O'">
                            {buffer.set(neighbors, $neighbors+1)}
                        </se:if>
                    </se:for>
                </se:for>
        
                <!--// rules Game Of Life -->
                {buffer.set(newCell, ".")}
                <se:if expression="$populated AND $neighbors == 2 || $neighbors == 3">
                    {buffer.set(newCell, "O")}    
                </se:if>
                {buffer.set(newLine, string.concat($newLine, $newCell))}
            </se:for>
    
            {datatable.add($newWorld, $newLine)}
        </se:for>
        <!--// replace world -->
        {buffer.set(world, $newWorld)}
    </se:text>

    {buffer.set(newWorldData, "")}
    
    <!--// render generation -->
    <b>Generation {buffer.get(generation)}</b>
    <se:text whitespace="remove">
        <pre style="font-size:7">
            <se:format inputdata="world">
                <se:rowformat>
                    {this.field(1)}<br />
                
                    <!--// serialize world -->
                    {buffer.set(newWorldData, string.concat($newWorldData, this.field(1), char.crlf()))}
                </se:rowformat>
            </se:format>
        </pre>
    </se:text>

    <input type="hidden" name="world" value="{buffer.get(newWorldData)}" />
    <input type="hidden" name="generation" value="{buffer.get(generation)}" />
    <input type="submit" value="Next Generation" />
</form>
Example Result CopyCode image Copy Code
<form method="post">
    
    
    
    
    

    
    
    
    <b>Generation 1</b>
    <pre style="font-size:7">........<br />..O.....<br />O.O.....<br />.OO.....<br />........<br />........<br />........<br />........<br /></pre>

    <input type="hidden" name="world" value="........
..O.....
O.O.....
.OO.....
........
........
........
........
" />
    <input type="hidden" name="generation" value="1" />
    <input type="submit" value="Next Generation" />
</form>