The KT problem (just in case you are not familiar with it)
      involves finding a path by which a knight can visit every square
      on the chess board exactly once, returning to the square it
      originally started from. Very many such routes exist but they are
      difficult to discover by hand. 
        
| 
               | 
            
               | 
            
               | 
            
               | 
            
               | 
          
| 1 | . | . | . | 6 | 
| . | . | K | . | . | 
| 0 | . | . | . | 7 | 
| . | 2 | . | 5 | . | 
Knight's 8 potential moves as defined in the algorithm's MoveDef object
We evaluate each potential move in turn. Having recorded a new move into the array we recursively call our Move method to commence evaluation of the moves now open to us from the new square. If a move turns out to be a dead-end then we simply exit the current Move method and we fall back into the underlying previous move position. The Move method when returned to clears the last (now finished branch) move and continues to search for the next one. In this way we build up and tear down the solution search tree. We know that we have found a possible solution when our search depth (or jumpnum) reaches the number of squares on the board. However we need to confirm that the knight could reach the initial square from the final square so that we can say that it has completed the tour. The Move method then exits so that further possible solutions can be explored from branches below.
Not all solutions are unique as there will always be identical solutions around all the lines of symmetry; horizontal, vertical, diagonal and rotational. We can use this feature to avoid undertaking some unnecessary early search branches. We should always start the search from one corner (0, 0). Now from a corner square there are only two possible initial jumps, labelled 5 and 7 in the diagram above (the solution gets rendered in the SE quadrant). Given that we would only repeat solutions around a diagonal line of symmetry if we search with both move 5 and 7 as an initial move we can reduce the solution time by half if we only ever commence with move 5. An implication of this is that the last move of the tour must always be to the square that we would end up on if we make move 7 from (0,0). So we can further enhance the algorithm by reserving this square (2, 1) so that solution trees don't use this square until the last ever move. This optimization gives a 5 fold improvement in overall solution time.
Although we make an attempt at avoiding duplication of solutions from diagonal symmetry (because it works in our favour with regards to solution time) we are not protected from accidentally reproducing solutions which are in reality repeats of previous solutions about a particular line of symmetry; horizontal, vertical, the other diagonal or rotational. It would be possible to store all unique solutions as they are discovered and to compare with each 'normalized' new one to confirm that it is indeed unique. The algorithm doesn't do this at present it, there isn't really much need.
There is a further technique for pruning search trees at a point
      at which we make a move that clearly blocks a solution path. 
      For every un-visited square which we could have reached with this
      jump, but didn't because we made a different move, we consider if
      an imaginary knight situated on these squares can now reach
      another vacant square. If it can not then clearly this un-visited
      square is now isolated because if the imaginary knight can't get
      out, none can get in. Hence this square has been isolated from the
      rest of the board by this move and consequently this particular
      solution tree is now doomed. We are not going to catch all doomed
      solution trees this way because we only detect isolated squares
      not isolated regions. To detect regions would just bloat the
      solution time too much. In fact this optimization is of marginal
      value because for each jump it adds an extra 2 levels of jump
      evaluation (up to 42 extra squares to consider). We have to scan
      for the other squares we could have reached (which is (8 - the
      actual square we went to - the square we came in from) * (8 - the
      square we had been on -> 6x7 = 42). In fact as we only
      re-discover that the square we had been on is used by inspection
      we have 6x8 = 48 extra squares to consider. So if the algorithm
      for pruning found no solutions to deprecate the algorithm would
      run about 50 times slower. For a 6x6 chess board the following is
      a list of how many solution branches get pruned at each level with
      this algorithm (depth and branches pruned). 
        
| 
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
          
| 
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
          
| 
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
          
| 
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
          
| 
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
          
| 
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
            
               | 
          
We therefore need to restrict the range in which we run the
      pruning test. Early pruning is advantageous because it removes
      otherwise long branches, however it can be seen that there are not
      many low level pruning opportunities. There are very many pruning
      opportunities in the middle depth range, except there are very
      many iterations run at these levels so the overhead of running the
      test is greatly multiplied. There are two parameters hard coded in
      the source Glb.PRUNE_START & Glb.PRUNE_END,
        adjusting these in conjunction with different javac optimization
        techniques will give different levels of  performance
        improvement (not all beneficial). 
        
        
The threads are managed by an object called ThreadManager. The ThreadManager acts as a synchronization object for all thread interactions. To ensure that we don't corrupt the output file by writing two solutions simultaneously even the PrintBoard() method has to be implemented in this object. The assignment of threads to tasks is handled by 2 methods within this object. ReportForWork() is called by threads which have exhausted the solution branch that they had been following and have consequently fallen out of the bottom of the recursive Move() method stack. HireHelp() is called by threads which are exploring new solution branches and have had one or more out of work threads attached to them.
In ReportForWork() a newly redundant thread scans the currentDepth of all
        threads to find out which one is functioning at the lowest
        depth. It chooses the thread currently working at the lowest
        depth because that shall be the most likely able to offer it the
        longest career opportunity. The thread adds it's self to the
        linked list of other optimistic helper threads which may have
        attached themselves to this particular thread, then calls wait.
        When a thread which is busy evaluating moves discovers that a
        thread has tagged itself to it then instead of making the next
        move itself it calls HireHelp() to obtain a thread to do it for it. HireHelp() pulls
        the first thread off the helperThread list. It copies it's current private view of
        the chess board over to the new thread's private copy and
        pre-sets the square it wants the thread to start at. Each thread
        has a KnightsMove class of it's own for such private state
        storage. The hiring thread then calls the new hire's inbuilt interrupt() method
        to wake the thread up. If a thread with tagged helpers hits a
        roadblock then it will donate all it's own helpers to the thread
        which it itself nominates to join. When on a call to ReportForWork() it
        is discovered that all threads are laid up we know that the
        solution search is finished and that the entire program should
        terminate. 
        
        
If you are able to make any improvements to this code or find any bugs please let me know. Also I would be interested in any feedback with regards to execution speed and the best pruning parameters for different classes of machine or JVM. If I get a significantly varied set of data points I shall publish them.
The source code is supplied to be used by the individuals who download it from this site and may not be redistributed for profit. The source may be modified or redistributed for any legal non-profit purpose provided reference to the original authorship is maintained.
As is traditional - ENJOY.