---------------------------------
# 8 puzzle using LDDLS
--------------------------------
import copy

GOAL_STATE = [[1,2,3] , [4,5,6], [7,8,0]]

class PuzzleStates:
    def __init__(self , board, parent = None, move = ""):
        self.board = board
        self.parent = parent
        self.move = move
        self.depth = 0
        if parent:
            self.depth = parent.depth + 1

    def __eq__(self, other):
        return self.board == other.board

    def find_blank(self):
        for i in range(3):
            for j in range(3):
                if self.board[i][j] == 0:
                    return i ,j
    def get_possible_moves(self):
        moves = []
        row,col = self.find_blank()

        directions =  [('UP', -1, 0), ('DOWN', 1, 0), ('LEFT', 0, -1), ('RIGHT', 0, 1)]

        for direction, dr, dc in directions:
            new_row, new_col = row + dr, col + dc
            if 0 <= new_row < 3 and 0 <= new_col < 3:
                new_board = copy.deepcopy(self.board)
                new_board[row][col], new_board[new_row][new_col] = new_board[new_row][new_col], new_board[row][col]
                moves.append(PuzzleStates(new_board, self, direction)) # Changed to PuzzleStates
        return moves

    def is_goal(self):
        return self.board == GOAL_STATE


def depth_limited_search(state, depth_limit):
    if state.is_goal():
        return state
    if depth_limit <= 0:
        return None

    for move in state.get_possible_moves():
        result = depth_limited_search(move, depth_limit - 1)
        if result:
            return result
    return None

def iterative_deepening_dls(initial_state):
    depth = 0
    while True:
        result = depth_limited_search(initial_state, depth)
        if result:
            return result, depth
        depth += 1

def print_board(board):
    for row in board:
        print(row)

def print_solution(solution_state):
    path = []
    current = solution_state
    while current:
        path.append(current)
        current = current.parent

    path.reverse()

    for i, state in enumerate(path):
        if i == 0:
            print("INITIAL STATE:")
        else:
            print(f"\nStep {i}: Move {state.move}")
        print_board(state.board)
    print(f"\nSolved in {len(path)-1} moves!")

if __name__ == "__main__":
    initial_board = [
        [1, 2, 3],
        [4, 0, 6],
        [7, 5, 8]
    ]

    print("INITIAL PUZZLE STATE:")
    print_board(initial_board)

    initial_state = PuzzleStates(initial_board) # Changed to PuzzleStates

    print("SOLVING")
    solution, depth = iterative_deepening_dls(initial_state)

    if solution:
        print("\nSOLUTION FOUND!")
        print_solution(solution)
    else:
        print("NO SOLUTION FOUND")