3D6+3

Code-Stücke können hier veröffentlicht werden.
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Hatte vor einigen Tagen aus Langeweile das folgende gebaut:

Dies wirft einen n-seitigen Würfel.

Code: Alles auswählen

import random

def d(n, aces=False):
    r = random.randrange(n) + 1
    return r + d(n, aces) if aces and r == n else r
Ist der zweite Parameter `True`, wird bei der höchstmöglichen Augenzahl der Würfel nochmal geworfen. Der Würfelwurf kann "explodieren" - etwas, das man z.B. im Rollenspiel Savage Worlds braucht. Worauf es mir aber eigentlich ankommt ist, einen beliebigen Ausdruck `xdy+z` auswerten zu können:

Code: Alles auswählen

import re
_RE_DICE = re.compile(r"(\d+)?d(\d+)?|(\d+)|([-+])")

def roll(dice, aces=False):
    result = 0; sign = 1
    for m in _RE_DICE.finditer(dice):
        if m.lastindex == 2:
            count = int(m.group(1) or 1)
            sides = int(m.group(2) or 6)
            for i in range(count):
                result += d(sides, aces) * sign
        elif m.lastindex == 3:
            result += int(m.group(3)) * sign
        else:
            sign = +1 if m.group(4) == '+' else -1
    return result
Kann man das noch knapper/eleganter/verständlicher ausdrücken?

Stefan
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Code: Alles auswählen

import re
import random
import operator


class Bunch(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)


match = re.compile(
    flags = re.VERBOSE,
    pattern = r"""
        ( (?P<times> \d* )
          (?P<item> d | D )
          (?P<sides> \d* )
          ( (?P<op> \+ | \- )(?P<value> \d+ ) | $ )
          |
          (?P<error> .* )
        )
    """
).match


ops = {'+':operator.add, '-':operator.sub, None:lambda x,_:x}


def roll(dice):
    parsed = Bunch(**match(dice).groupdict())
    if parsed.error:
        raise ValueError('Invalid argument "%s"' % parsed.error)
    times = int(parsed.times or 1)
    sides = int(parsed.sides or 6)
    value = int(parsed.value or 0)
    op = ops[parsed.op]
    return max(
        0, op(sum(random.randint(1, sides) for each in xrange(times)), value)
    )

print roll('12d8-3')
print roll('12d8')
print roll('12d-3')
print roll('12d')
print roll('d19+100')
print roll('d19')
print roll('d')
Wie das mit den aces gehen könnte, weiß ich nicht auf die Schnelle.

Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

@pillmucher, du kannst beispielsweise kein "d6+d8", ich schon :)

Das Neuwürfeln könntest du implementieren, indem du meine "d"-Funktion statt "random.randint(1, sides)" benutzt.

Stefan
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

sma hat geschrieben:@pillmucher, du kannst beispielsweise kein "d6+d8", ich schon :)
Dann wäre jetzt noch Klammern spannend a la 2D6 * (2D10- D6).

Ich habe bei der Realisierung von so etwas mal einen Ansatz gewählt, der die Würfelwerte durch ihr ausgewürfeltes Ergebnis ersetzt hat und dann (evil) eval benutzt hat um das Ergebnis zu berechnen.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

sma hat geschrieben:Hatte vor einigen Tagen aus Langeweile das folgende gebaut:

Dies wirft einen n-seitigen Würfel.
Man merkt doch gleich wer Random>>roll: verbrochen hat. ;)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Darii hat geschrieben:Man merkt doch gleich wer Random>>roll: verbrochen hat. ;)
*g*

Leider fanden die Pharo-Leute die Funktion wohl zu unwichtig um sie auch in ihre Squeak-Distribution aufzunehmen :)

Stefan
Antworten