Strafen können durch benannte Profile festgelegt werden

This commit is contained in:
Michael Stangl
2026-07-15 17:05:13 +02:00
parent db69f3498d
commit 4a059f32c5
5 changed files with 114 additions and 53 deletions
+35 -11
View File
@@ -437,21 +437,45 @@ class Strafliste:
return self.gibPunkte('Trennung',Gruppengroesse-1, N_Teilungen)
else:
raise IndexError("Bezeichner gibts nicht")
def laden( self, FileName ):
print("-- Strafliste laden")
def laden( self, FileName, Sektion='default' ):
"""laedt ein Strafpunkte-Profil aus einer Sektion der strafen.cfg.
Jede Sektion ist ein vollstaendiges Bewertungs-'Vorgehen' mit allen
Angaben in einem gemeinsamen Namensraum. Der Aufrufer waehlt das
Profil ueber 'Sektion' (Vorgabe 'default'); so lassen sich mehrere
Bewertungsstrategien in derselben Datei pflegen. Die Trennungs-
Straffen stehen unter den Schluesseln 'Anzahl_Trennungen_N'."""
print("-- Strafliste laden (Profil '%s')" % Sektion)
config = configparser.ConfigParser()
config.read( os.path.expanduser(FileName) )
if not config.has_section( Sektion ):
raise ValueError(
"Strafprofil '%s' nicht in %s gefunden (vorhanden: %s)"
% ( Sektion, FileName, ', '.join(config.sections()) or '-' ) )
# 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' ) )
# Abschnitt [Abstand] ist optional; fehlt er, wird der Abstand
# getrennter Gruppen nicht bestraft (Faktor 0).
if config.has_section( 'Abstand' ):
self.AbstandFaktor = ast.literal_eval(config.get( 'Abstand', 'Faktor' ))
for (k,v) in config.items( 'Trennung' ):
self.GruppenTrennen[int(k)] = ast.literal_eval(v)
self.NichtNachbar = ast.literal_eval(config.get( Sektion, 'NichtNachbar' ))
self.Allein = ast.literal_eval(config.get( Sektion, 'Allein' ))
# TischWertigkeiten ist optional; fehlt der Schluessel, werden freie
# Plaetze gar nicht bestraft (leeres Dict -> gibPunkte('Tischbesetzung')
# gibt 0), wodurch mehr Teilbelegungen moeglich sind.
if config.has_option( Sektion, 'TischWertigkeiten' ):
self.TischWert = ast.literal_eval( config.get( Sektion, 'TischWertigkeiten' ) )
else:
self.TischWert = {}
# AbstandFaktor ist optional; fehlt er, wird der Abstand getrennter
# Gruppen nicht bestraft (Faktor 0).
if config.has_option( Sektion, 'AbstandFaktor' ):
self.AbstandFaktor = ast.literal_eval(config.get( Sektion, 'AbstandFaktor' ))
# Trennungs-Straffen: Schluessel 'Anzahl_Trennungen_N' -> N Trennungen.
# frisches Dict, damit nicht der (mutable) Konstruktor-Default geteilt
# wird und Reste eines frueheren laden() bleiben.
self.GruppenTrennen = {}
for (k, v) in config.items( Sektion ):
# configparser liefert Schluessel klein geschrieben
if k.startswith( 'anzahl_trennungen_' ):
N = int( k.rsplit( '_', 1 )[-1] )
self.GruppenTrennen[N] = ast.literal_eval(v)
# Sitzplatzverteilung, bestehend aus den Einzelelementen
+9 -1
View File
@@ -51,6 +51,10 @@ if __name__ == '__main__':
parser.add_option( "--zyklus-art", dest="zyklusart", default="Adaptiv",
help="Name des GA-Profils in zyklus.cfg (Vorgabe: Adaptiv). "
"Weitere mitgelieferte Profile: Easy, Simple" )
parser.add_option( "--strafen", dest="strafen", default="default",
help="Name des Strafpunkte-Profils (Sektion in strafen.cfg; "
"Vorgabe: default). Erlaubt mehrere Bewertungs-Vorgehen in einer "
"Datei, z.B. --strafen locker" )
parser.add_option( "--seed", dest="seed", type="int", default=None,
help="Zufalls-Startwert (int) fuer random.seed(), macht den Lauf "
"reproduzierbar. Ohne Angabe laeuft der GA nichtdeterministisch. "
@@ -83,6 +87,7 @@ if __name__ == '__main__':
Zyklusconfig = os.path.join( CfgDir, 'zyklus.cfg' )
Zyklusart = options.zyklusart
Strafenconfig = os.path.join( CfgDir, 'strafen.cfg' )
Strafenart = options.strafen
# lade Eingabedateien
Tischconfig = os.path.join( InDir, 'tische.ini' )
@@ -99,7 +104,10 @@ if __name__ == '__main__':
Z.laden( Zyklusconfig, Zyklusart )
print(Z)
SL = Strafliste()
SL.laden( Strafenconfig )
try:
SL.laden( Strafenconfig, Strafenart )
except ValueError as Fehler:
parser.error( str(Fehler) )
print(SL)
TE = Tische()
TE.laden( Tischconfig )