pythonrecursionbacktracking

Backtracking in python - The Knight’s tour problem


Problem Statement: Given a N*N board with the Knight placed on the first block of an empty board. Moving according to the rules of chess knight must visit each square exactly once. Print the order of each cell in which they are visited on the chess board. If there are more than one solutions possible, print all of them.

Input :
N = 8
Output:
0  59  38  33  30  17   8  63
37  34  31  60   9  62  29  16
58   1  36  39  32  27  18   7
35  48  41  26  61  10  15  28
42  57   2  49  40  23   6  19
47  50  45  54  25  20  11  14
56  43  52   3  22  13  24   5
51  46  55  44  53   4  21  12

I tried a recursive solution using backtracking but do not quite understand what could be the problem with this piece of code. Could someone please help. Thanks a lot!

for n=2,3,4 it produces an empty list which is expected. and for n=5, it produces the following unexpected output:

[[[0, '.', '.', '.', '.'], ['.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.']], [[0, '.', '.', '.', '.'], ['.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.']]] ..continues

for n=8, the program keeps running and does not seem to produce an output for a long time

def knight(n):
  xy_movements = [(2,1),(2,-1),(-2,1),(-2,-1),(1,2),(-1,2),(1,-2),(-1,-2)]
  board = [["."]*n for _ in range(n)]
  board[0][0]=0
  current_x=0
  current_y=0
  result=[]

  def backtrack(num):    
    nonlocal n,current_x,current_y
    if num==(n*n)-1:
      result.append(board)
      return
    else:
      for move in xy_movements:
        if current_x+move[0]<0 or current_x+move[0]>=n or\
           current_y+move[1]<0 or current_y+move[1]>=n or\
           board[current_x+move[0]][current_y+move[1]] !=".":
          continue
        else:
          current_x+=move[0]
          current_y+=move[1]
          board[current_x][current_y] = num+1
          backtrack(num+1)
        
        board[current_x][current_y] = "."
        current_x -= move[0]
        current_y -= move[1]

  backtrack(0)
  return result

Solution

  • The problem is that result.append(board) will append the only board you have, while your backtracking algorithm will continue to update that board, and at the very end of the algorithm will have taken back all moves it did. So then board is empty, except for the first 0 that was placed and never removed. This means that in result you now reference an almost empty board, even though solutions were found.

    The solution is simple: take a copy of the board, and append that board to the result list:

                result.append([row[:] for row in board])
    

    This will solve the issue you mention when calling knight(5). For boards larger than 6, you will just have a problem that is too great: there are 19,591,828,170,979,904 solutions for an 8x8 board, so don't hope to get that in a few seconds. Your PC will probably not have enough memory to collect that many boards, let be you can wait for it.