0

I am trying to implement a knight's tour finder in python. Assuming the knight must start at the top left corner (called (0,0) here), it finds a solution for a 4x3 field, but not for any other fields.

def maneuvrability(position, path, width, height):
    if position[0] < width and position[1] < height and position not in path and position[0] >= 0 and position[1] >= 0:
        return True
    else:
        return False
        
def completedness(path,width,height):
    if len(path) == (width*height):
        return True
    else:
        return False
    
def possible_jumps(pos):
    return_list = []
    return_list.extend([
        (pos[0]-1,pos[1]-2),
        (pos[0]+1,pos[1]-2),
        (pos[0]+2,pos[1]-1),
        (pos[0]+2,pos[1]+1),
        (pos[0]-1,pos[1]+2),
        (pos[0]+1,pos[1]+2),
        (pos[0]-2,pos[1]-1),
        (pos[0]-2,pos[1]+1)])
    return return_list
    
def knights_tour(width,height,path=[(0,0)]):
    if completedness(path,width,height):
        return path
    else:
        elem = path[len(path)-1]
        succs = []
        succs.extend(possible_jumps(elem))
        for x in succs:
            if maneuvrability(x,path,width,height):
                return knights_tour(width,height,[y for y in path + [x]])
    
print(knights_tour(4,3))
print(knights_tour(5,5))
Barmar
  • 741,623
  • 53
  • 500
  • 612
PeterPefi
  • 87
  • 1
  • 1
  • 7
  • 1
    What's the point of extending an empty list? Why not just assign the extend argument directly to `return_list`? – Barmar Jun 23 '21 at 16:33
  • No, absolutely not :D I looked at the wiki site before but it seems it doesn't say anything about a 0,0 start. – PeterPefi Jun 23 '21 at 16:33
  • Ignore my previous comment, Wikipedia says "Cull et al. and Conrad et al. proved that on any rectangular board whose smaller dimension is at least 5, there is a (possibly open) knight's tour." So there should be a solution for 5x5. – Barmar Jun 23 '21 at 16:34
  • How would I assign the argument directly? I thought you can't use operations like that in a return statement – PeterPefi Jun 23 '21 at 16:36
  • 1
    `return_list = [(pos[0]-1, pos[1]-2), (pos[0]+1, pos[1]-2), ...]` – Barmar Jun 23 '21 at 16:37
  • 1
    And `succs = possible_jumps(elem)` – Barmar Jun 23 '21 at 16:37
  • 1
    And there's no need for the list comprehension here: `[y for y in path + [x]]` Just use `path + [x]` – Barmar Jun 23 '21 at 16:37
  • To figure out the problem, you should add print statements so you see all the steps it's taking. – Barmar Jun 23 '21 at 16:38
  • Aight I'll change those. Doesn't solve the algorithm though, I fear ^^ – PeterPefi Jun 23 '21 at 16:39
  • No, it just simplifies the code. I also suggest you do something like `x, y = pos` so you don't have to keep repeating the indexes. – Barmar Jun 23 '21 at 16:39
  • Okay, I'm pretty sure the error is with the actual backtracking. The 4x3 field is just lucky enough to go through on the very first try on all steps – PeterPefi Jun 23 '21 at 16:51

1 Answers1

1

Your backtracking is not correct. At every step, you are only checking if the next move is valid, and then returning whether or not that move results in a knight's tour. Instead, you need to adapt the code to check all valid moves, and then see if any of them resulted in a complete knight's tour.

  • Would you mind to elaborate? I thought I was basically doing this with the for-loop for all eligible successors. – PeterPefi Jun 23 '21 at 17:24
  • Okay. I now understand what the problem is, thank you! Still need to figure out a solution, but this helped :) – PeterPefi Jun 23 '21 at 17:30
  • 1
    @PeterPefi I would have provided a full solution, but I would've had to kind of rewrite all of your code because it is messy and a little hard to comprehend, kind of like Barmar suggested in comments. It is not a criticism, just a heads up that it helps to present clean code to get the best help possible. – Jerry Halisberry Jun 23 '21 at 21:08
  • 1
    But anyways, a simple explanation of the psuedo-code code is like this: For all possible moves: check if (path + move) results in a full knight's tour. If it does, return (path + move). If it is not, try the next move. If none of the moves work, report failure to the previous step. – Jerry Halisberry Jun 23 '21 at 21:10
  • I have managed to solve the problem, thank you. ( I swear I am not writing confusing code on purpose) – PeterPefi Jun 25 '21 at 09:49
  • @PeterPefi Nice! And haha, don't worry, I am just letting you know so that you can get better help in the future :) – Jerry Halisberry Jun 25 '21 at 17:22