"""Stapelregeln im Auflieger – reine Logik, ohne pygame. Begriffe -------- Barriere (``wall_x``) Rechte Kante des bereits gestapelten Ladeguts. Neue Koffer werden dort eingerastet. Reicht die Barriere über das Heck des Aufliegers hinaus, ist das Spiel verloren. Plattform Ein bereits platzierter Koffer, auf den der aktuelle Koffer obendrauf gesetzt werden darf (nur bündig an der Barriere, nicht zu schmal, und die Fläche obendrauf muss frei sein). """ from __future__ import annotations from . import config from .case import Case class Trailer: """Der Auflieger samt Stapel und Barrieren-Logik.""" def __init__( self, x: int = config.TRAILER_X, y: int = config.TRAILER_Y, width: int = config.TRAILER_WIDTH, height: int = config.TRAILER_HEIGHT, ) -> None: self.x = x self.y = y self.width = width self.height = height self.stack: list[Case] = [] self.wall_x: float = float(x) # ------------------------------------------------------------------ # Masse # ------------------------------------------------------------------ @property def right(self) -> float: return self.x + self.width @property def bottom(self) -> float: return self.y + self.height def reset(self) -> None: self.stack.clear() self.wall_x = float(self.x) # ------------------------------------------------------------------ # Auswertung # ------------------------------------------------------------------ def overflow(self) -> bool: """Ragt die Barriere über das Heck hinaus?""" return self.wall_x > self.right def load_percent(self) -> float: """Beladung in Prozent (Fläche der Koffer / Fläche des Aufliegers).""" total = self.width * self.height if total <= 0: return 0.0 used = sum(case.width * case.height for case in self.stack) return min(100.0, used / total * 100.0) def find_platform(self, case: Case) -> Case | None: """Koffer, auf den ``case`` obendrauf gesetzt werden darf (oder ``None``).""" for base in reversed(self.stack): if base.y <= self.y: continue # Oberkante = Auflieger-Oberkante if base.y - case.height < self.y: continue # oben kein Platz mehr if case.width > base.width: continue # zu breit für die Plattform if abs(base.right - self.wall_x) >= config.PLATFORM_TOLERANCE: continue # nicht bündig an der Barriere if self._top_is_busy(base): continue return base return None def _top_is_busy(self, base: Case) -> bool: """Liegt schon ein Koffer auf ``base``?""" for other in self.stack: if other is base: continue if abs(other.bottom - base.y) <= 0.5 and other.x < base.right and other.right > base.x: return True return False # ------------------------------------------------------------------ # Platzieren # ------------------------------------------------------------------ def stack_at_wall(self, case: Case) -> None: """Koffer bündig an die Barriere schieben.""" case.x = self.wall_x case.placed = True case.snapped = True self.stack.append(case) self.wall_x = case.right def stack_with_gap(self, case: Case) -> None: """Koffer mit einer Lücke einreihen (nach einer Kollision).""" case.x = self.wall_x + config.SNAP_THRESHOLD case.placed = True self.stack.append(case) self.wall_x = case.right def place_on_top(self, case: Case, base: Case) -> None: """Koffer rechtsbündig auf ``base`` ablegen.""" case.y = base.y - case.height case.x = base.right - case.width case.placed = True case.snapped = True self.stack.append(case) self.wall_x = case.right def remove(self, case: Case) -> None: """Koffer wieder entfernen (er ragt über das Heck hinaus).""" if case in self.stack: self.stack.remove(case)