Files
wichteln/app/draw.py
T
nico d56be7b2c0 Wichtel-Werkstatt: Secret-Santa-App mit Räumen, Ausschlüssen und Recovery-Links
- FastAPI + SQLite, serverseitig gerenderte Templates, mobil-zuerst
- Admin-Passwort, einseitige Ausschlüsse, eingefrorene Auslosung
- Teilnehmer sehen Ergebnis per Cookie; Einmal-Recovery-Links bei Verlust
- Docker/podman-tauglich (Entrypoint mit Privilegien-Drop, SELinux-:z)
- Unit-Tests für Auslosung, E2E-Testskript (30 Checks)
2026-08-05 16:07:21 +02:00

42 lines
1.3 KiB
Python

"""Auslosung: zufällige Zuordnung, bei der niemand sich selbst zieht
und einseitige Ausschlüsse respektiert werden.
Ansatz: Rejection-Sampling über zufällige Permutationen. Für typische
Gruppen (Familien/Freunde, < 30 Personen) findet sich eine gültige
Permutation fast immer in wenigen Versuchen (nur die Derangement-
Bedingung allein hat bereits Erfolgswahrscheinlichkeit ~1/e).
"""
from __future__ import annotations
import random
MAX_ATTEMPTS = 2000
def draw_assignment(
participant_ids: list[str],
exclusions: set[tuple[str, str]],
*,
rng: random.Random | None = None,
max_attempts: int = MAX_ATTEMPTS,
) -> dict[str, str] | None:
"""Liefert {giver_id: receiver_id} oder None, wenn keine gültige
Zuordnung gefunden wurde (z. B. unerfüllbare Ausschlüsse)."""
rng = rng or random.Random()
ids = list(participant_ids)
n = len(ids)
if n < 2:
return None
for _ in range(max_attempts):
receivers = ids[:]
rng.shuffle(receivers)
if any(receivers[i] == ids[i] for i in range(n)):
continue
mapping = dict(zip(ids, receivers))
if any((giver, receiver) in exclusions for giver, receiver in mapping.items()):
continue
return mapping
return None