9de80d2f2a
- bestellung.xml -> bestellung.json: neuer JSONConfig (loest XMLConfig ab), alle work-Beispiele konvertiert, Doku (CLAUDE/README/dateiformate) umgestellt - ga.py: Beobachtungsobjekt Entwicklung zeichnet den Loesungsverlauf auf, neuer Schalter --show-development erzeugt Entwicklung.svg (beste/mittlere/ schlechteste Wertigkeit ueber die Zyklus-Aktionen) - web/main.py: neuer Endpunkt /tische-datei (tische.ini-Upload, ast.literal_eval), Frontend kann jetzt eine tische.ini laden - libs/webserver.py: plattformunabhaengige start/stop/status-Steuerung der Web-Testinstanz (ersetzt run_web_helper.ps1), raeumt auch verwaiste --reload-Prozessbaeume zuverlaessig auf - Sicherheit/Modernisierung: eval() -> ast.literal_eval in Tische/Strafliste, sicherer Mini-Auswerter fuer die String-Ausdruecke in zyklus.cfg, defaultdict(set) in Plaetze - SVG: Tischnummer fett zentriert im Tischkreis statt "Tisch x" darueber - Tests: JSONConfig, Entwicklung und der Zyklus-Auswerter abgedeckt (9 Tests) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1245 lines
38 KiB
Python
Executable File
1245 lines
38 KiB
Python
Executable File
"""
|
|
Enthaelt alle Datenstrukturen um die noetigen Hierarchien zur Beschreibung einer Sitzplatzverteilung aufzubauen
|
|
|
|
"""
|
|
|
|
__author__ = "Michael Stangl"
|
|
__version__ = "$Revision: $"
|
|
__date__ = "$Date: $"
|
|
__copyright__ = "Copyright (c) 2005 Michael Stangl"
|
|
__license__ = "Python"
|
|
|
|
from itertools import chain
|
|
from random import *
|
|
import ast
|
|
import configparser, os
|
|
import copy
|
|
import math
|
|
from collections import defaultdict
|
|
from functools import total_ordering
|
|
|
|
|
|
# Strukturklassen: Person, Gruppe, Tisch
|
|
class Person:
|
|
"""enthaelt alle Daten zu einer Person die noetig sind"""
|
|
def __init__(self, Id, Vorname, Name, Titel=''):
|
|
self.Id = Id
|
|
self._Vorname = Vorname
|
|
self._Name = Name
|
|
self._Titel = Titel
|
|
def __repr__(self):
|
|
return ' Person %s: "%s %s %s"' % ( self.Id, self._Titel,
|
|
self._Vorname, self._Name)
|
|
|
|
@total_ordering
|
|
class Gruppe:
|
|
"""enthalt alle Daten zu einer Gruppe"""
|
|
Art = 'Gruppe'
|
|
def __init__(self, Id, Personen=[]):
|
|
self.Id = Id
|
|
self._Personen = set()
|
|
self._Anzahl = 0
|
|
self.PvonId = {}
|
|
for P in Personen:
|
|
self.Person_dazu( P )
|
|
def __repr__(self):
|
|
result = '+ %s %s: Ids: ' % (self.Art, self.Id)
|
|
for P in self._Personen:
|
|
result = result + repr(P.Id) + ','
|
|
result = result + ' %s Person(en)' % (self._Anzahl)
|
|
return result
|
|
def Person_dazu(self, P):
|
|
self._Personen.add(P)
|
|
self._Anzahl = self._Anzahl + 1
|
|
self.PvonId[ P.Id ] = P
|
|
def Person_weg(self, P):
|
|
if P in self._Personen :
|
|
self._Personen.remove(P)
|
|
self._Anzahl = self._Anzahl - 1
|
|
else:
|
|
raise KeyError("Person ist nicht enthalten")
|
|
def teilen(self, NeueGroesse=0):
|
|
if self._Anzahl == 1:
|
|
raise IndexError("Gruppe ist nur noch einer")
|
|
if NeueGroesse == 0:
|
|
NeueGroesse = int(self._Anzahl/2)
|
|
one = Gruppe( self.Id, self._Personen )
|
|
two = Gruppe( self.Id, self._Personen )
|
|
two._Personen.clear()
|
|
for i in range(NeueGroesse):
|
|
two._Personen.add(one._Personen.pop())
|
|
one._Anzahl = len(one._Personen)
|
|
two._Anzahl = len(two._Personen)
|
|
return [one,two]
|
|
def PersonVonPid(self, SearchedId):
|
|
return self.PvonId[SearchedId]
|
|
def Anzahl(self):
|
|
return self._Anzahl
|
|
def __contains__(self, P):
|
|
return P in self._Personen
|
|
def __iter__(self):
|
|
for P in self._Personen:
|
|
yield(P)
|
|
def __eq__(self,other):
|
|
return self._Anzahl == other._Anzahl
|
|
def __lt__(self,other):
|
|
return self._Anzahl < other._Anzahl
|
|
def __getattr__(self, attrname):
|
|
if attrname == 'Anzahl':
|
|
return self._Anzahl
|
|
elif attrname == 'Id':
|
|
return self.Id
|
|
else:
|
|
raise AttributeError(attrname)
|
|
|
|
class Tisch(Gruppe):
|
|
"""enthalt alle Daten zu einem Tisch"""
|
|
Art = 'Tisch'
|
|
def __init__(self, Id, Plaetze=1, Koordinaten=(0,0), pids=[], Nachbarliste=[], Hof=0, Nummer=0 ):
|
|
self._Plaetze = Plaetze
|
|
self.Hof = Hof
|
|
self.Nummer = Nummer
|
|
self.X_Koordinate = Koordinaten[0]
|
|
self.Y_Koordinate = Koordinaten[1]
|
|
self.Nachbarn = set(Nachbarliste)
|
|
Gruppe.__init__( self, Id, pids )
|
|
def istvoll(self, n=0):
|
|
if ((self._Anzahl + n) == self._Plaetze ):
|
|
return True
|
|
else:
|
|
return False
|
|
def istleer(self):
|
|
if (self._Anzahl == 0 ):
|
|
return True
|
|
else:
|
|
return False
|
|
def istNachbar(self,Tid):
|
|
if Tid in self.Nachbarn:
|
|
return True
|
|
else:
|
|
return False
|
|
def freiePlaetze(self):
|
|
return int( self._Plaetze - self._Anzahl )
|
|
def besetztePlaetze(self):
|
|
return self._Anzahl
|
|
def Person_dazu(self, P):
|
|
if (self._Anzahl == self._Plaetze ):
|
|
raise IndexError("Tisch voll")
|
|
else:
|
|
Gruppe.Person_dazu( self, P )
|
|
def Person_weg(self, P):
|
|
if (self._Anzahl == 0 ):
|
|
raise IndexError("Tisch schon leer")
|
|
else:
|
|
Gruppe.Person_weg( self, P )
|
|
def teilen(self):
|
|
pass
|
|
def __repr__(self):
|
|
result = ( Gruppe.__repr__( self ) +
|
|
' fuer [%s], %s noch frei,' %
|
|
(self._Plaetze,
|
|
self._Plaetze - self._Anzahl)
|
|
)
|
|
result = result + " Nachbarn:"
|
|
for N in chain(self.Nachbarn):
|
|
result = result + repr(N)
|
|
return result
|
|
def __getattr__(self, attrname):
|
|
if attrname == 'Anzahl':
|
|
return self._Anzahl
|
|
if attrname == 'Plaetze':
|
|
return self._Plaetze
|
|
elif attrname == 'Id':
|
|
return self.Id
|
|
else:
|
|
raise AttributeError(attrname)
|
|
|
|
|
|
# Containerklassen Personen, Gruppen, Tische, Plaetze
|
|
class Personen:
|
|
"""ein Pool mit mehreren Personen"""
|
|
def __init__( self, Personen=[] ):
|
|
self._Personen = set(Personen)
|
|
self.PvonId = {}
|
|
for P in Personen:
|
|
self.PvonId[ P.Id ] = P
|
|
def __repr__( self):
|
|
result = '+ PersonenListe:\n'
|
|
for P in self._Personen:
|
|
result = result + repr(P) + '\n'
|
|
return result
|
|
def PersonVonPid( self, SearchedId ):
|
|
return self.PvonId[SearchedId]
|
|
def PersonAdd( self, Person ):
|
|
self._Personen.add( Person )
|
|
Pid = Person.Id
|
|
self.PvonId[ Pid ] = Person
|
|
def ListeVonIds( self, PersonenIds ):
|
|
PersonenListe = []
|
|
for Id in PersonenIds:
|
|
PersonenListe.append( self.PersonVonPid( Id ) )
|
|
return PersonenListe
|
|
|
|
class Gruppen:
|
|
"""Containerklasse fuer Gruppenzusammenstellung """
|
|
Art = 'Gruppen'
|
|
def __init__( self, GruppenListe=[]):
|
|
self.GruppenListe = []
|
|
self.PvonPid = {}
|
|
self.GvonPid = {}
|
|
self.NLeute = 0
|
|
for G in chain( GruppenListe ):
|
|
self.Gruppe_dazu( G )
|
|
print("gruppen hinzugefuegt")
|
|
def __iter__( self):
|
|
sortiert = self.GruppenListe[:]
|
|
sortiert.sort()
|
|
for G in sortiert:
|
|
yield(G)
|
|
def __repr__( self):
|
|
result = "-- "+self.Art+"aufstellung:\n"
|
|
for G in chain( self.GruppenListe):
|
|
result = result + repr(G)+ '\n'
|
|
result = result + "Gesamtanzahl der Leute: " + repr(self.NLeute) + "\n"
|
|
return result
|
|
def vonId(self, SearchedId):
|
|
for G in chain(self.GruppenListe):
|
|
if ( int(G.Id) == SearchedId ):
|
|
return G
|
|
def PersonVonPid(self, SearchedId):
|
|
return self.PvonPid[SearchedId]
|
|
def GruppeVonPid(self, SearchedId):
|
|
return self.GvonPid[SearchedId]
|
|
def Gruppe_dazu(self, G):
|
|
self.GruppenListe.append( G )
|
|
self.NLeute = self.NLeute + G._Anzahl
|
|
for P in G:
|
|
self.PvonPid[ P.Id ] = P
|
|
self.GvonPid[ P.Id ] = G
|
|
|
|
class Tische:
|
|
"""Containerklasse fuer Tischaufstellung """
|
|
Art = 'Tisch'
|
|
def __init__( self, TischListe =[] ):
|
|
self.TischListe = TischListe
|
|
self.Groesster = 0
|
|
self.NStuehle = 0
|
|
self.IdGroesster = 0
|
|
# stelle den groessten Tisch fest
|
|
# und zaehle die Plaetze
|
|
for T in chain( TischListe ):
|
|
self.NStuehle = self.NStuehle + T.Plaetze
|
|
if (T.Plaetze > self.Groesster ):
|
|
self.Groesster = T.Plaetze
|
|
self.IdGroesster = T.Id
|
|
def __repr__( self ):
|
|
result = "-- "+self.Art+"aufstellung:\n"
|
|
for T in chain(self.TischListe):
|
|
result = result + repr(T)+ '\n'
|
|
result = ( result + " Groesster Tisch: "
|
|
+ repr(self.IdGroesster)
|
|
+ "( " + repr(self.Groesster) + " )\n" )
|
|
return result
|
|
def Tisch_dazu(self, T):
|
|
self.NStuehle = self.NStuehle + T.Plaetze
|
|
self.TischListe.append( T )
|
|
if (T.Plaetze > self.Groesster ):
|
|
self.Groesster = T.Plaetze
|
|
self.IdGroesster = T.Id
|
|
def __iter__( self):
|
|
sortiert = self.TischListe[:]
|
|
sortiert.sort()
|
|
for T in sortiert:
|
|
yield(T)
|
|
def laden( self, FileName ):
|
|
## print("-- Tische laden")
|
|
config = configparser.ConfigParser()
|
|
config.read( os.path.expanduser(FileName) )
|
|
for section in config.sections():
|
|
Tid = int(section)
|
|
# ast.literal_eval statt eval: tische.ini kann eine hochgeladene
|
|
# Fremddatei sein (Web-M1), da darf kein beliebiger Code laufen.
|
|
# Erlaubt weiterhin Zahlen, Tupel und Listen wie zuvor.
|
|
Nummer = ast.literal_eval(config.get( section, 'Nummer' ))
|
|
nPl = ast.literal_eval(config.get( section, 'Plaetze' ))
|
|
Koords = ast.literal_eval(config.get( section, 'Koordinaten' ))
|
|
Nachbarn = ast.literal_eval(config.get( section, 'Nachbarliste' ))
|
|
Hofid = ast.literal_eval(config.get( section, 'Hof' ))
|
|
T = Tisch( Tid, Plaetze=nPl, Koordinaten=Koords, Nachbarliste=Nachbarn, Hof=Hofid, Nummer=Nummer )
|
|
self.Tisch_dazu( T )
|
|
def vonId(self, SearchedId):
|
|
for T in chain(self.TischListe):
|
|
if ( int(T.Id) == SearchedId ):
|
|
return T
|
|
def deepcopy(self):
|
|
NeueTischliste = []
|
|
for T in chain( self.TischListe ):
|
|
TNeu = copy.deepcopy( T )
|
|
NeueTischliste.append( TNeu )
|
|
NeueTische = Tische( NeueTischliste )
|
|
return NeueTische
|
|
|
|
|
|
|
|
# Klasse fuer Information der Verteilung der Sitze
|
|
class Plaetze:
|
|
"""Informationen ueber freie oder benachbarte Plaetze"""
|
|
def __init__(self, Tische):
|
|
# je Anzahl freier Plaetze ein Set von Tisch-Ids. defaultdict(set)
|
|
# erspart die manuelle Vorbelegung und liefert fuer noch nie
|
|
# gesehene Fuellstaende automatisch ein leeres Set.
|
|
self.__TIDsNFreie = defaultdict(set)
|
|
self.__Groesster = Tische.Groesster
|
|
self.__Tische = Tische
|
|
for T in Tische.TischListe:
|
|
n_freie = T.freiePlaetze()
|
|
self.__TIDsNFreie[n_freie].add(T.Id)
|
|
def __repr__(self):
|
|
result = "\n-- Tische mit n Freien Plaetzen:\n"
|
|
for i in range(self.__Groesster+1):
|
|
# .get statt [i], damit das Anzeigen keine leeren Eintraege
|
|
# ins defaultdict schreibt
|
|
result = result + " " +repr(i) + " :" + repr(self.__TIDsNFreie.get(i, set())) + "\n"
|
|
return result
|
|
def IdsnFreie(self, n_freie):
|
|
"""gibt ein Set mit allen Tischids zurueck, wo noch X freie Plaetze sind"""
|
|
return self.__TIDsNFreie[n_freie]
|
|
def nFreie(self, n_freie):
|
|
"""gibt einen Tisch zurueck, wo noch X freie Plaetze sind"""
|
|
try:
|
|
TischId = int(self.__TIDsNFreie[n_freie].pop())
|
|
# aber veraendere deswegen die Liste nicht
|
|
self.__TIDsNFreie[n_freie].add(TischId)
|
|
return self.__Tische.vonId(TischId)
|
|
except KeyError:
|
|
return None
|
|
def mindestensNFreie(self, n_freie):
|
|
"""gibt einen Tisch zurueck, wo noch X oder mehr freie Plaetze sind"""
|
|
## print "-- Plaetze.mindestensNFreie", n_freie
|
|
## print "-- Plaetze.mindestensNFreie.Groesster", self.__Groesster
|
|
N = n_freie
|
|
if n_freie > self.__Groesster:
|
|
return None
|
|
while N < self.__Groesster+1:
|
|
T = self.nFreie(N)
|
|
if T is None:
|
|
N = N + 1
|
|
else:
|
|
return T
|
|
return None
|
|
def NachbarTische(self, n1, n2):
|
|
"""gibt zwei Nachbartische zurueck, wo zwei Gruppen hinpassen
|
|
|
|
falls durch eine Gruppe ein Tisch voll werden wuerde, ist voll=1
|
|
falls beide Tisch gefuellt werden wuerden, ist voll=2
|
|
"""
|
|
# zuerst versuchen einen Tisch fuer die groessere der beiden Gruppen zu finden
|
|
# falls es nicht klappt, wirds auch keine Nachbarn geben...
|
|
n = max([n1,n2])
|
|
T = self.mindestensNFreie( n )
|
|
if T is None:
|
|
return [None,None]
|
|
|
|
T1 = self.mindestensNFreie(n1)
|
|
|
|
# davon nun die Nachbarn abklappern, ob ein Tisch dabei ist,
|
|
# wo noch genuegend Plaetze frei sind
|
|
n_Nachbar = n2
|
|
IdNachbarTisch = 0
|
|
while n_Nachbar < self.__Groesster+1:
|
|
# ein Set bauen, das aus Tischids besteht, die sowohl Nachbarn sind,
|
|
# als auch mindestens die gewuenschte Anzahl von Plaetzen frei hat
|
|
NeueNachbarn = self.__TIDsNFreie[n_Nachbar] & T1.Nachbarn
|
|
try:
|
|
IdNachbarTisch = NeueNachbarn.pop()
|
|
T2 = self.__Tische.vonId( IdNachbarTisch )
|
|
return [T1,T2]
|
|
except KeyError:
|
|
n_Nachbar = n_Nachbar + 1
|
|
return [None,None]
|
|
def update(self, Tisch, n_freivorher):
|
|
"""aktualisiere die aktuelle Platzverteilung"""
|
|
## print "----------------"
|
|
## print "Plaetze.update"
|
|
self.__TIDsNFreie[n_freivorher].remove(Tisch.Id)
|
|
freiPlaetze = Tisch.freiePlaetze()
|
|
self.__TIDsNFreie[freiPlaetze].add(Tisch.Id)
|
|
## print "freie Plaetze: ", freiPlaetze, "Tischid", Tisch.Id, "\n"
|
|
|
|
# Strafliste. Was ist wie schlecht?
|
|
class Strafliste:
|
|
"""enthaelt die Informationen zur Vergabe der Wertigkeit einer Sitzplatzverteilung"""
|
|
def __init__(self, GruppenTrennen={}, NichtNachbar=-5, Allein=-20, TischWert={}):
|
|
self.GruppenTrennen = GruppenTrennen
|
|
self.NichtNachbar = NichtNachbar
|
|
self.Allein = Allein
|
|
self.TischWert = TischWert
|
|
def __repr__(self):
|
|
"""Drucke alle Bestrafungen auf der Konsole aus"""
|
|
return ("+ Strafliste:\n"
|
|
"Trennen von Gruppen (Groesse:Strafe): %s\n"
|
|
"Strafe kein Gruppenmitglied am Nachbartisch : %s\n"
|
|
"Strafe fuer eine Person allein: %s"
|
|
% ( self.GruppenTrennen,
|
|
self.NichtNachbar,
|
|
self.Allein) )
|
|
def gibPunkte(self, Typ, Gruppengroesse=0, N_Teilungen=0, freiePlaetze=0):
|
|
if Typ == 'allein':
|
|
return self.Allein
|
|
elif Typ == 'nichtNachbar':
|
|
return self.NichtNachbar
|
|
elif Typ == 'Tischbesetzung':
|
|
try:
|
|
return self.TischWert[freiePlaetze]
|
|
except KeyError:
|
|
return 0
|
|
elif Typ == 'Trennung':
|
|
if (N_Teilungen == 0):
|
|
return 0
|
|
elif (N_Teilungen == 1) & (Gruppengroesse < 2):
|
|
raise IndexError("Gruppe sind mindestens zwei Leute")
|
|
elif (N_Teilungen == 2) & (Gruppengroesse < 3):
|
|
raise IndexError("mindestens drei Leute noetig")
|
|
elif (N_Teilungen > 2):
|
|
return -50
|
|
if Gruppengroesse in self.GruppenTrennen[N_Teilungen]:
|
|
return self.GruppenTrennen[N_Teilungen][Gruppengroesse]
|
|
else:
|
|
return self.gibPunkte('Trennung',Gruppengroesse-1, N_Teilungen)
|
|
else:
|
|
raise IndexError("Bezeichner gibts nicht")
|
|
def laden( self, FileName ):
|
|
print("-- Strafliste laden")
|
|
config = configparser.ConfigParser()
|
|
config.read( os.path.expanduser(FileName) )
|
|
# ast.literal_eval statt eval: die Werte sind reine Literale
|
|
# (Zahlen und Dicts wie {2:-10}), also kein Code noetig.
|
|
self.NichtNachbar = ast.literal_eval(config.get( 'Globals', 'NichtNachbar' ))
|
|
self.Allein = ast.literal_eval(config.get( 'Globals', 'Allein' ))
|
|
self.TischWert = ast.literal_eval( config.get( 'Globals', 'TischWertigkeiten' ) )
|
|
for (k,v) in config.items( 'Trennung' ):
|
|
self.GruppenTrennen[int(k)] = ast.literal_eval(v)
|
|
|
|
|
|
# Sitzplatzverteilung, bestehend aus den Einzelelementen
|
|
@total_ordering
|
|
class Sitzplatzverteilung:
|
|
"""Loesungsvorschlag fuer eine Sitzplatzordung"""
|
|
|
|
# Aenderung der Sitzplatzverteilung (Aktion)
|
|
def __init__(self, Tische=[], Gruppen=[], Strafen='', VIPListe={}, VIPs=[] ):
|
|
"""erzeugt eine Sitzplatzverteilung nach dem Zufallsprinzip
|
|
|
|
Zufallgenerator, um zu entscheiden, nach welcher
|
|
Verteilungsart verfahren wird:
|
|
- zuerst die groessten Gruppen auf die passenden Tische
|
|
verteilen, solange sie noch an einen Tisch passen
|
|
oder
|
|
- zuerst alle kleinsten Gruppen, da man die nicht mehr teilen
|
|
sollte
|
|
"""
|
|
#print("+ Sitzplatzverteilung\n")
|
|
if Gruppen.NLeute > Tische.NStuehle:
|
|
print("Anzahl der Leute:", Gruppen.NLeute)
|
|
print("Anzahl der Plaetze:", Tische.NStuehle)
|
|
raise IndexError("zu wenig Stuehle")
|
|
|
|
self.MutationsGroesse = 2
|
|
self.VIP = VIPs
|
|
self.VIPListe = VIPListe
|
|
self.Gruppen = Gruppen
|
|
self.Strafen = Strafen
|
|
|
|
self.Tische = Tische.deepcopy()
|
|
## self.Tische = Tische
|
|
|
|
self.Groesster = Tische.Groesster
|
|
self.value = 0
|
|
self.Tid_Value = {}
|
|
self.Gid_Value = {}
|
|
self.Gid_Tid = {}
|
|
|
|
# zuerst die VIPs an die richtigen Tische setzen
|
|
self.VIPsSetzen( VIPListe, VIPs, self.Tische )
|
|
|
|
# fuehre Buch ueber die ab jetzt freien Plaetze
|
|
self.info = Plaetze( self.Tische )
|
|
|
|
# alle Gruppen auf die Tische verteilen
|
|
self.GruppenSetzen( Gruppen )
|
|
|
|
# die Sitzplatzverteilung bepunkten
|
|
self.bewerten()
|
|
print("erzeugt: ", self.value)
|
|
def __repr__(self):
|
|
"""Ausgabe der Sitzplatzverteilung an der Konsole"""
|
|
r = "-- Sitzplatzverteilung:\n"
|
|
Tids = []
|
|
for T in self.Tische:
|
|
Tids.append(T.Id)
|
|
Tids.sort()
|
|
for Tid in Tids:
|
|
T = self.Tische.vonId(Tid)
|
|
r = r + repr(T) + "\n"
|
|
r = r + " Gruppenwertigkeiten:\n"
|
|
Gids = []
|
|
for G in self.Gruppen:
|
|
Gids.append(G.Id)
|
|
Gids.sort()
|
|
for Gid in Gids:
|
|
G = self.Gruppen.vonId(Gid)
|
|
r = r + " Gruppe " + repr(G.Id) + " (" + repr(self.GruppenWertGet(G)) + ")"
|
|
r = r + ", Teilungen: " + repr(self.GruppenTeilungGet(G)) + "\n"
|
|
r = r + " Tischwertigkeiten:\n"
|
|
for Tid in Tids:
|
|
T = self.Tische.vonId(Tid)
|
|
r = r + " Tisch " + repr(T.Id) + " (" + repr(self.TischWertGet(T)) + ")\n"
|
|
r = r + "Wertigkeit:" + repr(self.value) + "\n"
|
|
return r
|
|
def speichern(self, OutputTP, OutputPT ):
|
|
"""Ausgabe der Sitzplatzverteilung in ein File"""
|
|
Tids = []
|
|
for T in self.Tische:
|
|
Tids.append(T.Id)
|
|
Tids.sort()
|
|
Liste = []
|
|
|
|
# rausschreiben Tisch -> Personen
|
|
fout = open( OutputTP, 'w+')
|
|
for Tid in Tids:
|
|
T = self.Tische.vonId(Tid)
|
|
fout.write( "["+ repr(T.Id)+ "]\n" )
|
|
PlatzNummer = 1
|
|
for P in T:
|
|
str = ""
|
|
str = str + P._Titel + " "
|
|
str = str + P._Vorname + " "
|
|
str = str + P._Name + " = "
|
|
str = str + "Hof " + repr(T.Hof) + ", "
|
|
str = str + "Tisch "+ repr( T.Nummer ) + ", "
|
|
str = str + "Nummer "+ repr( PlatzNummer )
|
|
str = str + "\n"
|
|
PlatzNummer = PlatzNummer + 1
|
|
fout.write( str )
|
|
|
|
str = ""
|
|
str = str + P._Name + ", "
|
|
str = str + P._Vorname + ", "
|
|
str = str + P._Titel + " = "
|
|
str = str + "Hof " + repr(T.Hof) + ", "
|
|
str = str + "Tisch "+ repr( T.Nummer ) + ", "
|
|
str = str + "Nummer "+ repr( PlatzNummer )
|
|
str = str + "\n"
|
|
Liste.append( str )
|
|
|
|
fout.write( "\n" )
|
|
fout.close()
|
|
|
|
# rausschreiben Personen -> Tisch
|
|
Liste.sort()
|
|
fout = open( OutputPT, 'w+')
|
|
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) )
|
|
|
|
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) )
|
|
|
|
# Tischnummer zentriert in die Tischmitte (nach den Personen-
|
|
# kreisen gezeichnet, damit sie oben liegt; ein halbtransparenter
|
|
# weisser Kreis darunter haelt sie auch ueber Personenkreisen
|
|
# lesbar, falls die Tischmitte belegt sein sollte)
|
|
Teile.append(
|
|
'<circle cx="%g" cy="%g" r="%g" fill="white" '
|
|
'fill-opacity="0.75"/>\n' % (Mx, My, PersonenRadius) )
|
|
Teile.append(
|
|
'<text x="%g" y="%g" text-anchor="middle" '
|
|
'dominant-baseline="central" font-size="%g" '
|
|
'font-weight="bold" fill="black">%s</text>\n'
|
|
% (Mx, My, PersonenRadius * 1.1, T.Nummer) )
|
|
|
|
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)
|
|
SchlechteGruppen = []
|
|
for Gid in IdsSchlechteGruppen:
|
|
G = self.Gruppen.vonId(Gid)
|
|
self.GruppeEntfernen(G)
|
|
SchlechteGruppen.append(G)
|
|
self.GruppenSetzen( SchlechteGruppen )
|
|
self.bewerten()
|
|
|
|
print("mutiert: ", self.value)
|
|
return self
|
|
def __eq__(self,other):
|
|
return self.value == other.value
|
|
def __lt__(self,other):
|
|
return self.value < other.value
|
|
def bewerten(self):
|
|
"""Errechnet die momentane Wertigkeit der Sitzplaetze"""
|
|
self.GesamtWertSet( 0 )
|
|
# Tische bewerten
|
|
for T in self.Tische:
|
|
#print(T)
|
|
n_freie = T.freiePlaetze()
|
|
Strafpunkte = self.Strafen.gibPunkte( 'Tischbesetzung', freiePlaetze=n_freie )
|
|
self.TischWertSet( T, Strafpunkte )
|
|
self.GesamtWertAdd( Strafpunkte )
|
|
|
|
#self.GruppenWertAdd( Gruppe, self.Strafen.gibPunkte( 'nichtNachbar') )
|
|
|
|
# schauen wie oft eine Gruppe getrennt wurde
|
|
for G in self.Gruppen:
|
|
NTeilungen = self.GruppenTeilungGet(G)
|
|
Strafpunkte = self.Strafen.gibPunkte( 'Trennung', G.Anzahl(), NTeilungen )
|
|
self.GruppenWertSet( G, Strafpunkte )
|
|
self.GesamtWertAdd( Strafpunkte )
|
|
|
|
# schauen ob Leute an einem Tisch allein sind
|
|
Strafpunkte = self.Strafen.gibPunkte( 'allein' )
|
|
for T in self.Tische:
|
|
Gids = self.LeuteAllein( T )
|
|
for Gid in Gids:
|
|
G = self.Gruppen.vonId( Gid )
|
|
self.GruppenWertAdd( G, Strafpunkte )
|
|
self.GesamtWertAdd( Strafpunkte )
|
|
def Wertigkeit(self):
|
|
"""Ausgabe der Guete der Sitzplatzverteilung an der Konsole"""
|
|
return ' L '+ repr(self.value)
|
|
|
|
# Personenspezifische Funktionen
|
|
def PersonSetzen(self, Person, Tisch):
|
|
"""setzt eine Person an den Tisch"""
|
|
Tisch.Person_dazu( Person )
|
|
def PersonEntfernen(self, Person, Tisch):
|
|
"""entfernt eine Person von dem Tisch"""
|
|
Tisch.Person_weg( Person )
|
|
FreiVorher = Tisch.freiePlaetze()
|
|
# der Tisch hat jetzt weniger freie Plaetze
|
|
self.info.update( Tisch, FreiVorher )
|
|
def PlatzTauschen(self, neuePerson, altePerson, Tisch):
|
|
"""Tauscht zwischen zwei Personen den Sitzplatz"""
|
|
self.PersonEntfernen( altePerson, Tisch )
|
|
self.PersonSetzen( neuePerson, Tisch )
|
|
|
|
# Gruppenspezifische Funktionen
|
|
def GruppenSetzen(self, Gruppen ):
|
|
"""setzt mal die grossen mal die kleinen Gruppen auf beliebige Plaetze"""
|
|
# Mache Liste der Gruppen, der Groesse nach sortiert
|
|
Gruppenliste = []
|
|
for G in Gruppen:
|
|
Gruppenliste.append(G)
|
|
|
|
# uns setzte sie auf freie Plaetze
|
|
while len(Gruppenliste) > 0:
|
|
wahl = choice(['grosse', 'kleine'])
|
|
if wahl == 'grosse':
|
|
G = Gruppenliste.pop(0)
|
|
else:
|
|
G = Gruppenliste.pop(-1)
|
|
self.GruppeSetzen(G)
|
|
def GruppeSetzen(self, Gruppe, Tisch=None ):
|
|
"""setzt eine Gruppe irgendwohin wo Platz ist
|
|
oder an einen gewuenschten Tisch
|
|
"""
|
|
## print "---------------------"
|
|
## print "GruppeSetzen: Gruppe:", Gruppe
|
|
if Tisch is None:
|
|
Tisch = self.info.mindestensNFreie(Gruppe.Anzahl())
|
|
|
|
# kein Tisch mehr in der gewuenschten groesse frei.
|
|
# also Gruppe trennen
|
|
if Tisch is None:
|
|
# neue Gruppengroesse zufaellig bestimmen
|
|
Teil1 = choice( range(Gruppe.Anzahl()) )
|
|
[G1, G2] = Gruppe.teilen( Teil1 )
|
|
|
|
# Benachbarte Tische suchen die gross genug sind
|
|
n1 = G1.Anzahl()
|
|
n2 = G2.Anzahl()
|
|
[TA,TNA] = self.info.NachbarTische(n1, n2)
|
|
[TB,TNB] = self.info.NachbarTische(n2, n1)
|
|
if TA is None and TB is None:
|
|
# keine Nachbarn gefunden, also irgendwo hinsetzen
|
|
self.GruppeSetzen(G1)
|
|
self.GruppeSetzen(G2)
|
|
elif TA is None:
|
|
self.GruppeSetzen(G2,TB)
|
|
self.GruppeSetzen(G1,TNB)
|
|
elif TB is None:
|
|
self.GruppeSetzen(G1,TA)
|
|
self.GruppeSetzen(G2,TNA)
|
|
else:
|
|
# nimm die Kombination, die die Tische am besten fuellt
|
|
vollA = 0
|
|
vollB = 0
|
|
if TA.istvoll(n1): vollA = vollA + 1
|
|
if TNA.istvoll(n2): vollA = vollA + 1
|
|
if TB.istvoll(n2): vollB = vollB + 1
|
|
if TNB.istvoll(n1): vollB = vollB + 1
|
|
if vollA > vollB:
|
|
self.GruppeSetzen(G1,TA)
|
|
self.GruppeSetzen(G2,TNA)
|
|
else:
|
|
self.GruppeSetzen(G2,TB)
|
|
self.GruppeSetzen(G1,TNB)
|
|
else:
|
|
FreiVorher = Tisch.freiePlaetze()
|
|
for P in Gruppe:
|
|
self.PersonSetzen(P, Tisch)
|
|
# der Tisch hat jetzt weniger freie Plaetze
|
|
self.info.update( Tisch, FreiVorher )
|
|
# zur Gruppe merken, an welchen Tischen sie jetzt sitzt
|
|
try:
|
|
Ids = self.Gid_Tid[Gruppe.Id]
|
|
Ids.add(Tisch.Id)
|
|
except KeyError:
|
|
s = set()
|
|
s.add(Tisch.Id)
|
|
self.Gid_Tid[Gruppe.Id] = s
|
|
def GruppeEntfernen(self, Gruppe):
|
|
"""entfernt eine Gruppe von Ihren Tischen"""
|
|
TischIds = self.Gid_Tid[Gruppe.Id]
|
|
|
|
for Tid in TischIds:
|
|
T = self.Tische.vonId(Tid)
|
|
FreiVorher = T.freiePlaetze()
|
|
for P in Gruppe:
|
|
#print "P:", P
|
|
try:
|
|
self.PersonEntfernen( P, T )
|
|
except IndexError:
|
|
pass
|
|
except KeyError:
|
|
pass
|
|
self.info.update( T, FreiVorher )
|
|
del self.Gid_Tid[Gruppe.Id]
|
|
# der Tisch hat jetzt weniger freie Plaetze
|
|
# print self.Tische
|
|
|
|
# Bewertungsfunktionen (Evaluation)
|
|
# entscheiden bei der Mutation der Sitzplatzverteilung welche Tische als
|
|
# erstes wieder aufgeloest werden und welche nicht so schnell
|
|
def GesamtWertSet(self, Wert):
|
|
"""setzt die Gesamtwertung auf einen bestimmten Wert"""
|
|
self.value = Wert
|
|
def GesamtWertGet(self):
|
|
"""gibt den Wert der Gesamtwertung zurueck"""
|
|
return self.value
|
|
def GesamtWertAdd(self, Wert):
|
|
"""fuegt den Wert zur Gesamtwertung hinzu"""
|
|
self.value = self.value + Wert
|
|
def GruppenWertSet(self, Gruppe, Wert):
|
|
self.Gid_Value[Gruppe.Id] = Wert
|
|
def GruppenWertGet(self, Gruppe):
|
|
"""gibt die Zufriedenheit einer Gruppe zurueck
|
|
|
|
je nachdem, wie oft die Gruppe oft geteilt wurde
|
|
(je nach deren Groesse, bei grossen laesst es sich nicht vermeiden)
|
|
"""
|
|
try:
|
|
return self.Gid_Value[Gruppe.Id]
|
|
except KeyError:
|
|
return 0
|
|
def GruppenWertAdd(self, Gruppe, Wert):
|
|
"""fuegt den Wert der Gruppenwertigkeit hinzu """
|
|
try:
|
|
self.Gid_Value[Gruppe.Id] = self.Gid_Value[Gruppe.Id] + Wert
|
|
except KeyError:
|
|
self.Gid_Value[Gruppe.Id] = Wert
|
|
def TischWertSet(self, Tisch, Wert):
|
|
"""setzt die Wertigkeit dieses Tisches auf einen bestimmten Wert"""
|
|
self.Tid_Value[Tisch.Id] = Wert
|
|
def TischWertGet(self, Tisch):
|
|
"""Gib die Wertigkeit eines Tisches zurueck
|
|
|
|
- wenn viele unzufriedene Leute an dem Tisch sitzen,
|
|
ist die Wertigkeit schlecht
|
|
- wenn nur noch ein Platz frei ist,
|
|
ist die Wertigkeit des Tisches ebenfalls schlecht
|
|
weil man niemanden mehr dort hinsetzen kann
|
|
- wenn die Leute der eigenen Gruppe nicht an den Nachbartischen sitzen,
|
|
ist die Wertigkeit auch nicht gut
|
|
"""
|
|
try:
|
|
return self.Tid_Value[Tisch.Id]
|
|
except KeyError:
|
|
return 0
|
|
|
|
def LeuteAllein(self, Tisch):
|
|
"""Stellt fest wieviele Leute an diesem Tisch alleine sind,
|
|
weil sie zu einer Gruppe gehoeren,
|
|
die an diesem Tisch nicht vertreten ist
|
|
"""
|
|
Anzahl_von_Gid = {}
|
|
Gids = []
|
|
for P in Tisch:
|
|
try:
|
|
G = self.Gruppen.GruppeVonPid(P.Id)
|
|
except KeyError:
|
|
# Person gehoert zu keiner Gruppe in self.Gruppen (z.B. VIP)
|
|
continue
|
|
try:
|
|
Anzahl_von_Gid[G.Id] = Anzahl_von_Gid[G.Id] + 1
|
|
except KeyError:
|
|
Anzahl_von_Gid[G.Id] = 1
|
|
for Gid, Anzahl in Anzahl_von_Gid.items():
|
|
if Anzahl == 1:
|
|
Gids.append(Gid)
|
|
return Gids
|
|
|
|
|
|
# Funktionen fuer mehrere Einheiten (Information)
|
|
def SchlechteTischeTopX(self, Anzahl=-1):
|
|
"""gibt eine List mit X Tischids zurueck, die schlechte Wertungen haben"""
|
|
return self.TopX( self.Tid_Value, Anzahl )
|
|
def SchlechteGruppenTopX(self, Anzahl=-1):
|
|
"""gibt eine List mit X Gruppenids zurueck, die schlechte Wertungen haben"""
|
|
return self.TopX( self.Gid_Value, Anzahl )
|
|
|
|
# Hilfsfunktionen
|
|
def GruppenTeilungGet(self, Gruppe):
|
|
try:
|
|
return len( self.Gid_Tid[Gruppe.Id] ) - 1
|
|
except KeyError:
|
|
return 0
|
|
def VIPsSetzen(self, VIPListe, VIPs, Tische ):
|
|
"""setze die VIPs auf Ihre Plaetze"""
|
|
#print " VIPsSetzen:"
|
|
for PersonId, TischId in VIPListe.items():
|
|
#print " VIPsSetzen: PersonId", PersonId
|
|
#print " VIPsSetzen: TischId", TischId
|
|
Person = VIPs.PersonVonPid(PersonId)
|
|
Tisch = Tische.vonId(TischId)
|
|
self.PersonSetzen( Person, Tisch )
|
|
def TopX(self, Liste, Anzahl=-1 ):
|
|
"""gibt eine Liste mit X Ids zurueck, die die schlechtesten Wertungen haben
|
|
|
|
Achtung: Dies ist bewusst *nicht* heapq.nsmallest. Der laufende
|
|
Schwellwert Min sinkt monoton mit, so dass nur Ids aufgenommen
|
|
werden, die waehrend der Iteration einen neuen Tiefpunkt erreichen
|
|
oder gleichziehen. Das Ergebnis haengt an der Iterationsreihenfolge
|
|
und praegt das Mutationsverhalten des GA (mutieren() ->
|
|
SchlechteGruppenTopX). Eine Umstellung auf heapq wuerde die
|
|
Loesungsfindung veraendern und ist daher bewusst unterlassen.
|
|
"""
|
|
Min = 0
|
|
RListe=[]
|
|
Wertigkeit=[]
|
|
for Id, Wert in Liste.items():
|
|
if Wert <= Min:
|
|
RListe.append(Id)
|
|
Wertigkeit.append(Wert)
|
|
Min = Wert
|
|
return [RListe[-Anzahl:], Wertigkeit[-Anzahl:] ]
|
|
|
|
|
|
# Sitzplatzverteilung, bestehend aus den Einzelelementen
|
|
class JSONConfig:
|
|
"""liest die Gaesteliste aus einer bestellung.json ein
|
|
(Nachfolger des frueheren XMLConfig; JSON hat die alte XML-Form
|
|
abgeloest, siehe doc/dateiformate.md).
|
|
|
|
Erwartete Struktur:
|
|
{
|
|
"gruppen": [
|
|
{
|
|
"name": "Familie Huber", # optional, nur Anzeige
|
|
"tisch": 1, # optional -> VIP-Fixplatz
|
|
"anzahl": 1, # optional -> Sammelbuchung
|
|
"personen": [
|
|
{"vorname": "Anna", "nachname": "Huber", "titel": ""}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
|
|
"tisch" macht die ganze Gruppe zu VIPs an diesem Tisch (laeuft
|
|
nicht durch die GA-Platzierung). "anzahl" > 1 vervielfacht jede
|
|
Person der Gruppe in so viele identische Buchungen (anonyme
|
|
Sammelbuchung). "titel" ist optional.
|
|
"""
|
|
def HandleBestellung(self, Daten):
|
|
"""die geladene JSON-Struktur in Personen/Gruppen/VIPs zerlegen
|
|
|
|
Rueckgabe: [ PN, GN, VIPGruppe, VipHash, GruppenNamen ]
|
|
(wie CSVConfig; das fruehere XMLConfig gab die ersten vier
|
|
Elemente zurueck - Aufrufer, die GruppenNamen nicht brauchen,
|
|
koennen es ignorieren.)
|
|
"""
|
|
if not isinstance( Daten, dict ) or 'gruppen' not in Daten:
|
|
raise ValueError("bestellung.json braucht ein Objekt mit dem "
|
|
"Schluessel 'gruppen'")
|
|
|
|
PN = Personen()
|
|
GN = Gruppen()
|
|
VIPGruppe = Gruppe( 0 )
|
|
VipHash = {}
|
|
GruppenNamen = {}
|
|
|
|
PID = 0
|
|
GID = 0
|
|
for Index, GruppeJSON in enumerate( Daten['gruppen'] ):
|
|
if 'personen' not in GruppeJSON:
|
|
raise ValueError("Gruppe %d: 'personen' fehlt" % (Index + 1))
|
|
|
|
IsVip = 'tisch' in GruppeJSON and GruppeJSON['tisch'] is not None
|
|
if IsVip:
|
|
try:
|
|
TischNr = int( GruppeJSON['tisch'] )
|
|
except (TypeError, ValueError):
|
|
raise ValueError("Gruppe %d: 'tisch' ist keine "
|
|
"Tischnummer" % (Index + 1))
|
|
|
|
try:
|
|
AnzahlReservierungen = int( GruppeJSON.get( 'anzahl', 1 ) )
|
|
except (TypeError, ValueError):
|
|
raise ValueError("Gruppe %d: 'anzahl' ist keine Zahl"
|
|
% (Index + 1))
|
|
if AnzahlReservierungen < 1:
|
|
raise ValueError("Gruppe %d: 'anzahl' muss >= 1 sein"
|
|
% (Index + 1))
|
|
|
|
# regulaere (nicht-VIP) Gruppen bekommen eine GID
|
|
if not IsVip:
|
|
GID = GID + 1
|
|
G = Gruppe( GID )
|
|
GruppenNamen[ GID ] = GruppeJSON.get( 'name', 'Gruppe %d' % GID )
|
|
|
|
for PersonJSON in GruppeJSON['personen']:
|
|
Vorname = str( PersonJSON.get( 'vorname', '' ) ).strip()
|
|
Nachname = str( PersonJSON.get( 'nachname', '' ) ).strip()
|
|
Titel = str( PersonJSON.get( 'titel', '' ) ).strip()
|
|
if Vorname == '' or Nachname == '':
|
|
raise ValueError("Gruppe %d: jede Person braucht Vorname "
|
|
"und Nachname" % (Index + 1))
|
|
for i in range( AnzahlReservierungen ):
|
|
PID = PID + 1
|
|
P = Person( PID, Vorname, Nachname, Titel )
|
|
if IsVip:
|
|
VipHash[ PID ] = TischNr
|
|
VIPGruppe.Person_dazu( P )
|
|
else:
|
|
PN.PersonAdd( P )
|
|
G.Person_dazu( P )
|
|
|
|
if not IsVip:
|
|
if G.Anzahl() > 0:
|
|
GN.Gruppe_dazu( G )
|
|
else:
|
|
# leere Gruppe wieder verwerfen
|
|
del GruppenNamen[ GID ]
|
|
GID = GID - 1
|
|
|
|
return [ PN, GN, VIPGruppe, VipHash, GruppenNamen ]
|
|
|
|
def laden(self, Bestellung):
|
|
"""lese alle Eingangsdaten aus einer JSON-Datei ein"""
|
|
import json
|
|
# utf-8-sig schluckt ein evtl. vorhandenes BOM
|
|
fsock = open( Bestellung, 'r', encoding='utf-8-sig' )
|
|
Daten = json.load( fsock )
|
|
fsock.close()
|
|
return self.HandleBestellung( Daten )
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
print("\n-- Erzeuge Personen:")
|
|
P1 = Person( 1, 'Michael', 'Stangl')
|
|
P2 = Person( 2, 'Magnus', 'Mueller')
|
|
P3 = Person( 3, 'Bernd Robert', 'Hoehn')
|
|
P4 = Person( 4, 'Heinz', 'Ruehmann')
|
|
P5 = Person( 5, 'Franz', 'Bruegge')
|
|
P6 = Person( 6, 'Hubert', 'K')
|
|
print(P1)
|
|
print(P2)
|
|
print(P3)
|
|
print(P4)
|
|
print(P5)
|
|
print(P6)
|
|
|
|
print("\n-- Mehrzahl von Personen:")
|
|
PN = Personen([P1, P2, P3, P4, P5, P6])
|
|
print(PN)
|
|
|
|
print("\n-- Erzeuge Gruppen mit den Personen:")
|
|
G1 = Gruppe( 1, [P1, P2] )
|
|
G1.Person_dazu( P3 )
|
|
G2 = Gruppe( 2 )
|
|
G2.Person_dazu( P4 )
|
|
G3 = Gruppe( 3, [P5,P6] )
|
|
|
|
print("\n-- Stelle Gruppen dar:")
|
|
print(G1)
|
|
print(G2)
|
|
print(G3)
|
|
|
|
print("\n-- Durchlaufe Gruppe 3:")
|
|
for P in G3:
|
|
print(P)
|
|
|
|
print("\n-- Mache Liste der Gruppen:")
|
|
GN = Gruppen([G1,G2,G3])
|
|
print(GN)
|
|
|
|
print("\n-- Durchlaufe Gruppen sortiert:")
|
|
for G in GN:
|
|
print(G)
|
|
|
|
print("\n-- Teile Gruppe:")
|
|
[GX, GY] = G1.teilen()
|
|
print(GX)
|
|
print(GY)
|
|
[GX, GY] = G1.teilen(3)
|
|
print(GX)
|
|
print(GY)
|
|
|
|
print("\n-- Erzeuge Tische mit Personen:")
|
|
T1 = Tisch( 1, Plaetze=5, Koordinaten=(1,2), pids=[P1, P2], Nachbarliste=[2] )
|
|
print(T1)
|
|
|
|
T2 = Tisch( 2, Plaetze=4, Koordinaten=(3,2), Nachbarliste=[1,3] )
|
|
T2.Person_dazu( P3 )
|
|
print(T2)
|
|
T2.Person_dazu( P4 )
|
|
print(T2)
|
|
T2.Person_weg( P4 )
|
|
print(T2)
|
|
T3 = Tisch( 3, Plaetze=2, Koordinaten=(5,2), Nachbarliste=[2] )
|
|
print("\n-- Ist Tisch 2 voll oder leer?:")
|
|
print("ist voll = ", T2.istvoll())
|
|
print("ist leer = ", T2.istleer())
|
|
|
|
print("\n-- Wieviele Plaetze sind an Tisch 1 noch frei ?:")
|
|
print("Freie Plaetze = ", T1.freiePlaetze())
|
|
print("Besetzte Plaetze = ", T1.besetztePlaetze())
|
|
|
|
print("\n-- Stelle Tische dar:")
|
|
print(T1)
|
|
print(T2)
|
|
print(T3)
|
|
print("\n-- Ist Person Nummer 3 an Tisch 1 ?:")
|
|
print(P1 in T1)
|
|
print("-- Ist Person Nummer 4 an Tisch 1 ?:")
|
|
print(P4 in T1)
|
|
|
|
TE = Tische([T1,T2,T3])
|
|
print(TE)
|
|
|
|
print("\n-- Hole Tisch 2 aufgrund seiner Id :")
|
|
TX = TE.vonId( 2 )
|
|
print(TX)
|
|
|
|
print("\n-- Hole Gruppe 2 aufgrund ihrer Id :")
|
|
GX = GN.vonId( 2 )
|
|
print(GX)
|
|
|
|
print("\n-- Hole Person 2 aufgrund ihrer Id aus den Gruppen:")
|
|
PX = GN.PersonVonPid( 2 )
|
|
print(PX)
|
|
|
|
print("\n-- schau wo noch mindestens 3 Plaetze frei sind:")
|
|
info = Plaetze(TE)
|
|
print(info.mindestensNFreie( 3 ))
|
|
|
|
print("\n-- schau wo zwei Gruppen bestimmter Groesse nebeneinander sitzen koennen:")
|
|
print(" zwei neben drei:")
|
|
[TX,TY] = info.NachbarTische(2, 3)
|
|
print(TX)
|
|
print(TY)
|
|
print(" drei neben zwei:")
|
|
[TX,TY] = info.NachbarTische(3, 2)
|
|
print(TX)
|
|
print(TY)
|
|
|
|
P1 = Person( 1, 'Michael', 'Stangl')
|
|
P2 = Person( 2, 'Magnus', 'Mueller')
|
|
P3 = Person( 3, 'Bernd Robert', 'Hoehn')
|
|
P4 = Person( 4, 'Heinz', 'Ruehmann')
|
|
P5 = Person( 5, 'Marlene', 'Dietrich')
|
|
P6 = Person( 6, 'Sergio', 'Leone')
|
|
P7 = Person( 7, 'Walter', 'Emmerlich')
|
|
P8 = Person( 8, 'Harald', 'Junke')
|
|
P9 = Person( 9, 'Willi', 'Brandt')
|
|
P10 = Person( 10, 'Michael', 'Schumacher')
|
|
P11 = Person( 11, 'Ralf', 'Schumacher')
|
|
P12 = Person( 12, 'Bla', 'Button')
|
|
P13 = Person( 13, 'Heinz Harald', 'Frenzen')
|
|
|
|
#
|
|
# T1 T2 T3 T4
|
|
# <3> <5> <3> <4>
|
|
#
|
|
# G1 G2 G3 G4
|
|
# <3> <2> <2> <6>
|
|
#
|
|
print("\n-- Mache Gruppen:")
|
|
VIPGruppe = ( Gruppe( 1, [P1, P2, P3] ) )
|
|
G2 = ( Gruppe( 2, [P4, P5] ) )
|
|
G3 = ( Gruppe( 3, [P6, P7] ) )
|
|
G4 = ( Gruppe( 4, [P8, P9, P10, P11, P12, P13] ) )
|
|
GN = Gruppen([G2,G3,G4])
|
|
print(GN)
|
|
|
|
print("\n-- Mache Tische:")
|
|
T1 = ( Tisch( 1, Plaetze=3, Koordinaten=(1,1), Nachbarliste=[2] ) )
|
|
T2 = ( Tisch( 2, Plaetze=5, Koordinaten=(2,1), Nachbarliste=[1,3] ) )
|
|
T3 = ( Tisch( 3, Plaetze=3, Koordinaten=(3,1), Nachbarliste=[2,4] ) )
|
|
T4 = ( Tisch( 4, Plaetze=4, Koordinaten=(4,1), Nachbarliste=[3] ) )
|
|
|
|
TE = Tische([T1,T2,T3,T4])
|
|
print(TE)
|
|
|
|
print("\n-- Mache Listen:")
|
|
# Personenid - Tischid
|
|
VIPListePT = { 3:1, 2:1, 1:1 }
|
|
print("VIP Liste:", VIPListePT)
|
|
|
|
# Strafliste
|
|
# die Teilung einer grossen Gruppe ist nicht so schlimm
|
|
# wie die einer kleinen
|
|
Teilung = list()
|
|
Teilung.append({'x':'x'})
|
|
Teilung.append({2:-10, 3:-8, 4:-6, 6:-4, 10:-2})
|
|
Teilung.append({3:-20, 4:-8, 6:-6, 10:-4})
|
|
Teilung.append({4:-30, 6:-10, 10:-6})
|
|
# Ein Platz an einem Tisch frei ist schlechter als zwei Plaetze frei
|
|
TischWertigkeiten={ 1:-3, 2:-2, 3:-1 }
|
|
SL = Strafliste(GruppenTrennen=Teilung, NichtNachbar=-3, Allein=-30, TischWert=TischWertigkeiten)
|
|
print(SL)
|
|
|
|
print("\n-- Teste Strafliste:")
|
|
print("SL.gibPunkte('Trennung', 7,1):", SL.gibPunkte('Trennung', 7,1))
|
|
print("SL.gibPunkte('Trennung', 5,1):", SL.gibPunkte('Trennung', 5,1))
|
|
print("SL.gibPunkte('Trennung', 3,1):", SL.gibPunkte('Trennung', 3,1))
|
|
try:
|
|
print("SL.gibPunkte('Trennung',(1,1):",SL.gibPunkte('Trennung',1,1))
|
|
except IndexError:
|
|
print("Gruppe sind mindestens zwei")
|
|
|
|
print("\n-- Baue Sitzplatzverteilung:")
|
|
SV = Sitzplatzverteilung(Tische=TE, Gruppen=GN, Strafen=SL,
|
|
VIPListe=VIPListePT, VIPs=VIPGruppe)
|
|
print(SV)
|
|
|
|
print("\n-- Mutiere Sitzplatzverteilung:")
|
|
SV.mutieren()
|
|
print(SV)
|
|
|
|
print("\n-- Gibt die 3 Tische mit der schlechtesten Wertung:")
|
|
[TischIds, Wertigkeit]=SV.SchlechteTischeTopX(3)
|
|
print("TischIds:", TischIds)
|
|
print("Wertigkeit:", Wertigkeit)
|
|
|
|
print("\n-- Gibt die Gruppen mit der schlechtesten Wertung:")
|
|
[GruppenIds, Wertigkeit]=SV.SchlechteGruppenTopX(3)
|
|
print("GruppenIds:", GruppenIds)
|
|
print("Wertigkeit:", Wertigkeit)
|
|
|
|
|
|
else:
|
|
pass
|