[xsl] Linear sequence of moves to rectangular board

Subject: [xsl] Linear sequence of moves to rectangular board
From: Stuart Yeates <stuart.yeates@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 12 May 2005 21:40:37 +0100 (BST)
I'm transforming a linear sequence of moves into the state of a rectangular board (initially in HTML, images to come, maybe in SVG later). The game I'm modelling is the game of, played with black and white stones on a rectangular grid. Stones can be added  and rarely removed. An empty state is sometimes in a transient state called "ko."

The attached xml2xhtml.xslt file transforms test1.go.xml into html, with the board in a table and calculates the number of moves and captures for black and white. By passing in different values of $moves, you can step through the moves in the game.

I'm aiming for maximum portability and maximum inter-operability. The XML file format is still flexible.

I have some questions about where to go from here:

(*) In real games $size is normally 19 (but potentially unbounded), and there are approximately 1/2 (size*size) moves per game. My recursive solution has a max depth of (2*size)+(1/2 (size*size)) ~= 200, is this likely to be a problem for any implementations? I'm particular thinking of transformations in browsers. 

(*) Maybe it's because I'm used to the gracefully recursion of scheme/lisp, but the recursion here feels rather clunky. Am I doing something wrong?

(*) I'd like to define calculate a value for $moves (commented out) and use it if no value is passed in on the command line/url, but I can't work out how. This doesn't work:

<xsl:if test="$moves = ''">
  <xsl:variable name="moves" select="count(//move) -1"/>
</xsl:if>

as the variable has the wrong scope, and $move access is an error if undefined. So how do I make it robust in the face of bad/missing input from the commandline or url?

(*) Currently I'm using explicit "order" attribute. The attribute seems redundant (and potentially error prone), is there a better way to do this?

(*) I would like to represent variations, moving from a linear sequence to a sparse tree, as shown in test2.go.xml and test3.go.xml. The desired behavour would be to display at each moves the possible moves following and provide a way to generate the board state for that move, much as incrementing $move does currently. I'm not sure whether restructuring the XML (which is not yet fixed) format would help.

Looking at other code and the manuals I have access to the ancestor axis seems like it should be the solution, but I can't quite seem to join up the dots. I can can see that one approach could be to transform test3.go.xml into a format like test1.go.xml, and then to html, but I'm not sure how portable two step transformations are.

(*) There are some rather obscure Unicode characters for representing go stones (on the same code-page as the chess pieces). I'm extremely doubtful about their support in a range of browsers, is there a good way to find out how widely they're used? I'm particularly interested in support for them amoung far-east localised windows versions, since this is where most go players are. 

cheers
stuart

---------

