Python 用A-star搜索算法求解N-难题

Python 用A-star搜索算法求解N-难题,python,a-star,puzzle,sliding-tile-puzzle,state-space,Python,A Star,Puzzle,Sliding Tile Puzzle,State Space,我正在用Python中的*为我的人工智能课程制作一个n字谜解算器。我的解决方案存在的问题是,solve_puzzle()没有正常工作。当搜索节点时,它会变得越来越深,但永远不会结束,深度会变得非常高(例如,某个子节点的深度是5k) 我认为它陷入了某种循环中,并且一直循环通过相同的节点(这正是我所想的,我不确定)。我真的不知道,我尝试了3种不同的A*解决方案,但结果是一样的——搜索循环永远不会结束,也永远不会达到目标 我可以解决非常原始的难题,如 开始->0,1,2,3,4,5,6,7,8目标->

我正在用Python中的*为我的人工智能课程制作一个n字谜解算器。我的解决方案存在的问题是,solve_puzzle()没有正常工作。当搜索节点时,它会变得越来越深,但永远不会结束,深度会变得非常高(例如,某个子节点的深度是5k)

我认为它陷入了某种循环中,并且一直循环通过相同的节点(这正是我所想的,我不确定)。我真的不知道,我尝试了3种不同的A*解决方案,但结果是一样的——搜索循环永远不会结束,也永远不会达到目标

我可以解决非常原始的难题,如
开始->0,1,2,3,4,5,6,7,8
目标->1,2,0,3,4,5,6,7,8(只向左移动两步)
但是对于像
这样的更复杂的谜题 开始=0,1,2,3,4,5,6,7,8
目标=1,2,5,0,3,4,6,7,8这是一个永无止境的搜索

< >我用数字移动瓷砖,而不是空白瓷砖。

以下是全部代码:

