4 January 2014

Silvester Party XSLT

After finishing my Code Cop Journeyman Tour last month I am free for new adventures. My backlog of insights I want to share is huge, but today I will celebrate the turn of the year. In Austria we usually celebrate the New Year's Eve with a party so let's see the results of my Silvester "party".

Last Global Day of Coderetreat, one participant used MSBuild to implement the rules of Conway's Game of Life. Maybe it did not improve his grasp of object orientation but surely trained his skills of writing build scripts and besides all it looked like a lot of fun. I have worked on the Game of Life in many mainstream programming languages, e.g. Java, JavaScript, Ruby and Scala as well some as less common languages like Dart, Pascal or even good old BASIC. MSBuild seemed like a perfect change from the usual and made me think about unfamiliar or even bizarre approaches, e.g. using Apache Ant or even XSLT (XSL transformations).

Over the years I have implemented the Prime Factors kata and the FizzBuzz kata in XSLT but never anything more elaborate. Although XSLT is a Turing complete language, I was not sure it would work out. On the other hand, the representation of the universe, i.e. the cells and their neighbours, might be some basic XML structure removing the need to implement any complex data structures. I even chose an HTML table to make visualization trivial.
<table>
  <tr><td>_</td><td>X</td><td>_</td></tr>
  <tr><td>_</td><td>X</td><td>_</td></tr>
  <tr><td>_</td><td>X</td><td>_</td></tr>
</table>
Based on this data structure I started writing XSLTunit test cases for evolving a 3x3 grid:
  • dead cells should stay dead
  • a single living cell in the middle should die
  • the 2x2 block in the upper left should stay alive
  • the 2x2 block in the lower right should stay alive
  • two single cells in the upper left should die
By triangulating the fake implementations I approached a solution, at least that was what I thought. It turned out that I did not nearly know enough about XPath and most of my selectors using parent, preceding-sibling or following-sibling were wrong but happened to return an expected result in my test cases. When I thought I had finished the code nothing worked as expected. Now how would I debug my XSLT code? No, I hate debugging and took a step back to write some tests to verify the selection of the living neighbours alone:
<xsl:template match="td" mode="countLiveNeighbours">
  <xsl:variable name="currentX"
                select="count(preceding-sibling::td) + 1" />
  <xsl:variable name="precedingRow"
                select="parent::tr/preceding-sibling::tr[1]" />
  <xsl:variable name="followingRow"
                select="parent::tr/following-sibling::tr[1]" />

  <xsl:variable name="neighbours"
                select="$precedingRow/td[$currentX - 1] |
                        $precedingRow/td[$currentX] |
                        $precedingRow/td[$currentX + 1] |
                        preceding-sibling::td[1] |
                        following-sibling::td[1] |
                        $followingRow/td[$currentX - 1] |
                        $followingRow/td[$currentX] |
                        $followingRow/td[$currentX + 1]" />

  <xsl:value-of select="count($neighbours[text() = 'X'])" />
</xsl:template>
As soon as I finished the countLiveNeighbours template, my previously created solution worked as expected.
<xsl:template match="td">
  <xsl:variable name="liveNeighbours">
    <xsl:apply-templates select="current()" mode="countLiveNeighbours" />
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="(current() = 'X' and $liveNeighbours = 2) or
                    $liveNeighbours = 3">
      <xsl:call-template name="live" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="die" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
I had chosen XSLT as bizarre approach on purpose, so I guess it is ok that it took me eight hours of Silvester to figure out the above solution ;-)

(Complete source at Bitbucket.)

2 comments:

Sebastian said...

Really man, XSLT? :-) You are the only developer I know that uses XSLT in his spare time voluntarily, just for fun :-)

Kudos for your hard stomach :-D

Peter Kofler said...

Thank you Sebastian. Just think about it, when you do not have to do something, it more likely to be fun. Obviously I would not be happy to use XSLT for some critical code that needs to be finished yesterday, but in my free time there is no pressure or need to get anything done.