Tischedatei, XMLdatei_Bestellung, AusgabeTischePersonen and AusgabePersonTisch are no longer configured in platz.cfg. Instead, platz.py now requires a --indir <dir> command-line switch pointing at a directory that must contain tische.ini and bestellung.xml, and into which TischePersonen.txt/PersonenTische.csv are written - e.g. work/test1 or work/test2. This makes it trivial to run against different guest lists without editing platz.cfg. platz.bat/platz.sh forward extra arguments so --indir can be passed through. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7.7 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— derivesPLATZfrom the script's own location (no path editing needed), setsPLATZ_BIN,PLATZ_CFG,PLATZ_LIBS,PLATZ_WORK,PLATZ_IN,PLATZ_OUT, addsPLATZ_LIBStoPYTHONPATH(idempotently), and creates missing folders. Sourced by every other script; not normally invoked directly. The.shvariant must be sourced (source bin/setenv.sh), not executed.bin/install_py.bat/bin/install_py.sh— callssetenv, then creates.venv(viapy -m venv/python3 -m venv) andpip install -r requirements.txtif.venvdoesn't already exist.bin/activate_venv.bat/bin/activate_venv.sh— callssetenv, then activates the existing.venv(errors out ifinstall_pyhasn't been run yet).bin/get_cmd.bat/bin/get_cmd.sh— callssetenv, then opens a new shell with thePLATZ_*environment already set.bin/platz.bat/bin/platz.sh— callssetenv, then runspython "$PLATZ_LIBS/platz.py". This is the actual program entry point (replaces the oldbin/run/bin/run.bat, which hardcoded absolute paths and have been removed).
libs/platz.py requires a command-line switch --indir <dir> (parsed with optparse,
since this is Python 2) pointing at a directory that holds the per-run input files and
receives the output files — e.g. work/test1/, work/test2/, or any new folder following
the same layout:
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>(withVorname/Nachname/optionalTitel) 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 (written back into the same
--indirdirectory):TischePersonen.txt(table → seated people) andPersonenTische.csv(person → table, sorted).
cfg/platz.cfg (still located via $PLATZ_CFG, independent of --indir) only selects the
GA cycle/penalty config:
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.
tische.ini/bestellung.xml/TischePersonen.txt/PersonenTische.csv are fixed filenames
resolved as os.path.join(indir, ...) in platz.py — they are no longer configured in
platz.cfg.
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;SitzplatzverteilunginStrukturdaten.pyis the concrete solution type used here.Zyklus("Cycle") — parsesAbfolge/Anzahlstrings (fromzyklus.cfg) into the ordered list of GA actions and their counts for one run.Farm— owns the population (Pool/PoolNeu), and executes aZyklusaction-by-action against instances of a given solution class (passed asKlasseplus its constructor args/kwargs).Bester()returns the fittest (max()) solution found; solutions are ordered via__cmp__onself.value.
Strukturdaten.py — domain model for this specific seating problem
Person,Gruppe(group — aSetof people, splittable viateilen()),Tisch(table — aGruppesubclass with a seat capacity and neighbor-table ids).- Container/index classes:
Personen,Gruppen,Tische, andPlaetze("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") — loadsstrafen.cfgand 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 actualLoesungsubclass 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 callsbewerten()to score the arrangement perStrafliste.mutieren()picks the worst-scoring groups (SchlechteGruppenTopX), evicts them, and reseats them, hoping for a better score.speichern()writes the two output files.XMLConfig— parsesbestellung.xmlintoPersonen/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__)
- Parse
--indir(required) from the command line viaoptparse. - Read
platz.cfgto locatezyklus.cfg/strafen.cfg(env-var expansion viaos.path.expandvars). - Load
Zyklus(GA schedule),Strafliste(penalties),Tische(table layout, from<indir>/tische.ini). - Load
<indir>/bestellung.xmlviaXMLConfig→ people, groups, VIP group, VIP seat map. - Build a
Farm(Zyklus, Sitzplatzverteilung, Tische=..., Gruppen=..., Strafen=..., VIPListe=..., VIPs=...), which runs the whole GA cycle in its constructor. - Take
Farm.Bester()and call.speichern(...)to write<indir>/TischePersonen.txtand<indir>/PersonenTische.csv.
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.