'''
class Node():

    def __init__(self, n_size, m_size, current_state, goal_state, choosen_heuristic, parent):
        self.n_size = n_size
        self.m_size = m_size
        self.dimension = self.n_size * self.m_size
        self.current_state = current_state
        self.goal_state = goal_state
        self.choosen_heuristic = choosen_heuristic
        self.parent = parent
        self.child = [None, None, None, None]
        self.last_operator = None
        self.heuristic_cost = 0
        self.depth = 0
        self.priority = self.depth + self.heuristic_cost

    def check_if_goal_is_reached(self):
        if (self.heuristic_cost == 0):
            print("GOAL IS REACHED!")
            exit(1)                                         #for now
            #return

    def get_blank_tile_position(self):
        blank_position = 0
        for i in range(self.dimension):
            if (self.current_state[i] == 0):
                blank_position = i
        return blank_position

    def misplaced_tiles_heuristic(self):
        misplaced_sum = 0
        for i in range(self.dimension):
            if self.current_state[i] != self.goal_state[i]:
                misplaced_sum += 1
        #print("Count of misplaced tiless in this node is : ", misplaced_sum)
        self.heuristic_cost = "misplaced"
        self.heuristic_cost = misplaced_sum
        self.check_if_goal_is_reached()
        return misplaced_sum

    def manhattan_distance(self):
        distance_sum = 0
        for i in range(self.dimension):
            current_x = self.current_state[i] % self.n_size
            goal_x = self.goal_state[i] % self.n_size
            current_y = self.current_state[i] // self.m_size
            goal_y = self.goal_state[i] // self.m_size
            distance_sum += abs(current_x - goal_x) + abs(current_y - goal_y)
        #print("Sum of Manhattan distance for this node is : ", distance_sum)
        self.heuristic_cost = "manhattan"
        #print("Hĺbka tohto uzla : ", self.depth)
        self.check_if_goal_is_reached()
        return distance_sum

    def generate_children(self, choosen_heuristic):
        possible_directions = []
        current_node_blank_position = self.get_blank_tile_position()
        # UP - I move a tile with number on it, not the blank tile
        if current_node_blank_position < (self.dimension - self.n_size):
            self.child[0] = Node(self.n_size, self.m_size, self.current_state, self.goal_state, self.choosen_heuristic, self.current_state)
            self.child[0] = copy.deepcopy(self)
            self.child[0].parent = self.current_state
            self.child[0].last_operator = "UP"
            self.child[0].depth = self.depth + 1

            new_blank_position = current_node_blank_position + self.m_size

            temp = self.child[0].current_state[current_node_blank_position]
            self.child[0].current_state[current_node_blank_position] =  self.child[0].current_state[new_blank_position]
            self.child[0].current_state[new_blank_position] = temp

            if choosen_heuristic == "misplaced":
                self.child[0].misplaced_tiles_heuristic()
            if choosen_heuristic == "manhattan":
                self.child[0].manhattan_distance()

            possible_directions.append("UP")
            print("Depth of this node is : : ", self.child[0].depth)
        else:           
            self.child[0] = None
        # DOWN - I move a tile with number on it, not the blank tile
        if current_node_blank_position > (self.n_size - 1):
            self.child[1] = Node(self.n_size, self.m_size, self.current_state, self.goal_state, self.choosen_heuristic, self.current_state)
            self.child[1] = copy.deepcopy(self)
            self.child[1].parent = self.current_state
            self.child[1].last_operator = "DOWN"
            self.child[1].depth = self.depth + 1

            new_blank_position = current_node_blank_position - self.m_size

            temp = self.child[1].current_state[current_node_blank_position]
            self.child[1].current_state[current_node_blank_position] =  self.child[1].current_state[new_blank_position]
            self.child[1].current_state[new_blank_position] = temp

            if choosen_heuristic == "misplaced":
                self.child[1].misplaced_tiles_heuristic()
            if choosen_heuristic == "manhattan":
                self.child[1].manhattan_distance()

            possible_directions.append("DOWN")
            #print("Depth of this node is : : ", self.child[1].depth)
        else:           
            self.child[1] = None
        # RIGHT - I move a tile with number on it, not the blank tile
        if (current_node_blank_position + self.n_size) % self.m_size:
            self.child[2] = Node(self.n_size, self.m_size, self.current_state, self.goal_state, self.choosen_heuristic, self.current_state)
            self.child[2] = copy.deepcopy(self)
            self.child[2].parent = self.current_state
            self.child[2].last_operator = "RIGHT"
            self.child[2].depth = self.depth + 1

            new_blank_position = current_node_blank_position - 1

            temp = self.child[2].current_state[current_node_blank_position]
            self.child[2].current_state[current_node_blank_position] =  self.child[2].current_state[new_blank_position]
            self.child[2].current_state[new_blank_position] = temp

            if choosen_heuristic == "misplaced":
                self.child[2].misplaced_tiles_heuristic()
            if choosen_heuristic == "manhattan":
                self.child[2].manhattan_distance()

            possible_directions.append("RIGHT")
            #print("Depth of this node is : : ", self.child[2].depth)
        else:           
            self.child[2] = None
        # LEFT - I move a tile with number on it, not the blank tile
        if (current_node_blank_position + 1) % self.m_size:
            self.child[3] = Node(self.n_size, self.m_size, self.current_state, self.goal_state, self.choosen_heuristic, self.current_state)
            self.child[3] = copy.deepcopy(self)
            self.child[3].parent = self.current_state
            self.child[3].last_operator = "LEFT"
            self.child[3].depth = self.depth + 1

            new_blank_position = current_node_blank_position + 1

            temp = self.child[3].current_state[current_node_blank_position]
            self.child[3].current_state[current_node_blank_position] =  self.child[3].current_state[new_blank_position]
            self.child[3].current_state[new_blank_position] = temp

            if choosen_heuristic == "misplaced":
                self.child[3].misplaced_tiles_heuristic()
            if choosen_heuristic == "manhattan":
                self.child[3].manhattan_distance()

            possible_directions.append("LEFT")
            #print("Depth of this node is : ", self.child[3].depth)
        else:           
            self.child[3] = None

        #print("From this node (the parent node) I can move to -> ", possible_directions)


def get_node_priority(node):
    return node.priority

def solve_puzzle(n_size, m_size, current_state, goal_state, choosen_heuristic):
    open_list = []
    closed_list = []
    init_node_parent = None
    init_node = Node(n_size, m_size, current_state, goal_state, choosen_heuristic, init_node_parent)
    open_list.append(init_node)

    while len(open_list) != 0:
        if (len(open_list) == 0):
            print("Fail - solution not found !")
        else:
            node = open_list.pop(0)

            if (node.parent != None):
                node.check_if_goal_is_reached()

            node.generate_children(choosen_heuristic)
            closed_list.append(node)

            temp_list = []
            for i in range(4):
                if node.child[i] != None:
                    temp_list.insert(0, node.child[i])
            sorted_temp_list = sorted(temp_list, key=get_node_priority, reverse=True)
            for x in range(len(sorted_temp_list)):
                if sorted_temp_list[x] != None:
                    open_list.insert(0, sorted_temp_list[x])

def print_current_node(current_node):
    puzzle = ""
    if current_node is None:
        puzzle = ""
        print(puzzle)
    else:
        puzzle = ""
        count_lines = 0
        for i in range(current_node.dimension):
            puzzle +=  (str(current_node.current_state[i]) + " ")
            count_lines += 1
            if (count_lines % current_node.m_size == 0):
                puzzle += "\n"
        print(puzzle)


def main():

    ###################
    #   Static Data   #
    ###################
    # static for now, later I will let user to choose
    n_size = 3
    m_size = 3
    current_state = [0, 1, 2, 3, 4, 5, 6, 7, 8]
    goal_state = [8, 0, 6, 5, 4, 7, 2, 3, 1]
    choosen_heuristic = "manhattan"
    start_time = time.process_time()
    solve_puzzle(n_size, m_size, current_state, goal_state, choosen_heuristic)
    end_time = time.process_time()
    search_time = end_time - start_time
    print("Solved in : ", search_time, "seconds.")

main()
'''
“”
类节点():
定义初始(自我、n大小、m大小、当前状态、目标状态、选择启发式、父项):
self.n\u size=n\u size
self.m_size=m_size
self.dimension=self.n\u大小*self.m\u大小
self.current_state=当前_状态
self.goal\u state=目标状态
self.choosen\u启发式=choosen\u启发式
self.parent=parent
self.child=[无,无,无,无]
self.last_运算符=无
self.u成本=0
self.depth=0
self.priority=self.depth+self.lu成本
def检查是否达到目标(自我):
如果(自我启发成本==0):
打印(“达到目标!”)
退出(1)#暂时退出
#返回
def get_blank_tile_位置(自身):
空白位置=0
对于范围内的i(自身尺寸):
如果(自身当前状态[i]==0):
空白位置=i
返回空白位置
def错误放置的瓷砖启发式(自我):
放错位置的总和=0
对于范围内的i(自身尺寸):
if self.current_state[i]!=自我目标状态[i]:
错位_和+=1
#打印(“此节点中错位平铺的计数为:”,错位总和)
self.heuristic_cost=“错位”
self.heuristic\u cost=放错位置的\u和
自我检查。检查是否达到目标()
返回错放的金额
def曼哈顿_距离(自身):
距离之和=0
对于范围内的i(自身尺寸):
当前\u x=自身。当前\u状态[i]%self.n\u大小
目标x=自我。目标状态[i]%self.n\u大小
current\u y=self.current\u state[i]//self.m\u size
goal\u y=self.goal\u state[i]//self.m\u size
距离总和+=abs(当前x-目标x)+abs(当前y-目标y)
#打印(“此节点的曼哈顿距离之和为:”,距离\u之和)
self.u成本=“曼哈顿”
#印刷品(“Hĺbka tohto uzla:”,自我深度)
自我检查。检查是否达到目标()
返回距离
def生成子项(自我、选择):
可能的方向=[]
当前\u节点\u blank\u position=self.get\u blank\u tile\u position()
我用一个数字移动一个瓦片,而不是空白的瓦片。
如果当前节点\u空白\u位置<(self.dimension-self.n\u size):
self.child[0]=节点(self.n\u大小、self.m\u大小、self.current\u状态、self.goal\u状态、self.choosen\u启发式、self.current\u状态)
self.child[0]=copy.deepcopy(self)
self.child[0]。parent=self.current\u状态
self.child[0]。last_operator=“UP”
self.child[0]。深度=self.depth+1
新建\u空白\u位置=当前\u节点\u空白\u位置+self.m\u大小
temp=self.child[0]。当前\u状态[当前\u节点\u空白\u位置]
self.child[0]。当前\u状态[当前\u节点\u空白\u位置]=self.child[0]。当前\u状态[新\u空白\u位置]
self.child[0]。当前\u状态[新\u空白\u位置]=临时
如果选择启发式==“错位”:
self.child[0]。放置错误的\u tiles\u启发式()
如果选择启发式==“曼哈顿”:
self.child[0].曼哈顿距离()
可能的方向。附加(“向上”)
打印(“此节点的深度为::”,self.child[0].Depth)
其他:
self.child[0]=无
我向下移动一个有数字的瓦片,而不是空白的瓦片。
如果当前节点\u空白\u位置>(self.n\u大小-1):
self.child[1]=节点(self.n\u大小、self.m\u大小、self.current\u状态、self.goal\u状态、self.choosen\u启发式、self.current\u状态)
self.child[1]=copy.deepcopy(self)
self.child[1]。parent=self.current\u state
self.child[1]。last_operator=“DOWN”
self.child[1]。深度=self.depth+1
新建\u空白\u位置=当前\u节点\u空白\u位置-self.m\u大小
temp=self.child[1]。当前\u状态[当前\u节点\u空白\u位置]
self.child[1]。当前\u状态[当前\u节点\u空白\u位置]=self.child[1]。当前\u状态[新\u空白\u位置]
self.child[1]。当前\u状态[新\u空白\u位置]=临时
如果选择启发式==“错位”:
self.child[1]。放错位置的\u tiles\u启发式()
如果选择启发式==“曼哈顿”:
self.child[1].曼哈顿距离()
可能的方向。附加(“向下”)
#打印(“此节点的深度为::”,self.child[1].Depth)
其他:
self.child[1]=无
我用一个数字移动一个瓦片,而不是空白的瓦片。
如果(当前节点\u空白\u位置+self.n\u大小)%self.m\u大小:
self.child[2]=节点(self.n\u大小、self.m\u大小、self.current\u状态、self.goal\u状态、self.choosen\u启发式、self.current\u状态)
    goal:     {1,2,3,4,5,6,7,8,0}
    6 steps:  {1,3,5,4,0,2,7,8,6}
    18 steps: {1,4,0,5,2,8,7,6,3}
    26 steps: {2,1,7,5,0,8,3,4,6}
    27 steps: {8,5,3,4,7,0,6,1,2}
    28 steps: {0,6,7,3,8,5,4,2,1}
    30 steps: {5,7,0,4,6,8,1,2,3}
    31 steps: {8,6,7,2,5,4,3,0,1}  (highest number of steps possible for 3x3 )