125 lines
4.3 KiB
Python
125 lines
4.3 KiB
Python
"""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.FAIL_GAP
|
||
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)
|