from dataclasses import dataclass
from typing import List, Optional

@dataclass(frozen=True)
class Cell:
    x: int
    y: int
    isWall: bool = False
    isStart: bool = False
    isExit: bool = False
    weight: int = 1

    def isPassable(self) -> bool:
        return not self.isWall


class Maze:
    def __init__(self, grid: List[List[Cell]]):
        self.grid = grid
        self.height = len(grid)
        self.width = len(grid[0]) if self.height else 0
        self.start: Optional[Cell] = None
        self.exit: Optional[Cell] = None

        for row in grid:
            for cell in row:
                if cell.isStart:
                    self.start = cell
                if cell.isExit:
                    self.exit = cell

    def getCell(self, x: int, y: int) -> Optional[Cell]:
        if 0 <= y < self.height and 0 <= x < self.width:
            return self.grid[y][x]
        return None

    def getNeighbors(self, cell: Cell) -> List[Cell]:
        result = []
        for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
            nxt = self.getCell(cell.x + dx, cell.y + dy)
            if nxt is not None and nxt.isPassable():
                result.append(nxt)
        return result

    def render(self, path=None, player_position=None) -> str:
        path_set = {(c.x, c.y) for c in path} if path else set()
        player_xy = (player_position.x, player_position.y) if player_position else None

        lines = []
        for y in range(self.height):
            row = []
            for x in range(self.width):
                c = self.grid[y][x]
                if player_xy == (x, y):
                    row.append("P")
                elif c.isStart:
                    row.append("S")
                elif c.isExit:
                    row.append("E")
                elif (x, y) in path_set:
                    row.append(".")
                elif c.isWall:
                    row.append("#")
                else:
                    row.append(" ")
            lines.append("".join(row))
        return "\n".join(lines)