简体   繁体   English

解决 Python 中的 8x8 递归骑士之旅

[英]Solving 8x8 Recursive Knight Tour in Python

Problem Explanation问题说明

I wanted to create a Knight's Tour in an 8x8 board and only used recursion to implement the code thus far.我想在 8x8 板上创建 Knight's Tour,到目前为止只使用递归来实现代码。

The knight tour for an 8x8 board has around 19 quadrillion moves. 8x8 棋盘的骑士巡回赛有大约 19 万亿次移动。

src = https://en.wikipedia.org/wiki/Knight%27s_tour#Number_of_tours 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.我使用 2 个列表,solution_list - 存储游览的解决方案和 pos_visited - 存储先前访问的位置,这些位置没有进一步的可能移动。

Error错误

This question Knight's Tour in Python - Getting Path from Predecessors helped me to figure out where I went wrong.这个问题Knight's Tour in Python - Getting Path from Predecessors帮助我弄清楚我哪里出错了。

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'.最初,移动到以前的 position 没有问题,但随着列表 pos_visited 的增长, function Next_move因此,无法从没有移动的 move_set 中选择移动并抛出“IndexError:无法从空序列中选择”。

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.这是 output 到目前为止我在抛出错误之前得到的。

PS: I've been learning python for 2 months now and don't know much about algorithms yet. PS:我已经学习 python 两个月了,对算法还不太了解。 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

I feel like your problem lies in this code here.我觉得你的问题在于这里的代码。 It's obviously not populating your move_set list for some reason.由于某种原因,它显然没有填充您的 move_set 列表。 IE what's happening is that the else part of your for loop is being executed for every possible move. IE 发生的事情是你的for循环的else部分正在为每一个可能的移动执行。 Which makes sense because judging by your board, there is no possible moves for the knight to make.这是有道理的,因为从你的棋盘来看,骑士不可能采取任何行动。 Each board[x][y] does not equal ' ' .每个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

Edit: As has been pointed out by the comments on your post.编辑:正如您帖子中的评论所指出的那样。 It looks like the knight has no moves to make because of cb is not defined in your retrack function, which I assume is meant to prevent the knight from having no possible moves.看起来骑士没有动作可做,因为在您的retrack中没有定义cb ,我认为这是为了防止骑士没有可能的动作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM