"""
Demonstration Vibe-Coding:
Klicke wiederholt irgendwo auf die schwarze Fläche.
"""

import random
import turtle
from collections import deque

# Kleinere Rasterzellen ergeben feinere, verschnörkelte Wege.
STEP = 14
WIDTH = 700
HEIGHT = 500
DELAY = 8

screen = turtle.Screen()
screen.setup(WIDTH, HEIGHT)
screen.bgcolor("#10141f")
screen.title("Turtle-Labyrinth – Klick zum Bewegen, Q zum Beenden")
screen.tracer(0, 0)

runner = turtle.Turtle()
runner.shape("turtle")
runner.color("#65ffb8")
runner.pencolor("#65ffb8")
runner.pensize(2)
runner.speed(0)
runner.penup()
runner.goto(0, 0)
runner.pendown()

# Begrenztes Spielfeld als unsichtbares Raster
MAX_X = WIDTH // (2 * STEP) - 2
MAX_Y = HEIGHT // (2 * STEP) - 2

ALL_CELLS = {
    (x, y)
    for x in range(-MAX_X, MAX_X + 1)
    for y in range(-MAX_Y, MAX_Y + 1)
}

current = (0, 0)
visited = {current}

target = None
pending_target = None
moving = False
last_direction = None


def neighbors(point):
    """Liefert alle Nachbarzellen innerhalb des Spielfelds."""
    x, y = point

    for neighbor in (
        (x + 1, y),
        (x - 1, y),
        (x, y + 1),
        (x, y - 1),
    ):
        if neighbor in ALL_CELLS:
            yield neighbor


def manhattan(a, b):
    """Entfernung bei ausschließlich waagrechten/senkrechten Schritten."""
    return abs(a[0] - b[0]) + abs(a[1] - b[1])


def direction(a, b):
    return b[0] - a[0], b[1] - a[1]


def screen_position(cell):
    return cell[0] * STEP, cell[1] * STEP


def grid_position(x, y):
    """Rundet einen Mausklick auf das Raster."""
    grid_x = round(x / STEP)
    grid_y = round(y / STEP)

    grid_x = max(-MAX_X, min(MAX_X, grid_x))
    grid_y = max(-MAX_Y, min(MAX_Y, grid_y))

    return grid_x, grid_y


def keeps_area_open(next_cell):
    """
    Prüft, ob nach dem Betreten von next_cell alle übrigen freien
    Zellen weiterhin von der neuen Turtle-Position erreichbar sind.

    So werden keine Kammern oder abgeschnittenen Bereiche erzeugt.
    """
    blocked = visited | {next_cell}
    free_cells = ALL_CELLS - blocked

    if not free_cells:
        return True

    reachable = {next_cell}
    queue = deque([next_cell])

    while queue:
        cell = queue.popleft()

        for neighbor in neighbors(cell):
            if neighbor in reachable:
                continue

            if neighbor in free_cells:
                reachable.add(neighbor)
                queue.append(neighbor)

    return len(reachable) == len(free_cells) + 1


def choose_next_cell():
    """
    Wählt einen sicheren Schritt.

    Meistens nähert sich die Turtle dem Ziel. Gelegentlich macht sie
    einen kurzen Umweg. Richtungswechsel werden bevorzugt.
    """
    candidates = [
        cell
        for cell in neighbors(current)
        if cell not in visited and keeps_area_open(cell)
    ]

    if not candidates:
        return None

    current_distance = manhattan(current, target)

    closer = [
        cell
        for cell in candidates
        if manhattan(cell, target) < current_distance
    ]

    farther = [
        cell
        for cell in candidates
        if manhattan(cell, target) > current_distance
    ]

    # Kleine Umwege sorgen für mehr Schnörkel.
    make_detour = farther and random.random() < 0.23

    if make_detour:
        selection = farther
    elif closer:
        selection = closer
    else:
        selection = candidates

    def score(cell):
        new_direction = direction(current, cell)
        distance_score = manhattan(cell, target)

        # Geradeauslaufen wird etwas unattraktiver.
        straight_penalty = 3 if new_direction == last_direction else 0

        # Zufall erzeugt unterschiedliche Wege.
        random_part = random.random() * 4

        return distance_score + straight_penalty + random_part

    return min(selection, key=score)


def move_one_step():
    """Führt einen einzelnen animierten Schritt aus."""
    global current
    global moving
    global last_direction
    global target
    global pending_target

    if target is None:
        moving = False
        return

    if current == target:
        moving = False
        target = None

        if pending_target is not None:
            next_target = pending_target
            pending_target = None
            start_movement(next_target)

        return

    next_cell = choose_next_cell()

    if next_cell is None:
        print("Kein sicherer Schritt mehr möglich.")
        moving = False
        target = None
        return

    last_direction = direction(current, next_cell)
    current = next_cell
    visited.add(current)

    runner.setheading({
        (1, 0): 0,
        (0, 1): 90,
        (-1, 0): 180,
        (0, -1): 270,
    }[last_direction])

    runner.goto(screen_position(current))
    screen.update()

    screen.ontimer(move_one_step, DELAY)


def start_movement(new_target):
    """Startet die Bewegung zu einem neuen Ziel."""
    global target
    global moving

    if new_target == current:
        return

    if new_target in visited:
        print("Dieser Punkt wurde bereits betreten.")
        return

    target = new_target

    if not moving:
        moving = True
        move_one_step()


def clicked(x, y):
    """Verarbeitet einen Mausklick."""
    global pending_target

    clicked_target = grid_position(x, y)

    if moving:
        # Der letzte Klick während einer Bewegung wird gespeichert.
        pending_target = clicked_target
    else:
        start_movement(clicked_target)


def quit_program():
    """Beendet das Programm mit Q."""
    try:
        screen.bye()
    except turtle.Terminator:
        pass

print("Klicke wiederholt irgendwo auf die schwarze Fläche.")
screen.onclick(clicked)
screen.listen()
screen.onkey(quit_program, "q")
screen.onkey(quit_program, "Q")
screen.update()
screen.mainloop()