簡體   English   中英

Python BFS 程序不返回網格和路徑,在有障礙物和解決方案的迷宮上

[英]Python BFS program not returning grid and path, on maze with obstacles and solutions

我已經導入了一個帶有數字的文本文件,如下例所示:


3 0 0 0 0 1 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 2 3 3 3 0 3 0 0 0 0 3 3 3 0 3 0 0 0 0 3 3 3 3 0 3 2 2 0 0 3 3 3 3 3 3 3 2 0 3 3 3


目標是讀取文本文件,將其格式化為我能夠做到的網格(即 10 x 10 網格),然后對列表列表進行排序以達到數字 3 是障礙的解決方案,數字 1 是起點,數字 2 是解決方案,我正在嘗試使用 BFS 算法,其中代理可以向上、向下、向左、向右移動。

我正在嘗試打印從起點(即 1)到達最接近的解決方案(即 2)所采取的步驟序列。 數字被格式化為字符串/文本。 我編寫的程序似乎正在運行,但它從不打印解決方案或終止。 要作為解決方案打印的移動序列的格式為:


“向下移動”“向上移動”等。 每一步都在換行符上

我在下面附上了我的代碼,我們將不勝感激任何可以提供的幫助

    import queue


def read_user_input():
    file_name = input('Enter the name of your file :\n')
    return file_name


def read_to_grid():
    file_name = read_user_input()
    for nums in open(file_name):
        line = list(nums.split())
        result = []
        for _ in range(0, len(line), 10):
            result.append(line[_:_ + 10])
        return result
    file_name.close()


def print_grid(result, path=''):
    for x, pos in enumerate(result[0]):
        if pos == '0':
            start = x

    i = start
    j = 0
    pos = set()
    for move in path:
        if move == 'Move Left':
            i -= 1
        elif move == 'Move Right':
            i += 1
        elif move == 'Move Up':
            j -= 1
        elif move == 'Move Down':
            j += 1

        pos.add((j, i))
    for j, row in enumerate(result):
        for i, col in enumerate(row):
            if (j, i) in pos:
                print('#', end='')
            else:
                print(col + ' ', end='')
        print()


def valid(result, moves):
    for x, pos in enumerate(result[0]):
        if pos == '0':
            start = x

    i = start
    j = 0
    for move in moves:
        if move == 'Move Left':
            i -= 1
        elif move == 'Move Right':
            i += 1
        elif move == 'Move Up':
            j -= 1
        elif move == 'Move Down':
            j += 1
        if not (0 <= i < len(result[0]) and 0 <= j < len(result)):
            return False
        elif (result[i][j] == '3'):
            return False

    return True


def find_goal(result, moves):
    for x, pos in enumerate(result[0]):
        if pos == '0':
            start = x

    i = start
    j = 0
    for move in moves:
        if move == 'Move Left':
            i -= 1
        elif move == 'Move Right':
            i += 1
        elif move == 'Move Up':
            j -= 1
        elif move == 'Move Down':
            j += 1

    if result[j][i] == '2':
        print('Found: ' + moves)
        print_grid(result, moves)
        return True

    return False


nums = queue.Queue()
nums.put('')
add = ''
result = read_to_grid()

while not find_goal(result, add):
    add = nums.get()
    for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
        put = add + j
        if valid(result, put):
            nums.put(put)

在調試您的代碼時,當涉及到您的“有效”和“查找目標”函數時,我遇到了一些無限循環和其他錯誤。

根據我的廣度優先搜索經驗,最好將每個點視為一個節點(在這種情況下為坐標),並讓您的隊列由當前正在嘗試的路徑列表組成。 其中每條路徑是橫向的每個節點的列表。 通常,您不想在給定路徑中多次訪問同一節點,因此您必須跟蹤此信息,而不是單獨“左”、“右”等...

話雖如此,我構建了您的代碼並創建了一個 function ,當給定一個考慮網格邊界的節點時,它將返回有效的相鄰節點,而不是 3 並且該節點是否已被訪問。 然后對於 BFS 部分,隊列以包含起始節點的列表開始(我創建了一個 function 來查找 1 的位置)。 然后,當隊列存在時,BFS 將彈出當前路徑,獲取該路徑中的最后一個節點,找到所有有效的相鄰節點。 對於每個有效的相鄰節點,一個新的路徑條目將被添加到由舊路徑 + 相鄰節點組成的隊列中。 如果相鄰節點之一是目標,它將結束搜索並返回路徑。 我已經在路徑中包含了方向信息,以便您可以解析出來。

