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
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 ;-)
2 comments:
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
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.
Post a Comment