6710d4f06a
Weniger vermeidbare Gruppentrennungen:
- strafen.cfg: Tischbesetzung bestraft jetzt gestaffelt nach freien
Plaetzen (teilbelegte Tische am teuersten, mit Fallback auf den
naechstkleineren Eintrag), Trennungsstrafen deutlich erhoeht - so
werden Gruppen nicht mehr getrennt, obwohl leere Tische danebenstehen
- gibPunkte('Tischbesetzung') mit Naechst-kleiner-Fallback
- mutieren() setzt geraeumte Gruppen groesste-zuerst neu
(GruppenSetzen(GrosseZuerst=True)), damit grosse Gruppen die frei
gewordenen ganzen Tische bekommen und wieder zusammenkommen
(uebrig bleiben nur physisch unvermeidbare Trennungen, Gruppe > Tisch)
Abstammungsbaum (neuer Schalter --show-lineage -> Abstammung.svg):
- neue Klasse ga.Abstammung zeichnet den vollen Mutationsbaum aller
Loesungen; der Ast der finalen besten Loesung ist hervorgehoben und
mit ihrer Id beschriftet
- hierarchische Loesungs-Ids (Farm._WurzelId/_KindId): Wurzel '7',
Mutationskind '7.2', dessen Kind '7.2.1' - die ganze Abstammung ist
direkt aus der Id ablesbar
Tests (15 gruen), README und CLAUDE.md aktualisiert; Beispiele mit den
neuen Strafen und beiden Grafiken neu berechnet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
147 lines
6.2 KiB
Markdown
147 lines
6.2 KiB
Markdown
# platz
|
|
|
|
A genetic-algorithm-based solver that assigns people to tables ("Tische") for an event,
|
|
given group memberships, table sizes/neighbor relationships, and a configurable penalty
|
|
scheme. It reads a guest list (XML) and a table layout (INI), searches for a good seating
|
|
using a small custom GA framework, and writes the result to text/CSV output files.
|
|
|
|
**This is Python 3 code** (migrated from an original Python 2 implementation). There are no
|
|
third-party dependencies (standard library only), so `requirements.txt` is empty — it
|
|
exists only so `bin/install_py` has something to install.
|
|
|
|
## Project structure
|
|
|
|
```
|
|
platz/
|
|
├── bin/ # environment + launcher scripts (.bat + .sh pairs)
|
|
├── cfg/ # GA cycle definition and penalty scheme
|
|
├── libs/ # the two Python modules (ga.py, Strukturdaten.py, platz.py)
|
|
├── tests/ # unittest suite for Strukturdaten.py
|
|
└── work/ # per-run input (table layout, guest list) and output
|
|
├── test1/
|
|
└── test2/
|
|
```
|
|
|
|
- `libs/platz.py` — entry point; wires together config, input data, and the GA run.
|
|
- `libs/ga.py` — generic genetic-algorithm scaffolding (`Loesung`, `Zyklus`, `Farm`,
|
|
`Entwicklung`).
|
|
- `libs/Strukturdaten.py` — domain model for the seating problem (`Person`, `Gruppe`,
|
|
`Tisch`, `Strafliste`, `Sitzplatzverteilung`, `JSONConfig`).
|
|
|
|
## Running the tests
|
|
|
|
```bash
|
|
python -m unittest discover -s tests -p "test_*.py" -v
|
|
```
|
|
|
|
If a stray global `PYTHONPATH` entry shadows the standard-library `tests` package name
|
|
(unrelated to this project), clear it for the command, e.g. `PYTHONPATH= python -m
|
|
unittest discover -s tests -p "test_*.py" -v`.
|
|
|
|
## Setup
|
|
|
|
1. Install a Python interpreter (`py` on Windows / `python3` on Linux/macOS must be on
|
|
`PATH`).
|
|
2. Run the installer for your platform to create a `.venv` and install `requirements.txt`:
|
|
|
|
```bat
|
|
:: Windows
|
|
bin\install_py.bat
|
|
```
|
|
|
|
```bash
|
|
# Linux/macOS
|
|
bin/install_py.sh
|
|
```
|
|
|
|
All `bin/` scripts derive `PLATZ` automatically from their own location (no path editing
|
|
required) by calling `setenv.bat`/`setenv.sh` first.
|
|
|
|
## Running
|
|
|
|
```bat
|
|
:: Windows
|
|
bin\activate_venv.bat
|
|
bin\platz.bat --indir work\test1
|
|
```
|
|
|
|
```bash
|
|
# Linux/macOS
|
|
source bin/activate_venv.sh
|
|
bin/platz.sh --indir work/test1
|
|
```
|
|
|
|
`bin/platz.bat` / `bin/platz.sh` call `setenv` to set `PLATZ`, `PLATZ_CFG`, `PLATZ_LIBS`,
|
|
`PLATZ_WORK`, `PLATZ_IN`, `PLATZ_OUT`, add `PLATZ_LIBS` to `PYTHONPATH`, and then run
|
|
`libs/platz.py`, forwarding any extra arguments (like `--indir`) to it.
|
|
|
|
Other helper scripts (`bin/setenv.*`, `bin/get_cmd.*`) follow the same environment
|
|
convention:
|
|
|
|
- `setenv` — sets all `PLATZ_*` environment variables and creates missing folders; sourced
|
|
by every other script, not normally called directly.
|
|
- `activate_venv` — activates the `.venv` created by `install_py`.
|
|
- `get_cmd` — opens a new shell with the `PLATZ_*` environment already set.
|
|
|
|
### Configuring a run
|
|
|
|
`--indir <dir>` is required and points at a directory containing the input files and
|
|
receiving the output files for one run — e.g. `work/test1/` or `work/test2/`:
|
|
|
|
- `tische.ini` — one section per table: `Nummer`, `Hof` (venue/court), `Plaetze` (seat
|
|
count), `Nachbarliste` (neighboring table ids), `Koordinaten`.
|
|
- `bestellung.json` — `{ "gruppen": [ ... ] }`, each group with a `"personen"` list
|
|
(`vorname`/`nachname`/optional `titel`), an optional `"anzahl"` (how many bookings to
|
|
expand each person into), an optional `"name"` (display label) and an optional `"tisch"`
|
|
(pins the group as a VIP group to that table instead of placing it via the GA).
|
|
|
|
To run against a different dataset, create a new folder with a `tische.ini` and
|
|
`bestellung.json` following the same layout and pass it via `--indir`.
|
|
|
|
The GA cycle and penalty scheme are read directly from `$PLATZ_CFG/zyklus.cfg` and
|
|
`$PLATZ_CFG/strafen.cfg` (independent of `--indir`); there is no longer a `platz.cfg`
|
|
indirection. The GA profile is selected with the `--zyklus-art <name>` switch (default:
|
|
`Adaptiv`).
|
|
|
|
`cfg/zyklus.cfg` defines the GA profiles. Two styles exist:
|
|
|
|
- **static** profiles (`Easy`, `Simple`) give an explicit `Abfolge` (sequence of actions)
|
|
and matching `Anzahl` (counts): `e` create, `s` select best, `S` select randomly, `m`
|
|
mutate, `j` keep parents, `z` advance generation.
|
|
- the **adaptive** profile (`Adaptiv`, the default) has no fixed action list. It is
|
|
recognised by a `Start` field and runs a generation loop: a large starting population
|
|
that shrinks each generation (`Schrumpfung`, down to `MinPopulation`) and stops once the
|
|
best score fails to improve by at least `Schwelle` for `Geduld` generations (or after
|
|
`MaxGenerationen`).
|
|
|
|
Select a different profile per run, e.g. `bin/platz.bat --indir work/test2 --zyklus-art Easy`.
|
|
|
|
`cfg/strafen.cfg` defines the penalty scheme: cost of splitting a group across tables (by
|
|
group size and number of splits), cost of a person sitting alone or without a group-mate at
|
|
a neighboring table, and per-table seat-utilization penalties.
|
|
|
|
### Output
|
|
|
|
A run writes two files into the `--indir` directory:
|
|
|
|
- `TischePersonen.txt` — table → seated people, with venue/table/seat numbers.
|
|
- `PersonenTische.csv` — person → table, sorted alphabetically by name.
|
|
|
|
Pass `--tosvg` to additionally write `Sitzplatzverteilung.svg` into the `--indir`
|
|
directory: tables are drawn as white circles, people as colored circles inside their
|
|
table's circle (color = group). See `doc/docu_tests.md` for example renderings.
|
|
|
|
Two more optional graphs go into the `--indir` directory:
|
|
|
|
- `--show-development` → `Entwicklung.svg`: the course of the search (best/average/worst
|
|
value over time, plus a panel with the number of solutions in the pool per step).
|
|
- `--show-lineage` → `Abstammung.svg`: the mutation tree of every solution. The final best
|
|
solution's ancestral branch is highlighted and labelled with its lineage id. Ids are
|
|
hierarchical — a root solution is a plain number (`7`), each mutation appends a child index
|
|
(`7.2`, then `7.2.1`) — so the id itself spells out the whole ancestry.
|
|
|
|
## Further details
|
|
|
|
See `CLAUDE.md` for a deeper architectural walkthrough of the GA framework and domain
|
|
model, intended for AI coding assistants but equally useful for human contributors.
|