Problem Explanation
I wanted to create a Knight's Tour in an 8x8 board and only used recursion to implement the code thus far.
The knight tour for an 8x8 board has around 19 quadrillion moves.
src = https://en.wikipedia.org/wiki/Knight%27s_tour#Number_of_tours
My perspective is that even if it reaches a square where it can no longer make a move it can move back recursively to find a solution every time without terminating.
I use 2 lists, solution_list - that stores the solution of the tour and pos_visited - that stores the positions previously visited, which don't have further possible moves.
Error
This question Knight's Tour in Python - Getting Path from Predecessors helped me to figure out where I went wrong.
Initially, there is no problem in moving to a previous position but as the list pos_visited grows the function Next_move, therefore, can't pick a move from move_set which has no moves and throws 'IndexError: Cannot choose from an empty sequence'.
I have only added the necessary code and relative functions for readability.
import random
def Start_Tour(board, solution_list, move_set, pos_visited):
# clears board
clear_board(board, solution_list, move_set, pos_visited)
# ASKS STARTING POSITION
print("Give initial coordinates to start: ")
x_initial, y_initial = first_pos(board)
board[x_initial][y_initial] = "♘"
# Displays board
display_board(board)
solution_list.append([x_initial, y_initial])
print(f"Starting position defined at {x_initial, y_initial}")
count = 0
while count < 65:
# Move_set gets valid and unoccupied coordinates in board
move_set = potential_moves(x_initial, y_initial, board)
# check if move_set is empty
if len(move_set) == 0:
# BACKTRACK if empty
print("Moving Back")
retrack(board, solution_list, pos_visited)
# Else-Place Knight
else:
# choose one move as xn,yn at random
xn, yn = Next_move(move_set, pos_visited)
# place marker in xn,yn
board[xn][yn] = "♘"
# clears previous position
clear_previous_pos(board, solution_list)
# adds position to solution_list
solution_list.append([xn, yn])
print(f"\n solution_list -> {solution_list}")
print("\n")
# displays board
display_board(board)
# sets x_initial and y_initial for generating next set of potential moves
x_initial = xn
y_initial = yn
count += 1
print(f"\n {count}")
def potential_moves(a, b, board):
"""
Takes current position of knight(a,b) and generates possible moves as a list
"""
move_set = [
[a - 1, b - 2],
[a - 2, b - 1],
[a - 2, b + 1],
[a - 1, b + 2],
[a + 1, b + 2],
[a + 2, b + 1],
[a + 2, b - 1],
[a + 1, b - 2],
]
for x, y in move_set[:]:
if x in range(0, 8) and y in range(0, 8) and board[x][y] == " ":
pass
else:
move_set.remove([x, y])
return move_set
def retrack(board, solution_list, pos_visited):
"""
helps knight to move back to previous position.
"""
x_current, y_current = solution_list.pop(-1) # x,y have no more valid moves
pos_visited.append([x_current, y_current]) # adds x,y to positions already visited
board[x_current][y_current] = " " # erases current x,y pos'n
x, y = solution_list[-1] # returns pos'n before getting stuck
if len(potential_moves(x, y, board)) != 0 and [x, y] not in pos_visited:
return x, y
else:
return retrack(board, solution_list, pos_visited)
def Next_move(move_set, pos_visited):
"""
returns a move at random from move_set if it isn't visited already.
"""
xn, yn = random.choice(move_set)
if positions_visited(xn, yn, pos_visited):
return xn, yn
else:
move_set.remove([xn, yn])
move_set1 = move_set
return Next_move(move_set, pos_visited)
def positions_visited(n1, n2, pos_visited): # Checks for position in tracker
"""
checks if position has been visited already
"""
if [n1, n2] in pos_visited:
return False
else:
return True
This is the output I get so far before an error is thrown.
P.S: I've been learning python for 2 months now and don't know much about algorithms yet. I first would like to arrive at a solution with a basic approach before trying to use algorithms. It will be very helpful if you could explain in detail if you think I should take a different approach to this.
Thank you
-Santosh
Edit: I have added the traceback error
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-22-aba4d6ed48dc> in <module>
----> 1 Start_Tour(cb,solution_list,move_set,pos_visited)
<ipython-input-18-0c959cf546f8> in Start_Tour(board, solution_list,
move_set, pos_visited)
31
32 # choose one move as xn,yn
----> 33 xn,yn = Next_move(move_set,pos_visited)
34
35 #place marker in xn,yn
<ipython-input-21-c2489e88fd1e> in Next_move(move_set, pos_visited)
9 move_set.remove([xn,yn])
10 move_set1 = move_set
---> 11 return Next_move(move_set,pos_visited)
<ipython-input-21-c2489e88fd1e> in Next_move(move_set, pos_visited)
3 #returns a move at random from move_set if it isn't visited already.
----> 4 xn,yn = random.choice(move_set)
5 if positions_visited(xn,yn,pos_visited):
6 return xn,yn
F:\Anaconda\lib\random.py in choice(self, sea)
259 i = self._randbelow(len(seq))
260 except ValueError:
--> 261 raise IndexError('Cannot choose from an empty
sequence') from None
262 return seq[i]
263
IndexError: Cannot choose from an empty sequence