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
following-siblingwere 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" /> <xsl:variable name="followingRow" select="parent::tr/following-sibling::tr" /> <xsl:variable name="neighbours" select="$precedingRow/td[$currentX - 1] | $precedingRow/td[$currentX] | $precedingRow/td[$currentX + 1] | preceding-sibling::td | following-sibling::td | $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
countLiveNeighbourstemplate, 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.)