Files
platz/CLAUDE.md
T
Michael Stangl e8911df7e9 Use setenv in launcher scripts, add requirements.txt and missing .sh scripts
platz.bat/platz.sh now derive PLATZ via setenv instead of hardcoded
paths, matching the Standard Programm Template already applied to
setenv/install_py/activate_venv/get_cmd. Added the missing .sh
counterparts to those .bat scripts, an empty requirements.txt for
install_py to install (project only uses the stdlib), and ignore
entries for .venv/, out/, .vscode/, *.swp.

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

7.1 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 build system, test suite, or linter configured in this repo. requirements.txt exists but is empty/commented — everything used is standard library — it's there only so bin/install_py.* has something to install into the .venv.

Running the program

Entry point is libs/platz.py, invoked via the scripts in bin/ (each provided as a .bat/.sh pair, following the "Standard Programm Template" convention — see ~/.claude/CLAUDE.md for the template rules):

  • bin/setenv.bat / bin/setenv.sh — derives PLATZ from the script's own location (no path editing needed), sets PLATZ_BIN, PLATZ_CFG, PLATZ_LIBS, PLATZ_WORK, PLATZ_IN, PLATZ_OUT, adds PLATZ_LIBS to PYTHONPATH (idempotently), and creates missing folders. Sourced by every other script; not normally invoked directly. The .sh variant must be sourced (source bin/setenv.sh), not executed.
  • bin/install_py.bat / bin/install_py.sh — calls setenv, then creates .venv (via py -m venv / python3 -m venv) and pip install -r requirements.txt if .venv doesn't already exist.
  • bin/activate_venv.bat / bin/activate_venv.sh — calls setenv, then activates the existing .venv (errors out if install_py hasn't been run yet).
  • bin/get_cmd.bat / bin/get_cmd.sh — calls setenv, then opens a new shell with the PLATZ_* environment already set.
  • bin/platz.bat / bin/platz.sh — calls setenv, then runs python "$PLATZ_LIBS/platz.py". This is the actual program entry point (replaces the old bin/run / bin/run.bat, which hardcoded absolute paths and have been removed).

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.