Files
platz/CLAUDE.md
T
Michael Stangl fd223e8806 Add CLAUDE.md and README.md documentation
Documents the GA-based seating solver's architecture, config file
chain, and setup/usage for both AI assistants and human contributors.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 11:07:32 +02:00

6.4 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project overview

platz (German: "Sitzplatzverteilung" / seating arrangement) is a genetic-algorithm-based solver that assigns people to tables ("Tische") for an event, given group memberships, table sizes/neighbor relationships, and a penalty scheme. It reads an XML order/guest list and INI table layout, runs a small custom GA framework to search for a good seating, and writes the result to text/CSV output files.

This is Python 2 code (print statements, ConfigParser, sets.Set, UserList, string.split, old-style raise Exception, "msg", xrange, dict.has_key, dict.iteritems). It will not run under Python 3 without a 2to3-style port. There is no requirements.txt, build system, test suite, or linter configured in this repo — a Python 2 interpreter with only the standard library is sufficient to run it.

Running the program

Entry point is libs/platz.py, invoked via the wrapper scripts in bin/, which set up environment variables and PYTHONPATH before calling it — the scripts never take a script argument, they just launch platz.py:

  • bin/run (bash, for Linux/macOS) — hardcodes PLATZ=/Users/sm/develop/python/platz
  • bin/run.bat (Windows) — hardcodes PLATZ=h:\develop\python\platz and a Python 2.4 interpreter path

Both scripts set PLATZ_CFG, PLATZ_LIBS, PLATZ_WORK and add PLATZ_LIBS to PYTHONPATH, then run python $PLATZ_LIBS/platz.py. The hardcoded paths at the top of these scripts must be edited to match wherever this repo is actually checked out before they will work.

Configuration is driven by cfg/platz.cfg, which points (via $PLATZ_CFG/$PLATZ_WORK env-var expansion) to:

  • cfg/zyklus.cfg — defines the GA run schedule ("Zyklus"): a sequence of actions (e=erzeugen/create, s=selektieren/select-best, S=random-select, m=mutieren/mutate, j=behalten/keep parents, z=neue Generation/advance generation) each with a count.
  • cfg/strafen.cfg — the penalty ("Strafpunkte") table: cost of splitting a group across tables (by group size and number of splits), cost of a lone person, and per-table under-utilization penalties.
  • A per-run working directory under work/<name>/ (e.g. work/test1/, work/test2/) containing:
    • tische.ini — table definitions: id, Nummer, Hof (venue/court), Plaetze (seats), Nachbarliste (neighbor table ids), Koordinaten.
    • bestellung.xml — the guest order: <Gruppe> blocks containing <Person> (with Vorname/Nachname/optional Titel) and an <Anzahl> (reservation count per person template — this expands into that many identical bookings), plus optional <Tisch> to pin a VIP group to a specific table.
    • Output: TischePersonen.txt (table → seated people) and PersonenTische.csv (person → table, sorted).

Architecture

Two library modules under libs/, imported via PYTHONPATH=$PLATZ_LIBS:

ga.py — generic genetic-algorithm scaffolding

Domain-agnostic and reusable in principle:

  • Loesung ("Solution") — base class for anything bred/mutated/selected by the GA. Meant to be subclassed; Sitzplatzverteilung in Strukturdaten.py is the concrete solution type used here.
  • Zyklus ("Cycle") — parses Abfolge/Anzahl strings (from zyklus.cfg) into the ordered list of GA actions and their counts for one run.
  • Farm — owns the population (Pool/PoolNeu), and executes a Zyklus action-by-action against instances of a given solution class (passed as Klasse plus its constructor args/kwargs). Bester() returns the fittest (max()) solution found; solutions are ordered via __cmp__ on self.value.

Strukturdaten.py — domain model for this specific seating problem

  • Person, Gruppe (group — a Set of people, splittable via teilen()), Tisch (table — a Gruppe subclass with a seat capacity and neighbor-table ids).
  • Container/index classes: Personen, Gruppen, Tische, and Plaetze ("Seats") which indexes tables by how many free seats they currently have, to answer "give me a table with at least N free seats" or "give me two neighboring tables that together fit two groups of given sizes" efficiently during placement/mutation.
  • Strafliste ("Penalty list") — loads strafen.cfg and computes penalty points for: splitting a group (gibPunkte('Trennung', ...), recursing to smaller group sizes if no exact rule is configured), a person sitting with no group-mate at their table ('allein'), a person having no group-mate at a neighboring table ('nichtNachbar'), and poor seat utilization at a table ('Tischbesetzung').
  • Sitzplatzverteilung ("Seating arrangement") — the actual Loesung subclass bred by the GA. Constructor seats VIPs first at pinned tables, then randomly seats groups either largest-first or smallest-first (coin flip), splitting a group across two neighboring tables when no single table has enough free seats, recursively splitting further if needed, then calls bewerten() to score the arrangement per Strafliste. mutieren() picks the worst-scoring groups (SchlechteGruppenTopX), evicts them, and reseats them, hoping for a better score. speichern() writes the two output files.
  • XMLConfig — parses bestellung.xml into Personen/Gruppen/VIP-group/VIP-seat-map, expanding each <Person> template by its sibling <Anzahl> into that many individual bookings, and treating any <Gruppe> containing a <Tisch> tag as a VIP group pinned to that table rather than going through normal GA placement.

Data flow through platz.py (__main__)

  1. Read platz.cfg to locate the other config/data files (env-var expansion via os.path.expandvars).
  2. Load Zyklus (GA schedule), Strafliste (penalties), Tische (table layout).
  3. Load bestellung.xml via XMLConfig → people, groups, VIP group, VIP seat map.
  4. Build a Farm(Zyklus, Sitzplatzverteilung, Tische=..., Gruppen=..., Strafen=..., VIPListe=..., VIPs=...), which runs the whole GA cycle in its constructor.
  5. Take Farm.Bester() and call .speichern(...) to write the output files.

Both libs/platz.py and libs/Strukturdaten.py also have self-test code under if __name__ == '__main__': that exercises the classes directly with hardcoded sample data — useful as a reference for how the classes are meant to be constructed and used.