from collections import deque
import heapq
from maze import MazeBuilder

from collections import deque
from maze import MazeBuilder

def build_path(previous, start, finish):
    if finish not in previous:
        return []
    path = []
    current = finish
    while current != start:
        path.append(current)
        current = previous[current]
    path.append(start)
    path.reverse()
    return path

class BfsStrategy:
    def solve(self, maze):
        start = maze.start
        finish = maze.finish
        queue = deque([start])
        previous = {start: None}
        visited_count = 0

        while queue:
            current = queue.popleft()
            visited_count += 1

            if current == finish:
                break

            for next_cell in maze.neighbors(current[0], current[1]):
                if next_cell not in previous:
                    previous[next_cell] = current
                    queue.append(next_cell)

        path = build_path(previous, start, finish)
        return {
            "name": "BFS",
            "path": path,
            "visited": visited_count,
            "length": len(path)
        }
class DfsStrategy:
    def solve(self, maze):
        start = maze.start
        finish = maze.finish
        stack = [start]
        previous = {start: None}
        visited_count = 0

        while stack:
            current = stack.pop()
            visited_count += 1

            if current == finish:
                break

            for next_cell in maze.neighbors(current[0], current[1]):
                if next_cell not in previous:
                    previous[next_cell] = current
                    stack.append(next_cell)

        path = build_path(previous, start, finish)
        return {
            "name": "DFS",
            "path": path,
            "visited": visited_count,
            "length": len(path)
        }
def distance(first, second):
    return abs(first[0] - second[0]) + abs(first[1] - second[1])

class AstarStrategy:
    def solve(self, maze):
        start = maze.start
        finish = maze.finish
        queue = []
        heapq.heappush(queue, (0, start))
        previous = {start: None}
        costs = {start: 0}
        visited_count = 0

        while queue:
            current = heapq.heappop(queue)[1]
            visited_count += 1

            if current == finish:
                break

            for next_cell in maze.neighbors(current[0], current[1]):
                new_cost = costs[current] + 1
                if next_cell not in costs or new_cost < costs[next_cell]:
                    costs[next_cell] = new_cost
                    priority = new_cost + distance(next_cell, finish)
                    heapq.heappush(queue, (priority, next_cell))
                    previous[next_cell] = current

        path = build_path(previous, start, finish)
        return {
            "name": "A*",
            "path": path,
            "visited": visited_count,
            "length": len(path)
        }

class MazeSolver:
    def __init__(self, strategy):
        self.strategy = strategy

    def solve(self, maze):
        return self.strategy.solve(maze)

if __name__ == "__main__":
    files = [
        "simple.txt",
        "medium.txt",
        "hard.txt"
    ]
    strategies = [BfsStrategy(), DfsStrategy(), AstarStrategy()]

    for filename in files:
        print("map:", filename)
        maze = MazeBuilder().from_file("raskatovia/docs/data/task2/maps/" + filename).build()
        for strategy in strategies:
            solver = MazeSolver(strategy)
            result = solver.solve(maze)
            print("algorithm:", result["name"])
            print("visited:", result["visited"])
            print("length:", result["length"])
            print(maze.draw(result["path"]))
            print()