# 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//` (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: `` blocks containing `` (with `Vorname`/`Nachname`/optional `Titel`) and an `` (reservation count per person template — this expands into that many identical bookings), plus optional `` 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 `` template by its sibling `` into that many individual bookings, and treating any `` containing a `` 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.