Tuesday 1 May 2012

9*9 Sudoku Solution using XSLT


Steps:-
Edit the board param for putting 0 where u wan to fill the sudoku using xslt and put the actual number of puzzle at its place:-

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/"
xmlns:fn="sudoku">

<xsl:param name="board" select="(
8,1,0,  0,0,0,  7,0,3,
0,0,0,  6,0,7,  0,0,8,
9,0,2,  3,1,0,  6,0,0,

0,4,0,  0,7,3,  5,6,0,
0,0,7,  9,0,1,  2,0,0,
0,6,3,  0,4,0,  0,9,0,

0,0,4,  0,9,2,  1,0,6,
6,0,0,  5,0,4,  0,0,0,
7,0,8,  0,0,0,  0,5,9
)" as="xs:integer+"/>

<xsl:param name="verbose" select="false()" as="xs:boolean"/>

<xsl:variable name="rowStarts" select="(1, 10, 19, 28, 37, 46, 55, 64, 73)" as="xs:integer+"/>
<xsl:variable name="topLeftGroup"     select="(1, 2, 3,     10, 11, 12,  19, 20, 21)" as="xs:integer+"/>
<xsl:variable name="topGroup"         select="(4, 5, 6,     13, 14, 15,  22, 23, 24)" as="xs:integer+"/>
<xsl:variable name="topRightGroup"    select="(7, 8, 9,     16, 17, 18,  25, 26, 27)" as="xs:integer+"/>
<xsl:variable name="midLeftGroup"     select="(28, 29, 30,  37, 38, 39,  46, 47, 48)" as="xs:integer+"/>
<xsl:variable name="center"           select="(31, 32, 33,  40, 41, 42,  49, 50, 51)" as="xs:integer+"/>
<xsl:variable name="midRightGroup"    select="(34, 35, 36,  43, 44, 45,  52, 53, 54)" as="xs:integer+"/>
<xsl:variable name="bottomLeftGroup"  select="(55, 56, 57,  64, 65, 66,  73, 74, 75)" as="xs:integer+"/>
<xsl:variable name="bottomGroup"      select="(58, 59, 60,  67, 68, 69,  76, 77, 78)" as="xs:integer+"/>
<xsl:variable name="bottomRightGroup" select="(61, 62, 63,  70, 71, 72,  79, 80, 81)" as="xs:integer+"/>

<xsl:function name="fn:getRowStart" as="xs:integer+" saxon:memo-function="yes">
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:sequence select="xs:integer(floor(($index - 1) div 9) * 9) + 1"/>
</xsl:function>

<xsl:function name="fn:getRowNumber" as="xs:integer+" saxon:memo-function="yes">
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:sequence select="(($index - 1) idiv 9) + 1"/>
</xsl:function>

<xsl:function name="fn:getColNumber" as="xs:integer+" saxon:memo-function="yes">
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:sequence select="(($index - 1) mod 9) + 1"/>
</xsl:function>

<xsl:function name="fn:getRow" as="xs:integer+">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:sequence select="subsequence($board, fn:getRowStart($index), 9)"/>
</xsl:function>

<xsl:function name="fn:getCol" as="xs:integer+">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:variable name="gap" select="($index - 1) mod 9" as="xs:integer"/>
 <xsl:variable name="colIndexes" select="for $x in $rowStarts return $x + $gap" as="xs:integer+"/>
 <xsl:sequence select="for $x in $colIndexes return $board[$x]"/>
</xsl:function>

<xsl:function name="fn:getGroup" as="xs:integer*">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:choose>
  <xsl:when test="$index = $topLeftGroup">
   <xsl:sequence select="for $x in $topLeftGroup return $board[$x]"/>
  </xsl:when>
  <xsl:when test="$index = $topGroup">
   <xsl:sequence select="for $x in $topGroup return $board[$x]"/>
  </xsl:when>
  <xsl:when test="$index = $topRightGroup">
   <xsl:sequence select="for $x in $topRightGroup return $board[$x]"/>
  </xsl:when>
  <xsl:when test="$index = $midLeftGroup">
   <xsl:sequence select="for $x in $midLeftGroup return $board[$x]"/>
  </xsl:when>
  <xsl:when test="$index = $center">
   <xsl:sequence select="for $x in $center return $board[$x]"/>
  </xsl:when>
  <xsl:when test="$index = $midRightGroup">
   <xsl:sequence select="for $x in $midRightGroup return $board[$x]"/>
  </xsl:when>
  <xsl:when test="$index = $bottomLeftGroup">
   <xsl:sequence select="for $x in $bottomLeftGroup return $board[$x]"/>
  </xsl:when>
  <xsl:when test="$index = $bottomGroup">
   <xsl:sequence select="for $x in $bottomGroup return $board[$x]"/>
  </xsl:when>
  <xsl:when test="$index = $bottomRightGroup">
   <xsl:sequence select="for $x in $bottomRightGroup return $board[$x]"/>
  </xsl:when>
 </xsl:choose>
