Solve 8-Puzzle Problem with Python and A* Search

Write a Program to Implement 8-Puzzle problem using Python

import heapq
from termcolor import colored

# Class to represent the state of the 8-puzzle
class PuzzleState:
    def __init__(self, board, parent, move, depth, cost):
        self.Board = board  # The puzzle board configuration
        self.Parent = parent  # Parent state
        self.Move = move  # Move to reach this state
        self.Depth = depth  # Depth in the search tree
        self.Cost = cost  # Cost (depth + heuristic)

    def __lt__(self, other):
        return self.Cost < other.Cost

# Function to display the board in a visually appealing format
def print_board(board):
    print(“+—+—+—+”)
    for row in range(0, 9, 3):
        row_visual = “|”
        for tile in board[row:row + 3]:
            if tile == 0:  # Blank tile
                row_visual += f” {colored(‘ ‘, ‘cyan’)} |”
            else:
                row_visual += f” {colored(str(tile), ‘yellow’)} |”
        print(row_visual)
        print(“+—+—+—+”)

# Goal state for the puzzle
goal_state = [1, 2, 3, 4, 5, 6, 7, 8, 0]

# Possible moves for the blank tile (up, down, left, right)
moves = {
    ‘U’: -3,  # Move up
    ‘D’: 3,   # Move down
    ‘L’: -1,  # Move left
    ‘R’: 1    # Move right
}

# Function to calculate the heuristic (Manhattan distance)
def heuristic(board):
    distance = 0
    for i in range(9):
        if board[i] != 0:
            x1, y1 = divmod(i, 3)
            x2, y2 = divmod(board[i] – 1, 3)
            distance += abs(x1 – x2) + abs(y1 – y2)
    return distance

# Function to get the new state after a move
def move_tile(board, move, blank_pos):
    new_board = board[:]
    new_blank_pos = blank_pos + moves[move]
    new_board[blank_pos], new_board[new_blank_pos] = new_board[new_blank_pos], new_board[blank_pos]
    return new_board

# A* search algorithm
def a_star(start_state):
    open_list = []
    closed_list = set()
    heapq.Heappush(open_list, PuzzleState(start_state, None, None, 0, heuristic(start_state)))

    while open_list:
        current_state = heapq.Heappop(open_list)

        if current_state.Board == goal_state:
            return current_state

        closed_list.Add(tuple(current_state.Board))

        blank_pos = current_state.Board.Index(0)

        for move in moves:
            if move == ‘U’ and blank_pos < 3:  # Invalid move up
                continue
            if move == ‘D’ and blank_pos > 5:  # Invalid move down
                continue
            if move == ‘L’ and blank_pos % 3 == 0:  # Invalid move left
                continue
            if move == ‘R’ and blank_pos % 3 == 2:  # Invalid move right
                continue

            new_board = move_tile(current_state.Board, move, blank_pos)

            if tuple(new_board) in closed_list:
                continue

            new_state = PuzzleState(new_board, current_state, move, current_state.Depth + 1, current_state.Depth + 1 + heuristic(new_board))
            heapq.Heappush(open_list, new_state)

    return None

# Function to print the solution path
def print_solution(solution):
    path = []
    current = solution
    while current:
        path.Append(current)
        current = current.Parent
    path.Reverse()

    for step in path:
        print(f”Move: {step.Move}”)
        print_board(step.Board)

# Initial state of the puzzle
initial_state = [1, 2, 3, 4, 0, 5, 6, 7, 8]

# Solve the puzzle using A* algorithm
solution = a_star(initial_state)

# Print the solution
if solution:
    print(colored(“Solution found:”, “green”))
    print_solution(solution)
else:
    print(colored(“No solution exists.”, “red”))