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>
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— 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).
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>(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:
TischePersonen.txt(table → seated people) andPersonenTische.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;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__)
- Read
platz.cfgto locate the other config/data files (env-var expansion viaos.path.expandvars). - Load
Zyklus(GA schedule),Strafliste(penalties),Tische(table layout). - Load
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 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.