</xsl:function>

<xsl:function name="fn:getOtherValuesInTriple" as="xs:integer+" saxon:memo-function="yes">
 <xsl:param name="num" as="xs:integer"/>
 <xsl:sequence select="(xs:integer(((($num -1) idiv 3) * 3) + 1) to xs:integer(((($num - 1) idiv 3) * 3) + 3))[. != $num]"/>
</xsl:function>

<xsl:function name="fn:getRowFriends" as="xs:integer+">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:sequence select="for $x in fn:getOtherValuesInTriple($index) return $board[$x]"/>
</xsl:function>

<xsl:function name="fn:getColFriends" as="xs:integer+">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:sequence select="for $x in fn:getOtherValuesInTriple(fn:getRowNumber($index))
            return
             $board[(($x * 9) - 8) + (($index - 1) mod 9)]"/>
</xsl:function>

<xsl:function name="fn:reducePossibleValues" as="xs:integer*">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="index" as="xs:integer+"/>
 <xsl:param name="possibleValues" as="xs:integer+"/>

 <xsl:variable name="rowFriends" select="fn:getRowFriends($board, $index)" as="xs:integer+"/>
 <xsl:variable name="colFriends" select="fn:getColFriends($board, $index)" as="xs:integer+"/>

 <xsl:variable name="otherRow1" select="fn:getRow($board, 9 * fn:getOtherValuesInTriple(fn:getRowNumber($index))[1])" as="xs:integer+"/>
 <xsl:variable name="otherRow2" select="fn:getRow($board, 9 * fn:getOtherValuesInTriple(fn:getRowNumber($index))[2])" as="xs:integer+"/>

 <xsl:variable name="otherCol1" select="fn:getCol($board, fn:getOtherValuesInTriple($index)[1])" as="xs:integer+"/>
 <xsl:variable name="otherCol2" select="fn:getCol($board, fn:getOtherValuesInTriple($index)[2])" as="xs:integer+"/>

 <xsl:variable name="reducedValues" as="xs:integer*">
  <xsl:for-each select="$possibleValues">
   <xsl:if test=". = $otherCol1 and . = $otherCol2">
    <xsl:choose>
     <xsl:when test="not($colFriends = 0)">
      <xsl:sequence select="."/>
     </xsl:when>
     <xsl:when test="$colFriends != 0">
      <xsl:choose>
       <xsl:when test="$colFriends[1] = 0">
        <xsl:if test=". = $otherRow1">
         <xsl:sequence select="."/>
        </xsl:if>
       </xsl:when>
       <xsl:when test="$colFriends[2] = 0">
        <xsl:if test=". = $otherRow2">
         <xsl:sequence select="."/>
        </xsl:if>
       </xsl:when>
      </xsl:choose>
     </xsl:when>
    </xsl:choose>
   </xsl:if>
 
   <xsl:if test=". = $otherRow1 and . = $otherRow2">
    <xsl:choose>
     <xsl:when test="not($rowFriends = 0)">
      <xsl:sequence select="."/>
     </xsl:when>
     <xsl:when test="$rowFriends != 0">
      <xsl:choose>
       <xsl:when test="$rowFriends[1] = 0">
        <xsl:if test=". = $otherCol1">
         <xsl:sequence select="."/>
        </xsl:if>
       </xsl:when>
       <xsl:when test="$rowFriends[2] = 0">
        <xsl:if test=". = $otherCol2">
         <xsl:sequence select="."/>
        </xsl:if>
       </xsl:when>
      </xsl:choose>
     </xsl:when>
    </xsl:choose>
   </xsl:if>
  </xsl:for-each>
 </xsl:variable>

 <xsl:sequence select="if (count($reducedValues) = 1) then $reducedValues else $possibleValues"/>

</xsl:function>

<xsl:function name="fn:getAllowedValues" as="xs:integer*">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="index" as="xs:integer+"/>

 <xsl:variable name="possibleValues" select="(1 to 9)[not(. = (fn:getRow($board, $index), fn:getCol($board, $index), fn:getGroup($board, $index)))]" as="xs:integer*"/>

 <xsl:choose>
  <xsl:when test="count($possibleValues) > 1">
      <xsl:variable name="reducedValues" select="fn:reducePossibleValues($board, $index, $possibleValues)" as="xs:integer*"/>
 
   <xsl:sequence select="$reducedValues"/>
  </xsl:when>
  <xsl:otherwise>
   <xsl:sequence select="$possibleValues"/>
  </xsl:otherwise>
 </xsl:choose>