這應該打印出最接近 2 的路徑,如下所示:

[((5, 0), ''), ((5, 1), 'Down'), ((6, 1), 'Right'), ((6, 2), 'Down'), ((7, 2), 'Right'), ((7, 3), 'Down'), ((7, 4), 'Down'), ((7, 5), 'Down')]

您會看到...sorted(path_queue, key=lambda...該行不是必需的,但它是一種確定隊列優先級的懶惰方式,總是嘗試最短的當前路徑。如果刪除它,您會看到仍然得到一個有效的路徑,但它更長。

def read_user_input():
    file_name = input('Enter the name of your file :\n')
    return file_name


def read_to_grid():
    file_name = read_user_input()
    for nums in open(file_name):
        line = list(nums.split())
        result = []
        for _ in range(0, len(line), 10):
            result.append(line[_:_ + 10])

    int_result = []
    for i, row in enumerate(result):
        int_result.append([])
        for col in row:
            int_result[i].append(int(col))
    
    return int_result


def print_grid(result, path=''):
    for x, pos in enumerate(result[0]):
        if pos == 0:
            start = x

    i = start
    j = 0
    pos = set()
    for move in path:
        if move == 'Move Left':
            i -= 1
        elif move == 'Move Right':
            i += 1
        elif move == 'Move Up':
            j -= 1
        elif move == 'Move Down':
            j += 1

        pos.add((j, i))
    for j, row in enumerate(result):
        for i, col in enumerate(row):
            if (j, i) in pos:
                print('#', end='')
            else:
                print(str(col) + ' ', end='')
        print()


def find_start_node(grid):
    for i, row in enumerate(grid):
        if 1 in row:
            return ((row.index(1), i), '')

    return (None, None)

def valid_adj(cur_node, grid, visited):
    x = cur_node[0][0]
    y = cur_node[0][1]
    
    adj = []

    if ((y + 1) < 10) and (grid[y + 1][x] != 3) and  not (any((x, y + 1) in node for node in visited)):
        adj.append(((x, y + 1), 'Down'))

    if ((x + 1) < 10) and (grid[y][x + 1] != 3) and not (any((x + 1, y) in node for node in visited)):
        adj.append(((x + 1, y), 'Right'))

    if ((y - 1) >= 0) and (grid[y - 1][x] != 3) and not (any((x, y - 1) in node for node in visited)):
        adj.append(((x, y - 1), 'Up'))

    if ((x - 1) >= 0) and (grid[y][x - 1] != 3) and not (any((x - 1, y) in node for node in visited)):
        adj.append(((x - 1, y), "Left"))

    return adj

def BFS(grid):
    start_node = find_start_node(grid)
    path_queue = [[start_node]]

    while path_queue:
        path_queue = sorted(path_queue, key=lambda x: len(x), reverse=True) # More optimized to guarantee shortest path, not needed

        cur_path = path_queue.pop()
        cur_node = cur_path[-1]

        if cur_node not in cur_path[:].pop():
            adj = valid_adj(cur_node, grid, cur_path)

            for node in adj:
                new_path = list(cur_path)
                new_path.append(node)

                path_queue.append(new_path)

                if grid[node[0][1]][node[0][0]] == 2:
                    print('path found')
                    return new_path

    return -1

grid = read_to_grid()

print_grid(grid)
print(BFS(grid))

好的,Ryan 的回答已經說明了一切,但是您的代碼工作雖然效率不高,但我改變的唯一值得的是,您可以使用列表而不是使用列表列表,並且有效的 function 現在檢查行進的路徑,這樣它就可以知道它在哪里,所以它不會循環。

import queue

# Read name file from user
def read_user_input():
    file_name = input('Enter the name of your file :\n')
    return file_name

# Read file and return list of list[10]
def read_to_grid():
    with open(read_user_input()) as file:
        for nums in file:
            line = list(nums.split())
            return line

# Shows a text grid
def print_grid(result, path=[]):
    for x, pos in enumerate(result):
        if pos == '1':
            start = x

    i = start
    #j = 0
    pos = set()
    
    for move in path:
        if move == 'Move Left':
            i -= 1
        elif move == 'Move Right':
            i += 1
        elif move == 'Move Up':
            i -= 10
        elif move == 'Move Down':
            i += 10

        pos.add(i)

    for i, celd in enumerate(result):
        if i % 10 == 0:
            print()
        if i in pos:
            print('# ', end='')
        else:
            print(celd + ' ', end='')      

# Validates coordinates and traveled path
def valid(result, moves):
    for x, pos in enumerate(result):
        if pos == '1':
            start = x

    i = start % 10
    j = start // 10

    # Where we start
    travel = [(j,i)]

    for move in moves:
        if move == 'Move Left':
            i -= 1
        elif move == 'Move Right':
            i += 1
        elif move == 'Move Up':
            j -= 1
        elif move == 'Move Down':
            j += 1

        # Check if we have already been there
        if (j, i) in travel:
            return False
        else:
            travel += [(j,i)]

        # Check coordinates
        if i >= 10 or i < 0 or j >= len(result) // 10 or j < 0:
            return False
        elif result[i+j*10] == '3':
            return False

    return True

# Return true if 2 is reached
def find_goal(result, moves):
    for x, pos in enumerate(result):
        if pos == '1':
            start = x

    i = start
    #j = 0
    for move in moves:
        if move == 'Move Left':
            i -= 1
        elif move == 'Move Right':
            i += 1
        elif move == 'Move Up':
            i -= 10
        elif move == 'Move Down':
            i += 10

    if result[i] == '2':
        print('Found: ',' '.join(moves))
        print_grid(result, moves[0:-1])
        return True

    return False

nums = queue.Queue()
result = read_to_grid()
add = []

while not find_goal(result, add):
    if not nums.empty():
        add = nums.get()
    for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
        put = add + [j]
        if valid(result, put):
            nums.put(put)

編輯:

我清理了一下:

import queue

# Read name file from user
def read_user_input():
    file_name = input('Enter the name of your file :\n')
    return file_name

# Read file and return list of list[10]
def read_to_grid():
    with open(read_user_input()) as file:
        for nums in file:
            line = list(nums.split())
            return line

# Shows a text grid
def print_grid(result, path=[]):
    pos = set()
    
    for (x,y), _ in path:
        i = x + y*10
        pos.add(i)

    for i, celd in enumerate(result):
        if i % 10 == 0:
            print()
        if i in pos:
            print('# ', end='')
        else:
            print(celd + ' ', end='')      

# Validates coordinates and traveled path
def valid(result, moves):
    # Unpack
    (i,j), _ = moves[-1]

    # Check if already traveled
    if any(x == i and y == j for (x,y), __ in moves[:-1]):
        return False

    # Check coordinates
    if i >= 10 or i < 0 or j >= len(result) // 10 or j < 0:
        return False
    elif result[i+j*10] == '3':
        return False

    return True

# Return true if 2 is reached
def find_goal(result, moves):
    # Unpack
    (i,j), _ = moves[-1]

    if result[i+j*10] == '2':        
        #Print moves
        output = 'Found: '
        for (x,y), _ in moves:
            output += " "+_
        print(output)       
        #Print grid
        print_grid(result, moves[1:-1])

        return True
        
    return False

# Return new position and which movement was done.
def move(pos, dir):
    (x, y), _ = pos
    
    if dir == 'Move Left':
        x -= 1
    elif dir == 'Move Right':
        x += 1
    elif dir == 'Move Up':
        y -= 1
    elif dir == 'Move Down':
        y += 1
    
    return (x, y), dir 

nums = queue.Queue()
result = read_to_grid()

# Find the starting position
for x, pos in enumerate(result):
    if pos == '1':
        start = x

add = [((start % 10, start // 10),'')]

while not find_goal(result, add):
    if not nums.empty():
        add = nums.get()
    for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
        put = add + [move(add[-1],j)]
        if valid(result, put):
            nums.put(put)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM