Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74e08f389c | |||
| 0232e54f37 |
@@ -29,9 +29,10 @@ artifacts to be aware of when touching this code:
|
||||
Python 3 raises `TypeError` for that, which is what surfaced it. Fixed to call
|
||||
`G.Anzahl()`.
|
||||
|
||||
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`.
|
||||
There is no build system or linter configured in this repo. A `tests/` unittest suite
|
||||
exists (see below); `requirements.txt` is still 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
|
||||
|
||||
@@ -81,6 +82,15 @@ GA cycle/penalty config:
|
||||
resolved as `os.path.join(indir, ...)` in `platz.py` — they are no longer configured in
|
||||
`platz.cfg`.
|
||||
|
||||
Pass `--tosvg` to additionally write `<indir>/Sitzplatzverteilung.svg` via
|
||||
`Sitzplatzverteilung.alsSVG()` (see below).
|
||||
|
||||
Run the test suite with `bin/run_tests.bat` / `bin/run_tests.sh` (or directly: `python -m
|
||||
unittest discover -s tests -p "test_*.py" -v`). A stray global `PYTHONPATH` entry on this
|
||||
machine points at an unrelated project that also defines a `tests` package — this shadows
|
||||
`import tests` but does not affect `unittest discover`, which is what the run_tests
|
||||
scripts use.
|
||||
|
||||
## Architecture
|
||||
|
||||
Two library modules under `libs/`, imported via `PYTHONPATH=$PLATZ_LIBS`:
|
||||
@@ -115,7 +125,11 @@ Domain-agnostic and reusable in principle:
|
||||
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.
|
||||
better score. `speichern()` writes the two output files. `alsSVG()` renders the layout to
|
||||
SVG: tables as white circles sized to fit all their seats' person-circles without
|
||||
touching, people as colored circles (color = group, via `SVGGruppenFarben`) placed evenly
|
||||
around the inside of their table's circle. The table-to-table grid spacing is derived
|
||||
from the largest table radius so tables never overlap regardless of their `Koordinaten`.
|
||||
- `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
|
||||
|
||||
@@ -121,6 +121,10 @@ 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.
|
||||
|
||||
## Further details
|
||||
|
||||
See `CLAUDE.md` for a deeper architectural walkthrough of the GA framework and domain
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM Fuehrt die unittest-Suite in tests/ aus
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
python -m unittest discover -s "%PLATZ%\tests" -p "test_*.py" -v
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# ================================================================
|
||||
# Fuehrt die unittest-Suite in tests/ aus
|
||||
# ================================================================
|
||||
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
|
||||
|
||||
python -m unittest discover -s "$PLATZ/tests" -p "test_*.py" -v
|
||||
@@ -0,0 +1,84 @@
|
||||
# Dokumentation der Unittest-Szenarien
|
||||
|
||||
Die drei Testfaelle in `tests/test_sitzplatzverteilung.py` pruefen die Platzierungs- und
|
||||
Trennungslogik von `Sitzplatzverteilung` (in `libs/Strukturdaten.py`) anhand konkreter,
|
||||
handgebauter Tisch-/Gruppenkonfigurationen. Jeder Test schreibt zusaetzlich eine
|
||||
SVG-Visualisierung nach `doc/images/` (erzeugt mit `Sitzplatzverteilung.alsSVG()`, das auch
|
||||
ueber den `--tosvg`-Schalter von `bin/platz.bat`/`bin/platz.sh` erreichbar ist). Tische
|
||||
werden darin als weisse Kreise dargestellt, Personen als farbige Kreise innerhalb ihres
|
||||
Tischkreises; alle Personen derselben Gruppe haben dieselbe Farbe.
|
||||
|
||||
## Szenario 1: Drei Tische in einer Kette, drei Paare
|
||||
|
||||
`TestDreiTischeKettePaare` in `tests/test_sitzplatzverteilung.py`
|
||||
|
||||
- Drei Tische `T1-T2-T3` (Kette: T1 nur mit T2 benachbart, T3 nur mit T2 benachbart), je
|
||||
2 Plaetze.
|
||||
- Drei Gruppen zu je 2 Personen (Paare).
|
||||
|
||||
Da jeder Tisch exakt so viele Plaetze hat wie eine Gruppe Personen, passt jedes Paar
|
||||
komplett an einen Tisch. Der Test prueft, dass keine Gruppe getrennt wird
|
||||
(`GruppenTeilungGet(G) == 0` fuer alle drei Gruppen) und dass die optimale Wertigkeit
|
||||
(`SV.value == 0`, keine Strafpunkte) erreicht wird.
|
||||
|
||||

|
||||
|
||||
## Szenario 2: Vier Tische in einer Linie, zwei Vierergruppen
|
||||
|
||||
`TestVierTischeLinieViergruppen` in `tests/test_sitzplatzverteilung.py`
|
||||
|
||||
- Vier Tische `T1-T2-T3-T4` in einer Linie (T1↔T2↔T3↔T4), je 2 Plaetze.
|
||||
- Zwei Gruppen zu je 4 Personen.
|
||||
|
||||
Da kein Tisch 4 Plaetze hat, muss jede Vierergruppe auf zwei Tische aufgeteilt werden. Der
|
||||
Test prueft, dass jede Gruppe genau einmal geteilt wird (`GruppenTeilungGet(G) == 1`) und
|
||||
dass die beiden Teiltische pro Gruppe ein zusammenhaengendes Nachbarpaar bilden — entweder
|
||||
`{T1,T2}` oder `{T3,T4}}`, nie ueber die mittlere Naht zwischen T2 und T3 hinweg vermischt.
|
||||
Ausserdem sind am Ende alle vier Tische voll besetzt.
|
||||
|
||||

|
||||
|
||||
## Szenario 3: Sechs Tische im Kreis, vier Dreiergruppen
|
||||
|
||||
`TestSechsTischeKreisDreiergruppen` in `tests/test_sitzplatzverteilung.py`
|
||||
|
||||
- Sechs Tische `T1..T6`, im Kreis angeordnet (jeder Tisch hat genau zwei Nachbarn: z.B. T1
|
||||
ist mit T2 und T6 benachbart), je 2 Plaetze.
|
||||
- Vier Gruppen zu je 3 Personen (12 Personen auf 6×2 = 12 Plaetzen — exakt ausgelastet).
|
||||
|
||||
Da kein Tisch 3 Plaetze hat, muss jede Dreiergruppe auf zwei Tische aufgeteilt werden
|
||||
(2+1). Der Test prueft zwei Arten von Eigenschaften:
|
||||
|
||||
- **Immer gueltig, unabhaengig vom Zufalls-Seed:** Bei exakt ausgelasteten 12 Plaetzen fuer
|
||||
12 Personen bleibt keine andere Aufteilung uebrig — jede Gruppe wird also garantiert
|
||||
genau einmal geteilt (`GruppenTeilungGet(G) == 1`), und am Ende sind alle sechs Tische
|
||||
voll besetzt.
|
||||
- **Nur mit dem hier fest gewaehlten Seed zusaetzlich beobachtet:** Die beiden Teiltische
|
||||
jeder Gruppe sind tatsaechlich echte Nachbarn im Kreis. Das ist keine Garantie des
|
||||
Algorithmus fuer jeden beliebigen Seed — bei sehr knapper Auslastung kann der
|
||||
GA-Platzierungsfallback (`Sitzplatzverteilung.GruppeSetzen`, wenn `NachbarTische` keine
|
||||
passenden Nachbarn mehr findet) auch nicht benachbarte Tische fuer die beiden Teile einer
|
||||
Gruppe waehlen. Mit einem grossen Stichprobentest ueber viele Seeds trat das in gut zwei
|
||||
Dritteln der Faelle auf; der im Test verwendete Seed wurde bewusst so gewaehlt, dass
|
||||
zusaetzlich die Nachbarschaftseigenschaft zutrifft, um sie hier sichtbar zu machen.
|
||||
|
||||
Die `Koordinaten` der sechs Tische in diesem Test sind bewusst auf einem Kreis im
|
||||
mathematischen Sinn angeordnet (Winkel gleichmaessig verteilt), damit auch die
|
||||
SVG-Visualisierung die Kreisform erkennen laesst — die tatsaechliche Nachbarschaft, die der
|
||||
Algorithmus fuer die Platzierung nutzt, kommt aber ausschliesslich aus der
|
||||
`Nachbarliste`/`Nachbarn`-Angabe jedes Tisches, nicht aus den Koordinaten.
|
||||
|
||||