</xsl:function>

<xsl:function name="fn:tryValues" as="xs:integer*">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="emptyCells" as="xs:integer+"/>
 <xsl:param name="possibleValues" as="xs:integer+"/>

 <xsl:variable name="index" select="$emptyCells[1]" as="xs:integer"/>

 <xsl:variable name="newBoard" select="(subsequence($board, 1, $index - 1), $possibleValues[1], subsequence($board, $index + 1))" as="xs:integer+"/>

 <xsl:if test="$verbose">
  <xsl:message>
   <xsl:value-of select="concat('Trying ', $possibleValues[1], ' out of a possible ', string-join(for $i in $possibleValues return xs:string($i), ' '), ' at index ', $index)"/>
  </xsl:message>
 </xsl:if>

 <xsl:variable name="result" select="fn:populateValues($newBoard, subsequence($emptyCells, 2))" as="xs:integer*"/>

 <xsl:sequence select="if (empty($result)) then
                               if (count($possibleValues) > 1) then
                                 fn:tryValues($board, $emptyCells, subsequence($possibleValues, 2))
                               else
                                 ()
                             else
                               $result"/>
</xsl:function>

<xsl:function name="fn:populateValues" as="xs:integer*">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="emptyCells" as="xs:integer*"/>

 <xsl:choose>
  <xsl:when test="empty($emptyCells)">
   <xsl:message>Done!</xsl:message>
   <xsl:sequence select="$board"/>
  </xsl:when>
  <xsl:otherwise>
   <xsl:variable name="index" select="$emptyCells[1]" as="xs:integer"/>
   <xsl:variable name="possibleValues" select="fn:getAllowedValues($board, $index)" as="xs:integer*"/>
   <xsl:choose>
    <xsl:when test="count($possibleValues) = 0">
     <xsl:if test="$verbose">
      <xsl:message>! Cannot go any further !</xsl:message>
     </xsl:if>
     <xsl:sequence select="()"/>
    </xsl:when>
    <xsl:when test="count($possibleValues) > 1">
     <xsl:sequence select="fn:tryValues($board, $emptyCells, $possibleValues)"/>
    </xsl:when>
    <xsl:when test="count($possibleValues) = 1">
     <xsl:variable name="newBoard" select="(subsequence($board, 1, $index - 1), $possibleValues[1], subsequence($board, $index + 1))" as="xs:integer+"/>

     <xsl:if test="$verbose">
      <xsl:message>
       <xsl:value-of>
        <xsl:text>Only one value </xsl:text>
        <xsl:value-of select="$possibleValues[1]"/>
        <xsl:text> for index </xsl:text>
        <xsl:value-of select="$index"/>
       </xsl:value-of>
      </xsl:message>
     </xsl:if>

     <xsl:sequence select="fn:populateValues($newBoard, subsequence($emptyCells, 2))"/>
    </xsl:when>
   </xsl:choose>
  </xsl:otherwise>
 </xsl:choose>
</xsl:function>

<xsl:function name="fn:populateSingleValueCells" as="xs:integer+">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:param name="emptyCellsXML" as="element()*"/>

 <xsl:for-each select="1 to 81">
  <xsl:variable name="pos" select="." as="xs:integer"/>
  <xsl:choose>
   <xsl:when test="$emptyCellsXML[@index = $pos]/@possibleValues = 1">
    <xsl:sequence select="$emptyCellsXML[@index = $pos]/@values"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:sequence select="$board[$pos]"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:for-each>

</xsl:function>

<xsl:function name="fn:populateSingleValueCells" as="xs:integer+">
 <xsl:param name="board" as="xs:integer+"/>

 <xsl:variable name="emptyCells" select="for $x in (1 to 81) return $x[$board[$x] = 0]" as="xs:integer*"/>

 <xsl:variable name="emptyCellsXML" as="element()*">
  <xsl:for-each select="$emptyCells">
   <xsl:variable name="allowedValues" select="fn:getAllowedValues($board, .)" as="xs:integer*"/>
   <cell index="{.}" possibleValues="{count($allowedValues)}" values="{$allowedValues}"/>
  </xsl:for-each>
 </xsl:variable>

 <xsl:choose>
  <xsl:when test="$emptyCellsXML/@possibleValues = 1">
   <xsl:variable name="newBoard" select="fn:populateSingleValueCells($board, $emptyCellsXML)" as="xs:integer+"/>
   <xsl:sequence select="fn:populateSingleValueCells($newBoard)"/>
  </xsl:when>
  <xsl:otherwise>
   <xsl:sequence select="$board"/>
  </xsl:otherwise>
 </xsl:choose>

</xsl:function>

<xsl:function name="fn:solveSudoku" as="xs:integer+">
  <xsl:param name="startBoard" as="xs:integer+"/>

  <xsl:variable name="board" select="fn:populateSingleValueCells($startBoard)" as="xs:integer+"/>

  <xsl:variable name="emptyCells" select="for $x in (1 to 81) return $x[$board[$x] = 0]" as="xs:integer*"/>

  <xsl:variable name="emptyCellsXML" as="element()*">
    <xsl:for-each select="$emptyCells">
     <xsl:variable name="allowedValues" select="fn:getAllowedValues($board, .)" as="xs:integer*"/>
     <cell index="{.}" possibleValues="{count($allowedValues)}" values="{$allowedValues}"/>
    </xsl:for-each>
  </xsl:variable>

 <xsl:variable name="emptyCellsOrdered" as="xs:integer*">
  <xsl:for-each select="$emptyCellsXML">
   <xsl:sort select="@possibleValues" data-type="number" order="ascending"/>
   <xsl:sequence select="@index"/>
  </xsl:for-each>
 </xsl:variable>

 <xsl:variable name="endBoard" select="fn:populateValues($board, $emptyCells)" as="xs:integer*"/>

 <xsl:choose>
  <xsl:when test="empty($endBoard)">
   <xsl:message>! Invalid board - The starting board is not correct</xsl:message>
   <xsl:sequence select="$startBoard"/>
  </xsl:when>
  <xsl:otherwise>
   <xsl:sequence select="$endBoard"/>
  </xsl:otherwise>
 </xsl:choose>
</xsl:function>

<xsl:template match="/tt" name="maintt">
 <div>
  <xsl:variable name="emptyCellsXML" as="element()*">
   <xsl:for-each select="for $x in (1 to 81) return $x[$board[$x] = 0]">
    <cell index="{.}" possibleValues="{count(fn:getAllowedValues($board, .))}" values="{fn:getAllowedValues($board, .)}"/>
   </xsl:for-each>
  </xsl:variable>

  <xsl:for-each select="$emptyCellsXML">
   <xsl:sort select="@possibleValues" data-type="number" order="ascending"/>
   <xsl:copy-of select="."/>
  </xsl:for-each>
 </div>
</xsl:template>

<xsl:template match="/" name="main">
 <xsl:call-template name="drawResult">
  <xsl:with-param name="board" select="fn:solveSudoku($board)"/>
 </xsl:call-template>
</xsl:template>

<xsl:template name="drawResult">
 <xsl:param name="board" as="xs:integer+"/>
 <html>
  <head>
   <title>Sudoku - XSLT</title>
   <style>
    table { border-collapse: collapse;
    border: 1px solid black; }
    td { padding: 10px; }
    .norm { border-left: 1px solid #CCC;
      border-top: 1px solid #CCC;}
    .csep { border-left: 1px solid black; }
    .rsep { border-top: 1px solid black; }
   </style>
  </head>
  <body>
   <xsl:call-template name="drawBoard">
    <xsl:with-param name="board" select="$board"/>
   </xsl:call-template>
  </body>
 </html>
</xsl:template>

<xsl:template name="drawBoard">
 <xsl:param name="board" as="xs:integer+"/>
 <table>
  <xsl:for-each select="1 to 9">
   <xsl:variable name="i" select="."/>
   <tr>
    <xsl:for-each select="1 to 9">
     <xsl:variable name="pos" select="(($i - 1) * 9) + ."/>
     <td class="{if (position() mod 3 = 1) then 'csep' else ('norm')} {if ($i mod 3 = 1) then 'rsep' else ('norm')}">
      <xsl:value-of select="$board[$pos]"/>
     </td>
    </xsl:for-each>
   </tr>
  </xsl:for-each>
 </table>
</xsl:template>

<xsl:template name="drawBasicBoard">
 <xsl:param name="board" as="xs:integer+"/>
 <xsl:for-each select="1 to 9">
  <xsl:variable name="i" select="."/>

  <xsl:for-each select="1 to 9">
   <xsl:variable name="pos" select="(($i - 1) * 9) + ."/>
   <xsl:value-of select="$board[$pos]"/>
   <xsl:value-of select="if ($pos mod 3 = 0) then ',   ' else ', '"/>
  </xsl:for-each>

  <xsl:value-of select="if ($i mod 3 = 0) then '


' else '
'"/>
 </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

2 comments:

VISHNU SINGH said...
This comment has been removed by the author.
Pravesh Kumar said...

this is nice
awesome