<?xml version='1.0'?>
<!-- xml2xhtml.xslt -->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name="size" select="//@dimensiony"/>

  <!--<xsl:variable name="moves" select="count(//move) -1"/>-->

	<!-- do the rows -->

    <xsl:template name="tablerow">
        <xsl:param name="rownumber"/>
         <tr>

        <xsl:call-template name="tablecolumn">
            <xsl:with-param name="columnnumber" select="$size -1"/>
            <xsl:with-param name="rownumber" select="$rownumber"/>
        </xsl:call-template>

        </tr>

       <xsl:if test="$rownumber &gt; 0">
          <xsl:call-template name="tablerow">
            <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber - 1"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:if>
    </xsl:template>


	<!-- do the columns -->	

    <xsl:template name="tablecolumn">
        <xsl:param name="rownumber"/>
        <xsl:param name="columnnumber"/>
         <td>

          <xsl:call-template name="tablecell">
            <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber"/>
            </xsl:with-param>
            <xsl:with-param name="columnnumber"> 
 		<xsl:value-of select="$columnnumber"/>
            </xsl:with-param>
          </xsl:call-template>

        </td>
       <xsl:if test="$columnnumber  &gt; 0">
          <xsl:call-template name="tablecolumn">
            <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber"/>
            </xsl:with-param>
            <xsl:with-param name="columnnumber"> 
 		<xsl:value-of select="$columnnumber - 1"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:if>
    </xsl:template>

	<!-- do a single cell -->	

    <xsl:template name="tablecell">
        <xsl:param name="rownumber"/>
        <xsl:param name="columnnumber"/>

          <xsl:call-template name="tablecellmove">
            <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber"/>
            </xsl:with-param>
            <xsl:with-param name="columnnumber"> 
 		<xsl:value-of select="$columnnumber"/>
            </xsl:with-param>
            <xsl:with-param name="status">empty</xsl:with-param>
            <xsl:with-param name="move"> 
 		<xsl:value-of select="0"/>
            </xsl:with-param>
          </xsl:call-template>

    </xsl:template>

    <xsl:template name="tablecellmove">
        <xsl:param name="rownumber"/>
        <xsl:param name="columnnumber"/>
        <xsl:param name="status"/>
        <xsl:param name="move"/>

        <!-- <xsl:value-of select="$status"/> -->

        <xsl:choose>

          <xsl:when test="$move = $moves">
            <xsl:value-of select="$status"/>
          </xsl:when>

          <xsl:when test="//move[@order=$move]/ko/vertex[@x=$rownumber][@y=$columnnumber]">
            <xsl:call-template name="tablecellmove">
              <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber"/>
              </xsl:with-param>
              <xsl:with-param name="columnnumber"> 
 	        <xsl:value-of select="$columnnumber"/>
              </xsl:with-param>
              <xsl:with-param name="status">ko</xsl:with-param>
              <xsl:with-param name="move"> 
 		<xsl:value-of select="$move + 1"/>
              </xsl:with-param>
            </xsl:call-template>
          </xsl:when>

          <xsl:when test="//move[@order=$move]/remove/vertex[@x=$rownumber][@y=$columnnumber]">
            <xsl:call-template name="tablecellmove">
              <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber"/>
              </xsl:with-param>
              <xsl:with-param name="columnnumber"> 
 	        <xsl:value-of select="$columnnumber"/>
              </xsl:with-param>
              <xsl:with-param name="status">empty</xsl:with-param>
              <xsl:with-param name="move"> 
 		<xsl:value-of select="$move + 1"/>
              </xsl:with-param>
            </xsl:call-template>
          </xsl:when>
          
          <xsl:when test="//move[@order=$move]/vertex[@x=$rownumber][@y=$columnnumber]">
            <xsl:call-template name="tablecellmove">
              <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber"/>
              </xsl:with-param>
              <xsl:with-param name="columnnumber"> 
 	        <xsl:value-of select="$columnnumber"/>
              </xsl:with-param>
              <xsl:with-param name="status">
                <xsl:value-of select="//move[@order=$move]/@colour"/>
              </xsl:with-param>
              <xsl:with-param name="move"> 
 		<xsl:value-of select="$move + 1"/>
              </xsl:with-param>
            </xsl:call-template>
          </xsl:when>

          <xsl:when test="$status = 'ko'">
            <xsl:call-template name="tablecellmove">
              <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber"/>
              </xsl:with-param>
              <xsl:with-param name="columnnumber"> 
 	        <xsl:value-of select="$columnnumber"/>
              </xsl:with-param>
              <xsl:with-param name="status">empty</xsl:with-param>
              <xsl:with-param name="move"> 
 		<xsl:value-of select="$move + 1"/>
              </xsl:with-param>
            </xsl:call-template>
          </xsl:when>
          
          <xsl:when test="//move[@order=$move]">
            <xsl:call-template name="tablecellmove">
              <xsl:with-param name="rownumber"> 
 		<xsl:value-of select="$rownumber"/>
              </xsl:with-param>
              <xsl:with-param name="columnnumber"> 
 	        <xsl:value-of select="$columnnumber"/>
              </xsl:with-param>
              <xsl:with-param name="status"> 
 		<xsl:value-of select="$status"/>
              </xsl:with-param>
              <xsl:with-param name="move"> 
 		<xsl:value-of select="$move + 1"/>
              </xsl:with-param>
            </xsl:call-template>
          </xsl:when>

          <xsl:otherwise>
            <xsl:value-of select="$status"/>
          </xsl:otherwise>
	</xsl:choose>
	
    </xsl:template>

  <xsl:template match="game">
    <html>
       <xsl:apply-templates/>

    <body>

    <p>Board size: <xsl:value-of select="$size"/></p>

    <p>Moves: <xsl:value-of select="$moves"/></p>

    <p>Black captures: <xsl:value-of select="count(//move[@colour = 'black'][@order &lt; $moves]/remove/vertex)"/></p>

    <p>White captures: <xsl:value-of select="count(//move[@colour = 'white'][@order &lt; $moves]/remove/vertex)"/></p>

      <table>
        <xsl:call-template name="tablerow">
            <xsl:with-param name="rownumber" select="$size - 1"/>
        </xsl:call-template>
     </table>
    </body>
    </html>	
  </xsl:template>

