Ich hatte heute eine schlaflose Nacht und daher die Möglichkeit, meinen Gedanken freien Lauf zu lassen ...
Mein obiger Ansatz gefiel mir bereits eine Viertelstunde nach dem Absenden nicht mehr. Ich wollte zumindest eine einfache Form der Modifikatoren implementieren (einfache Grundrechenarten als Suffix)[1]. Es sollten einfache Konstrukte ausgewertet werden können:
|
Quellcode
|
1
2
3
4
5
6
|
1d6
1d6-1
1d8+10
2d8*2
2d10/2
etc.
|
Der Reguläre Ausdruck
Als erstes definiere ich den
Regulären Ausdruck, welcher die oben aufgeführten Würfelbeschreibungen abdecken kann:
|
Quellcode
|
1
2
3
|
PATTERN = re.compile(r'''(\d+) d (\d+) # count, "d", eyes
(?:(\+|-|\*|/?) (\d+))? # [operator, value]
''', re.X)
|
Modifikator-Funktionen
Nach dem Werfen der einzelnen Würfel und dem Aufsummieren deren Werte, kann dieses Ergebnis noch modifiziert werden. Dieses wird als Suffix zur Würfelnotation angegeben.
- 1d6+3 (Zum Gesamtergebnis wird immer 3 hinzuaddiert)
- 2d8*2 (Das Gesamtergebnis wird immer verdoppelt)
- 2d6-1 (Vom Gesamtergebnis wird immer 1 subtrahiert)
- etc.
Um diese Funktionen möglichst "elegant" (subjektiv) zu implementieren, habe ich mich dafür entschieden, diese mittels Funktionen höherer Ordnung zu erzeugen. Dies erlaubt es mir, jede beliebige Funktion als Modifikator zu verwenden, welche genau einen Parameter entgegen nimmt (Würfelwert) und ein (modifiziertes) Ergebnis zurück gibt.
Die vier Grundrechenarten sind wie folgt definiert:
|
Quellcode
|
1
2
3
4
5
6
7
8
9
10
11
|
def Add(summand):
return lambda a: a + summand
def Sub(subtrahend):
return lambda a: a - subtrahend
def Mul(factor):
return lambda a: a * factor
def Div(divisor):
return lambda a: a / divisor
|
Diese müssen noch mit den jeweiligen Zeichen assoziiert werden, um sinnvoll genutzt werden zu können:
|
Quellcode
|
1
2
3
4
5
6
|
MODIFICATOR_MAPPING = {
'+': Add,
'-': Sub,
'*': Mul,
'/': Div,
}
|
Würfel erzeugen
Die Funktion zum Erzeugen des/der Würfel aus meinem vorangegangenen Beitrag habe ich leicht abgeändert. Die Modifikation wird nun nicht mehr (am Ende) addiert, sondern die Modifikationsfunktion wird, wie beschrieben, mit der Summe der Würfel aufgerufen:
|
Quellcode
|
1
2
|
def Dice(count, eyes, mod=lambda a: a):
return lambda: mod(sum([random.randint(1, eyes) for _ in range(count)]))
|
Hilfsfunktionen
Um die Zuvor definierte Notation verwenden zu können, muss aus einer Zeichenkette eine Würfelfunktion erzeugt werden.
Dies erledigt folgende Funktion:
|
Quellcode
|
1
2
3
4
5
6
7
8
|
def create_from_string(string):
count, eyes, operator, value = PATTERN.match(string).groups()
# AttributeError if something went wrong here (pattern-mismatch)
if operator and value:
return Dice(int(count), int(eyes),
MODIFICATOR_MAPPING[operator](int(value)))
else:
return Dice(int(count), int(eyes))
|
Alles zusammen ...
Den
vollständigen Quelltext (dice.py) gibts im Pastebin.
Gut zu wissen (Stichworte)
Um das ganze nachvollziehen zu können, solltest Du dich mit folgenden Themen befassen:
Grüße ... bwbg (nach eine halben Kaffeemaschinenladung)
---
[1]: Die Würfelnotation scheint nicht wirklich standardisiert zu sein. Für komplexere Angaben wie
8*1d6-(1d8-1) lohnt es sich m. E. bereits einen eignen Parser nebst Grammatik zu bauen. Darauf habe ich jetzt mal verzichtet, Parserbau ist etwas, womit ich mich noch nicht eingehend beschäftigt habe.