""" 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 configparser, os import copy import math from functools import total_ordering from xml.dom import minidom # 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) Nummer = eval(config.get( section, 'Nummer' )) nPl = eval(config.get( section, 'Plaetze' )) Koords = eval(config.get( section, 'Koordinaten' )) Nachbarn = eval(config.get( section, 'Nachbarliste' )) Hofid = 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): # mache Listen mit den Tischen mit n Freien Plaetzen self.__TIDsNFreie = [] self.__Groesster = Tische.Groesster self.__Tische = Tische for i in range( self.__Groesster + 1 ): self.__TIDsNFreie.append( set() ) 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): result = result + " " +repr(i) + " :" + repr(self.__TIDsNFreie[i]) + "\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) ) self.NichtNachbar = eval(config.get( 'Globals', 'NichtNachbar' )) self.Allein = eval(config.get( 'Globals', 'Allein' )) self.TischWert = eval( config.get( 'Globals', 'TischWertigkeiten' ) ) for (k,v) in config.items( 'Trennung' ): self.GruppenTrennen[int(k)] = 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( '\n' ) Teile.append( '\n' % (Breite, Hoehe, Breite, Hoehe) ) Teile.append( '\n' ) for T in self.Tische: Mx, My = TischMitte(T) TR = TidZuRadius[T.Id] Teile.append( '\n' % (Mx, My, TR) ) Teile.append( 'Tisch %s\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( '\n' % (Px, Py, PersonenRadius, Farbe) ) Teile.append( '\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""" Min = 0 RListe=[] Wertigkeit=[] # TODO filter verwenden 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 XMLConfig: """aus Xml file alles einlesen""" def getText(self, nodelist): rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data return rc def handlePersonNode( self, person ): """Personendaten erfassen""" info = {} try: Titel = person.getElementsByTagName("Titel")[0] info["Titel"] = self.getText(Titel.childNodes) except IndexError: info["Titel"] = "" Vorname = person.getElementsByTagName("Vorname")[0] Nachname = person.getElementsByTagName("Nachname")[0] info["Vorname"] = self.getText(Vorname.childNodes) info["Nachname"] = self.getText(Nachname.childNodes) return info def HandleBestellung(self, Bestellung): """alle Bloecke in der XML Bestellung parsen""" PidListe = [] VipHash = {} PN = Personen() GN = Gruppen() VIPGruppe = Gruppe( 0 ) print('-HandleBestellung') AlleGruppenXML = Bestellung.getElementsByTagName("Gruppe") PID = 0 GID = 0 for GruppeXML in AlleGruppenXML: GID = GID + 1 print('-- GID ' + repr(GID)) G = Gruppe( GID ) IsVip = False AnzahlReservierungen = 1 for Node in GruppeXML.childNodes: if Node.nodeName == 'Tisch': TischNr = self.getText(Node.childNodes) print('TischNr:', TischNr) IsVip = True if Node.nodeName == 'Anzahl': AnzahlReservierungen = int(self.getText(Node.childNodes)) print('Anzahl der Reservierungen:', AnzahlReservierungen) IsContingent = True for Node in GruppeXML.childNodes: if Node.nodeName == 'Person': NRes = 0 for i in range(int(AnzahlReservierungen)): NRes = NRes + 1 PID = PID + 1 print("PID =" + repr(PID)) info = self.handlePersonNode( Node ) Vorname = info["Vorname"] Nachname = info["Nachname"] Titel = info["Titel"] P = Person( PID, Vorname, Nachname, Titel) if IsVip == True: PidListe.append( int(PID) ) VipHash[ int(PID) ] = int(TischNr) VIPGruppe.Person_dazu( P ) else: PN.PersonAdd( P ) G.Person_dazu( P ) if (G.Anzahl() > 0) & (IsVip != True): GN.Gruppe_dazu( G ) if IsVip == True: GID = GID - 1 print(PN) print(GN) print(VIPGruppe) print(VipHash) print('==========') return [ PN, GN, VIPGruppe, VipHash ] def laden(self, Bestellung): """lese alle Eingangsdaten aus einem XML File ein""" fsock = open( Bestellung, 'r') xmlini = minidom.parse(fsock) return self.HandleBestellung(xmlini) 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