This implementation uses a **Cellular Automata** approach for organic cave generation, combined with a **Breadth-First Search (BFS)** to ensure all walkable areas are connected via corridors. It implements a **Bresenham-based Raycasting** engine for the Line of Sight (LoS) and a state-driven game loop.

```python
import random
import math
import time
import os
from collections import deque

# --- CONFIGURATION ---
WIDTH = 21
HEIGHT = 21
RADIUS = 5  # Line of Sight radius
WALL = "#"
FLOOR = "."
PLAYER = "@"
ENEMY = "E"
FOG = " "
VOID = "░"

class Dungeon:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.grid = []
        self.player_pos = [0, 0]
        self.enemies = []
        self.generate()

    def generate(self):
        # 1. Random Noise Initialization
        self.grid = [[WALL for _ in range(self.width)] for _ in range(self.height)]
        for y in range(self.height):
            for x in range(self.width):
                if random.random() > 0.45:
                    self.grid[y][x] = FLOOR

        # 2. Cellular Automata (Smoothing)
        for _ in range(3):
            new_grid = [row[:] for row in self.grid]
            for y in range(1, self.height - 1):
                for x in range(1, self.width - 1):
                    neighbors = 0
                    for dy in [-1, 0, 1]:
                        for dx in [-1, 0, 1]:
                            if dy == 0 and dx == 0: continue
                            if self.grid[y+dy][x+dx] == WALL:
                                neighbors += 1
                    if neighbors > 4:
                        new_grid[y][x] = WALL
                    else:
                        new_grid[y][x] = FLOOR
            self.grid = new_grid

        # 3. Ensure Connectivity (BFS to find all floors and connect them)
        self._ensure_connectivity()

        # 4. Place Player and Enemies
        floors = [(x, y) for y in range(self.height) for x in range(self.width) if self.grid[y][x] == FLOOR]
        if not floors: # Fallback
            self.grid[1][1] = FLOOR
            floors = [(1, 1)]
            
        self.player_pos = list(random.choice(floors))
        
        self.enemies = []
        for _ in range(3):
            e_pos = random.choice(floors)
            if e_pos != tuple(self.player_pos):
                self.enemies.append({'pos': list(e_pos), 'alive': True})

    def _ensure_connectivity(self):
        """Uses BFS to find disconnected floor pockets and carves corridors."""
        floors = [(x, y) for y in range(self.height) for x in range(self.width) if self.grid[y][x] == FLOOR]
        if not floors: return

        visited = set()
        components = []

        for f in floors:
            if f not in visited:
                component = []
                queue = deque([f])
                visited.add(f)
                while queue:
                    curr = queue.popleft()
                    component.append(curr)
                    for dx, dy in [(0,1),(0,-1),(1,0),(-1,0)]:
                        nx, ny = curr[0]+dx, curr[1]+dy
                        if 0 <= nx < self.width and 0 <= ny < self.height:
                            if self.grid[ny][nx] == FLOOR and (nx, ny) not in visited:
                                visited.add((nx, ny))
                                queue.append((nx, ny))
                components.append(component)

        # Connect components with simple L-shaped corridors
        for i in range(len(components) - 1):
            start = components[i][0]
            end = components[i+1][0]
            self._carve_corridor(start, end)

    def _carve_corridor(self, start, end):
        x, y = start
        tx, ty = end
        while x != tx:
            self.grid[y][x] = FLOOR
            x += 1 if tx > x else -1
        while y != ty:
            self.grid[y][x] = FLOOR
            y += 1 if ty > y else -1

    def get_line_of_sight(self):
        """Raycasting using Bresenham-like logic for visibility."""
        visible = set()
        px, py = self.player_pos
        
        for angle in range(0, 360, 2):
            rad = math.radians(angle)
            dx = math.cos(rad)
            dy = math.sin(rad)
            
            for r in range(RADIUS + 1):
                cx = int(px + dx * r)
                cy = int(py + dy * r)
                
                if 0 <= cx < self.width and 0 <= cy < self.height:
                    visible.add((cx, cy))
                    if self.grid[cy][cx] == WALL:
                        break # Blocked by wall
                else:
                    break
        return visible

class Engine:
    def __init__(self):
        self.dungeon = Dungeon(WIDTH, HEIGHT)
        self.turn_count = 0

    def render(self, auto_mode=False):
        os.system('cls' if os.name == 'nt' else 'clear')
        print(f"--- DUNGEON CRAWLER | Turn: {self.turn_count} {'(AUTO)' if auto_mode else ''} ---")
        
        visible_tiles = self.dungeon.get_line_of_sight()
        
        output = []
        for y in range(self.dungeon.height):
            row = []
            for x in range(self.dungeon.width):
                if (x, y) in visible_tiles:
                    # Check for entities
                    if [x, y] == self.dungeon.player_pos:
                        row.append(PLAYER)
                    elif any(e['pos'] == [x, y] and e['alive'] for e in self.dungeon.enemies):
                        