用Python解决8x8递归骑士之旅

时间:2020-07-30 12:06:30

标签: python recursion knights-tour

问题说明

我想在8x8板上创建Knight's Tour,到目前为止只使用了递归来实现代码。

8x8棋盘的骑士之旅大约有19百万次动作。

src = https://en.wikipedia.org/wiki/Knight%27s_tour#Number_of_tours

我的观点是,即使到达一个不能再移动的正方形,它也可以每次都递归地返回以找到解决方案而不会终止。

我使用2个列表solution_list-存储旅行的解决方案 和pos_visited-存储以前访问的位置,这些位置没有进一步的移动。

错误

这个问题Knight's Tour in Python - Getting Path from Predecessors帮助我弄清楚哪里出了问题。

最初,移动到上一个位置没有问题,但是随着pos_visited列表增加功能 Next_move ,因此,无法从没有移动的move_set中选择移动并抛出'IndexError :无法从空序列中进行选择。”

我只添加了必要的代码和相关功能以提高可读性。

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:我已经学习Python 2个月了,对算法的了解还不多。在尝试使用算法之前,我首先想提供一种具有基本方法的解决方案。如果您可以详细解释是否认为我应该采用其他方法,这将非常有帮助。

谢谢

-桑托什

编辑:我添加了追溯错误

    
    ---------------------------------------------------------------------------
    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

1 个答案:

答案 0 :(得分:0)

我觉得您的问题出在这里的代码中。由于某种原因,显然没有填充您的move_set列表。也就是说,正在发生的情况是else循环的for部分正在为每个可能的动作执行。这是有道理的,因为根据董事会的判断,骑士没有可能采取任何行动。每个board[x][y]不等于' '

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

编辑:正如您的帖子中的评论所指出的那样。看来骑士cb没有在您的retrack函数中定义,因此无法做出任何动作,我认为这是为了防止骑士没有做出任何动作。