</xsl:stylesheet>


---------

<!-- test1.go.xml -->


<?xml version="1.0"?>
<?xml-stylesheet type="text/xml" href="xml2xhtml.xslt"?>
<game>
  <board type="rectangular" dimensiony="3" dimensionx="3" />
  <move colour="black" order="0" >
    <vertex x="0" y="1" />
  </move>
  <annotate>
    <text></text>
  </annotate>
  <move colour="white" order="1">
    <vertex x="0" y="0" />
  </move>
  <move colour="black" order="2">
    <vertex x="1" y="0" />
    <remove>
      <vertex x="0" y="0" />
    </remove>
    <ko>
      <vertex x="0" y="0" />
    </ko>
  </move>
  <annotate>
    <score type="estimated" estimator="gnugo" version="9.7"/>
  </annotate>
  <move colour="white" order="3">
    <vertex x="1" y="1" />
  </move>
  <move colour="black" order="4">
    <vertex x="0" y="0" />
  </move>
</game>



---------

<!-- test2.go.xml -->

<?xml version="1.0"?>
<?xml-stylesheet type="text/xml" href="./xml2xhtml.xslt"?>
<game>
  <board type="rectangular" dimensiony="4" dimensionx="4" />
  <annotation> 
     <text> this is a comment at the start of the file</text>
  </annotation>
  <move colour="black" order="0" id="00001">
    <vertex x="0" y="1" />
  </move>
  <move colour="white" order="1" id="00002">
    <vertex x="0" y="0" />
  </move>
  <move colour="black" order="2" id="00003">
    <vertex x="1" y="0" />
    <remove>
      <vertex x="0" y="0" />
    </remove>
  </move>

  <variation>
  <move colour="white" order="3" id="00004">
    <vertex x="2" y="1" />
  </move>
  <move colour="black" order="4" id="00005">
    <vertex x="1" y="1" />
  </move>
  </variation>

  <variation>
  <move colour="white" order="3" id="00006">
    <vertex x="1" y="1" />
  </move>
  <move colour="black" order="4" id="00007">
    <vertex x="0" y="0" />
  </move>
  </variation>

</game>




---------

<!-- test3.go.xml -->

<?xml version="1.0"?>
<?xml-stylesheet type="text/xml" href="./xml2xhtml.xslt"?>
<game>
  <board type="rectangular" dimensiony="4" dimensionx="4" />
  <annotation> 
     <text> this is a comment at the start of the file</text>
  </annotation>
  <move colour="black" order="0" id="00001">
    <vertex x="0" y="1" />
  </move>
  <move colour="white" order="1" id="00002">
    <vertex x="0" y="0" />
  </move>
  <move colour="black" order="2" id="00003">
    <vertex x="1" y="0" />
    <remove>
      <vertex x="0" y="0" />
    </remove>
  </move>

  <variation>
    <move colour="white" order="3" id="00004">
      <vertex x="2" y="1" />
    </move>
    <variation>
      <move colour="black" order="4" id="00005">
        <vertex x="1" y="1" />
      </move>
    </variation>
    <variation>
      <move colour="black" order="4" id="00006">
        <vertex x="1" y="2" />
      </move>
    </variation>
  </variation>

  <variation>
    <move colour="white" order="3" id="00007">
      <vertex x="1" y="1" />
    </move>
    <move colour="black" order="4" id="00008">
      <vertex x="0" y="0" />
    </move>
  </variation>

</game>

Current Thread