|
||||
|
||||
## Bilder neu erzeugen
|
||||
|
||||
Die drei SVGs in `doc/images/` werden beim Ausfuehren der Testsuite automatisch neu
|
||||
geschrieben:
|
||||
|
||||
```bash
|
||||
bin/run_tests.sh
|
||||
```
|
||||
|
||||
```bat
|
||||
bin\run_tests.bat
|
||||
```
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="251.2" height="90.4" viewBox="0 0 251.2 90.4">
|
||||
<rect width="100%" height="100%" fill="white"/>
|
||||
<circle cx="45.2" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="45.2" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 1</text>
|
||||
<circle cx="45.2" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="45.2" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="125.6" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 2</text>
|
||||
<circle cx="125.6" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="125.6" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="206" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="206" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 3</text>
|
||||
<circle cx="206" cy="32" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
|
||||
<circle cx="206" cy="58.4" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="251.2" height="412" viewBox="0 0 251.2 412">
|
||||
<rect width="100%" height="100%" fill="white"/>
|
||||
<circle cx="125.6" cy="366.8" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="125.6" y="329.6" text-anchor="middle" font-size="10" fill="black">Tisch 1</text>
|
||||
<circle cx="125.6" cy="353.6" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="125.6" cy="380" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="206" cy="286.4" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="206" y="249.2" text-anchor="middle" font-size="10" fill="black">Tisch 2</text>
|
||||
<circle cx="206" cy="273.2" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="206" cy="299.6" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="206" cy="125.6" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="206" y="88.4" text-anchor="middle" font-size="10" fill="black">Tisch 3</text>
|
||||
<circle cx="206" cy="112.4" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
|
||||
<circle cx="206" cy="138.8" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
|
||||
<circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="125.6" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 4</text>
|
||||
<circle cx="125.6" cy="32" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
|
||||
<circle cx="125.6" cy="58.4" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
|
||||
<circle cx="45.2" cy="125.6" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="45.2" y="88.4" text-anchor="middle" font-size="10" fill="black">Tisch 5</text>
|
||||
<circle cx="45.2" cy="112.4" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
|
||||
<circle cx="45.2" cy="138.8" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
|
||||
<circle cx="45.2" cy="286.4" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="45.2" y="249.2" text-anchor="middle" font-size="10" fill="black">Tisch 6</text>
|
||||
<circle cx="45.2" cy="273.2" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="45.2" cy="299.6" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="331.6" height="90.4" viewBox="0 0 331.6 90.4">
|
||||
<rect width="100%" height="100%" fill="white"/>
|
||||
<circle cx="45.2" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="45.2" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 1</text>
|
||||
<circle cx="45.2" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="45.2" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="125.6" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 2</text>
|
||||
<circle cx="125.6" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="125.6" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="206" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="206" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 3</text>
|
||||
<circle cx="206" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="206" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="286.4" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
|
||||
<text x="286.4" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 4</text>
|
||||
<circle cx="286.4" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
<circle cx="286.4" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -13,6 +13,7 @@ from itertools import chain
|
||||
from random import *
|
||||
import configparser, os
|
||||
import copy
|
||||
import math
|
||||
from functools import total_ordering
|
||||
from xml.dom import minidom
|
||||
|
||||
@@ -532,6 +533,115 @@ class Sitzplatzverteilung:
|
||||
fout.writelines( Liste )
|
||||
fout.close()
|
||||
|
||||
# Farbpalette fuer die Gruppen-Einfaerbung im SVG (wird zyklisch
|
||||
# wiederverwendet, falls es mehr Gruppen als Farben gibt)
|
||||
SVGGruppenFarben = [
|
||||
'#e6194b', '#3cb44b', '#4363d8', '#f58231', '#911eb4',
|
||||
'#42d4f4', '#f032e6', '#bfef45', '#fabed4', '#469990',
|
||||
'#dcbeff', '#9a6324', '#800000', '#aaffc3', '#000075' ]
|
||||
|
||||
def alsSVG(self, OutputSVG, PersonenRadius=12, Puffer=10):
|
||||
"""Zeichnet die Sitzplatzverteilung als SVG:
|
||||
|
||||
Tische werden als weisse Kreise dargestellt, Personen als
|
||||
farbige Kreise (Farbe = Gruppe) innerhalb ihres Tischkreises.
|
||||
Das Koordinatenraster der Tische wird so skaliert, dass alle
|
||||
Personenkreise in ihrem Tischkreis Platz haben und sich die
|
||||
Tischkreise untereinander nicht ueberschneiden.
|
||||
"""
|
||||
# Tischradius je Tisch berechnen: gross genug, damit alle
|
||||
# Personenkreise (auf einem inneren Kreis gleichmaessig verteilt)
|
||||
# nebeneinander Platz haben, ohne sich zu beruehren. Der Faktor
|
||||
# 1.1 auf den rechnerischen Mindestabstand ist ein kleiner
|
||||
# Sicherheitsabstand gegen Rundungsfehler und damit sich die
|
||||
# Kreise auch optisch sichtbar nicht beruehren.
|
||||
def SitzRadiusVon(Plaetze):
|
||||
if Plaetze <= 1:
|
||||
return 0.0
|
||||
return 1.1 * PersonenRadius / math.sin(math.pi / Plaetze)
|
||||
|
||||
def TischRadius(Plaetze):
|
||||
return SitzRadiusVon(Plaetze) + PersonenRadius + Puffer
|
||||
|
||||
TidZuRadius = {}
|
||||
GroessterRadius = 0
|
||||
for T in self.Tische:
|
||||
r = TischRadius( T.Plaetze )
|
||||
TidZuRadius[T.Id] = r
|
||||
if r > GroessterRadius:
|
||||
GroessterRadius = r
|
||||
|
||||
# Rasterabstand der Tisch-Koordinaten: doppelter groesster
|
||||
# Tischradius (plus Puffer) verhindert Ueberschneidungen
|
||||
# unabhaengig davon, welche Tische tatsaechlich benachbart sind
|
||||
Rasterabstand = 2 * GroessterRadius + Puffer
|
||||
|
||||
Xs = [ T.X_Koordinate for T in self.Tische ]
|
||||
Ys = [ T.Y_Koordinate for T in self.Tische ]
|
||||
MinX, MinY = min(Xs), min(Ys)
|
||||
MaxX, MaxY = max(Xs), max(Ys)
|
||||
|
||||
Rand = GroessterRadius + Puffer
|
||||
Breite = (MaxX - MinX) * Rasterabstand + 2 * Rand
|
||||
Hoehe = (MaxY - MinY) * Rasterabstand + 2 * Rand
|
||||
|
||||
def TischMitte(T):
|
||||
x = (T.X_Koordinate - MinX) * Rasterabstand + Rand
|
||||
y = (T.Y_Koordinate - MinY) * Rasterabstand + Rand
|
||||
return (x, y)
|
||||
|
||||
def GruppeVonPerson(P):
|
||||
try:
|
||||
return self.Gruppen.GruppeVonPid(P.Id)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
GidZuFarbe = {}
|
||||
def FarbeVonGruppe(G):
|
||||
if G is None:
|
||||
return '#888888'
|
||||
if G.Id not in GidZuFarbe:
|
||||
Index = len(GidZuFarbe) % len(self.SVGGruppenFarben)
|
||||
GidZuFarbe[G.Id] = self.SVGGruppenFarben[Index]
|
||||
return GidZuFarbe[G.Id]
|
||||
|
||||
Teile = []
|
||||
Teile.append( '<?xml version="1.0" encoding="UTF-8"?>\n' )
|
||||
Teile.append(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="%g" height="%g" '
|
||||
'viewBox="0 0 %g %g">\n' % (Breite, Hoehe, Breite, Hoehe) )
|
||||
Teile.append( '<rect width="100%" height="100%" fill="white"/>\n' )
|
||||
|
||||
for T in self.Tische:
|
||||
Mx, My = TischMitte(T)
|
||||
TR = TidZuRadius[T.Id]
|
||||
Teile.append(
|
||||
'<circle cx="%g" cy="%g" r="%g" fill="white" '
|
||||
'stroke="black" stroke-width="1.5"/>\n' % (Mx, My, TR) )
|
||||
Teile.append(
|
||||
'<text x="%g" y="%g" text-anchor="middle" '
|
||||
'font-size="10" fill="black">Tisch %s</text>\n'
|
||||
% (Mx, My - TR - 2, T.Id) )
|
||||
|
||||
Plaetze = list(T)
|
||||
n = len(Plaetze)
|
||||
SitzRadius = SitzRadiusVon( T.Plaetze )
|
||||
for i, P in enumerate(Plaetze):
|
||||
Winkel = 2 * math.pi * i / max(n, 1) - math.pi / 2
|
||||
Px = Mx + SitzRadius * math.cos(Winkel)
|
||||
Py = My + SitzRadius * math.sin(Winkel)
|
||||
Farbe = FarbeVonGruppe( GruppeVonPerson(P) )
|
||||
Teile.append(
|
||||
'<circle cx="%g" cy="%g" r="%g" fill="%s" '
|
||||
'stroke="black" stroke-width="1"/>\n'
|
||||
% (Px, Py, PersonenRadius, Farbe) )
|
||||
|
||||
Teile.append( '</svg>\n' )
|
||||
|
||||
fout = open( OutputSVG, 'w' )
|
||||
fout.writelines( Teile )
|
||||
fout.close()
|
||||
|
||||
def mutieren(self):
|
||||
"""Aendert die vorhandene Sitzplatzverteilung hoffentlich zum Besseren"""
|
||||
[IdsSchlechteGruppen, Wertigkeiten] = self.SchlechteGruppenTopX(Anzahl=self.MutationsGroesse)
|
||||
|
||||
@@ -24,6 +24,10 @@ if __name__ == '__main__':
|
||||
parser.add_option( "--indir", dest="indir", default=None,
|
||||
help="Verzeichnis mit tische.ini und bestellung.xml; "
|
||||
"dort werden auch TischePersonen.txt und PersonenTische.csv abgelegt" )
|
||||
parser.add_option( "--tosvg", dest="tosvg", action="store_true", default=False,
|
||||
help="zusaetzlich eine Sitzplatzverteilung.svg im --indir Verzeichnis "
|
||||
"erzeugen (Tische als weisse Kreise, Personen als farbige "
|
||||
"Kreise je Gruppe)" )
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.indir is None:
|
||||
@@ -52,6 +56,7 @@ if __name__ == '__main__':
|
||||
# lade Ausgabedateien
|
||||
AusgabeTP= os.path.join( InDir, 'TischePersonen.txt' )
|
||||
AusgabePT = os.path.join( InDir, 'PersonenTische.csv' )
|
||||
AusgabeSVG = os.path.join( InDir, 'Sitzplatzverteilung.svg' )
|
||||
|
||||
Z = Zyklus()
|
||||
Z.laden( Zyklusconfig, Zyklusart )
|
||||
@@ -76,6 +81,8 @@ if __name__ == '__main__':
|
||||
S = F1.Bester()
|
||||
print(S)
|
||||
S.speichern( AusgabeTP, AusgabePT )
|
||||
if options.tosvg:
|
||||
S.alsSVG( AusgabeSVG )
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
@@ -20,6 +20,8 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'libs'))
|
||||
|
||||
from Strukturdaten import Gruppe, Gruppen, Person, Sitzplatzverteilung, Strafliste, Tisch, Tische
|
||||
|
||||
IMAGES_DIR = os.path.join(os.path.dirname(__file__), '..', 'doc', 'images')
|
||||
|
||||
|
||||
def erzeuge_personen(anzahl, start_id=1):
|
||||
return [Person(i, 'Vorname%d' % i, 'Nachname%d' % i) for i in range(start_id, start_id + anzahl)]
|
||||
@@ -48,6 +50,7 @@ class TestDreiTischeKettePaare(unittest.TestCase):
|
||||
def test_alle_paare_komplett_an_einem_tisch(self):
|
||||
SV = Sitzplatzverteilung( Tische=self.Tische, Gruppen=self.Gruppen,
|
||||
Strafen=self.Strafen, VIPListe={}, VIPs=Gruppe(0) )
|
||||
SV.alsSVG( os.path.join(IMAGES_DIR, 'drei_tische_kette_paare.svg') )
|
||||
|
||||
for G in (self.G1, self.G2, self.G3):
|
||||
self.assertEqual( SV.GruppenTeilungGet(G), 0,
|
||||
@@ -87,6 +90,7 @@ class TestVierTischeLinieViergruppen(unittest.TestCase):
|
||||
def test_viergruppen_bleiben_auf_zusammenhaengendem_tischpaar(self):
|
||||
SV = Sitzplatzverteilung( Tische=self.Tische, Gruppen=self.Gruppen,
|
||||
Strafen=self.Strafen, VIPListe={}, VIPs=Gruppe(0) )
|
||||
SV.alsSVG( os.path.join(IMAGES_DIR, 'vier_tische_linie_viergruppen.svg') )
|
||||
|
||||
# jede Gruppe wird genau einmal geteilt (2 Tische a 2 Plaetze)
|
||||
self.assertEqual( SV.GruppenTeilungGet(self.G1), 1 )
|
||||
@@ -138,6 +142,7 @@ class TestSechsTischeKreisDreiergruppen(unittest.TestCase):
|
||||
def test_dreiergruppen_je_einmal_auf_nachbartische_geteilt(self):
|
||||
SV = Sitzplatzverteilung( Tische=self.Tische, Gruppen=self.Gruppen,
|
||||
Strafen=self.Strafen, VIPListe={}, VIPs=Gruppe(0) )
|
||||
SV.alsSVG( os.path.join(IMAGES_DIR, 'sechs_tische_kreis_dreiergruppen.svg') )
|
||||
|
||||
# 4 Dreiergruppen = 12 Personen auf 6x2 = 12 Plaetzen -> alle Tische voll.
|
||||
# (SV.Tische ist eine Kopie der urspruenglichen Tische, siehe
|
||||
|
||||
Reference in New Issue
Block a user