Komplexe Würfel, die Ausdrücke rollen


23

Hintergrund

Ich spiele regelmäßig D & D mit einigen Freunden. Während wir über die Komplexität einiger Systeme / Versionen beim Würfeln und Anwenden von Boni und Strafen sprachen, kamen wir scherzhaft auf eine zusätzliche Komplexität für das Würfeln von Ausdrücken. Einige von ihnen waren zu empörend (wie das Erweitern einfacher Würfelausdrücke wie 2d6Matrixargumente 1 ), aber der Rest ergibt ein interessantes System.

Die Herausforderung

Bewerten Sie einen komplexen Würfelausdruck anhand der folgenden Regeln und geben Sie das Ergebnis aus.

Grundlegende Bewertungsregeln

  • Wann immer ein Operator eine Ganzzahl erwartet, aber eine Liste für einen Operanden erhält, wird die Summe dieser Liste verwendet
  • Wann immer ein Operator eine Liste erwartet, aber eine Ganzzahl für einen Operanden empfängt, wird die Ganzzahl als eine Liste mit einem Element behandelt, die diese Ganzzahl enthält

Betreiber

Alle Operatoren sind binäre Infixoperatoren. Zum Zwecke der Erklärung awird der linke Operand und bder rechte Operand sein. Die Listennotation wird für Beispiele verwendet, in denen Operatoren Listen als Operanden verwenden können, tatsächliche Ausdrücke jedoch nur aus positiven ganzen Zahlen und Operatoren bestehen.

  • d: aUnabhängige, einheitliche Zufallszahlen im Bereich ausgeben[1, b]
    • Vorrang: 3
    • Beide Operanden sind ganze Zahlen
    • Beispiele: 3d4 => [1, 4, 3],[1, 2]d6 => [3, 2, 6]
  • t: nimm die bniedrigsten Werte vona
    • Vorrang: 2
    • aist eine Liste, bist eine ganze Zahl
    • Wenn b > len(a), werden alle Werte zurückgegeben
    • Beispiele: [1, 5, 7]t1 => [1], [5, 18, 3, 9]t2 => [3, 5],3t5 => [3]
  • T: nimm die bhöchsten Werte vona
    • Vorrang: 2
    • aist eine Liste, bist eine ganze Zahl
    • Wenn b > len(a), werden alle Werte zurückgegeben
    • Beispiele: [1, 5, 7]T1 => [7], [5, 18, 3, 9]T2 => [18, 9],3T5 => [3]
  • r: Wenn Elemente in enthalten bsind a, rollen Sie diese Elemente mit der dgenerierten Anweisung erneut
    • Vorrang: 2
    • Beide Operanden sind Listen
    • Das erneute Rollen wird nur einmal durchgeführt, daher ist es möglich, noch Elemente bim Ergebnis zu haben
    • Beispiele: 3d6r1 => [1, 3, 4] => [6, 3, 4], 2d4r2 => [2, 2] => [3, 2],3d8r[1,8] => [1, 8, 4] => [2, 2, 4]
  • R: Wenn Elemente in enthalten bsind a, wiederholen Sie diese Elemente, bis keine Elemente von bvorhanden sind, und verwenden Sie dabei ddie von ihnen generierte Anweisung
    • Vorrang: 2
    • Beide Operanden sind Listen
    • Beispiele: 3d6R1 => [1, 3, 4] => [6, 3, 4], 2d4R2 => [2, 2] => [3, 2] => [3, 1],3d8R[1,8] => [1, 8, 4] => [2, 2, 4]
  • +: addieren aund bzusammen
    • Vorrang: 1
    • Beide Operanden sind ganze Zahlen
    • Beispiele: 2+2 => 4, [2]+[2] => 4,[3, 1]+2 => 6
  • -: subtrahieren bvona
    • Vorrang: 1
    • Beide Operanden sind ganze Zahlen
    • b wird immer kleiner sein als a
    • Beispiele: 2-1 => 1, 5-[2] => 3,[8, 3]-1 => 10
  • .: verketten aund bzusammen
    • Vorrang: 1
    • Beide Operanden sind Listen
    • Beispiele: 2.2 => [2, 2], [1].[2] => [1, 2],3.[4] => [3, 4]
  • _: Ausgabe amit allen Elementen von bentfernt
    • Vorrang: 1
    • Beide Operanden sind Listen
    • Beispiele: [3, 4]_[3] => [4], [2, 3, 3]_3 => [2],1_2 => [1]

Zusätzliche Regeln

  • Wenn der Endwert eines Ausdrucks eine Liste ist, wird er vor der Ausgabe summiert
  • Die Auswertung von Begriffen führt nur zu positiven Ganzzahlen oder Listen positiver Ganzzahlen. Bei jedem Ausdruck, der zu einer nicht positiven Ganzzahl oder einer Liste mit mindestens einer nicht positiven Ganzzahl führt, werden diese Werte durch 1s ersetzt
  • Klammern können verwendet werden, um Begriffe zu gruppieren und die Reihenfolge der Auswertung festzulegen
  • Operatoren werden in der Reihenfolge der höchsten bis zur niedrigsten Priorität ausgewertet, wobei die Auswertung bei gebundener Priorität von links nach rechts erfolgt (wird also 1d4d4ausgewertet als (1d4)d4).
  • Die Reihenfolge der Elemente in Listen spielt keine Rolle. Es ist durchaus akzeptabel, dass ein Operator, der eine Liste ändert, sie mit ihren Elementen in einer anderen relativen Reihenfolge zurückgibt
  • Begriffe, die nicht bewertet werden können oder zu einer Endlosschleife führen würden (wie 1d1R1oder 3d6R[1, 2, 3, 4, 5, 6]), sind ungültig

Testfälle

Format: input => possible output

1d20 => 13
2d6 => 8
4d6T3 => 11
2d20t1 => 13
5d8r1 => 34
5d6R1 => 20
2d6d6 => 23
3d2R1d2 => 3
(3d2R1)d2 => 11
1d8+3 => 10
1d8-3 => 4
1d6-1d2 => 2
2d6.2d6 => 12
3d6_1 => 8
1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)) => 61

Alle außer dem letzten Testfall wurden mit der Referenzimplementierung generiert.

Gearbeitetes Beispiel

Ausdruck: 1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3))

  1. 8d20t4T2 => [19, 5, 11, 6, 19, 15, 4, 20]t4T2 => [4, 5, 6, 11]T2 => [11, 6](voll: 1d(([11, 6])d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)))
  2. 6d6R1r6 => [2, 5, 1, 5, 2, 3]r1R6 => [2, 5, 3, 5, 2, 3]R6 => [2, 5, 3, 5, 2, 3]( 1d([11, 6]d[2, 5, 3, 5, 2, 3]-2d4+1d2).(1d(4d6_3d3)))
  3. [11, 6]d[2, 5, 3, 5, 2, 3] => 17d20 => [1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]( 1d([1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]-2d4+1d2).(1d(4d6_3d3)))
  4. 2d4 => 7( 1d([1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]-7+1d2).(1d(4d6_3d3)))
  5. 1d2 => 2( 1d([1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]-7+2).(1d(4d6_3d3)))
  6. [1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]-7+2 => 133-7+2 => 128( 1d128).(1d(4d6_3d3)))
  7. 4d6_3d3 => [1, 3, 3, 6]_[3, 2, 2] => [1, 3, 3, 6, 3, 2, 2]( 1d128).(1d[1, 3, 3, 6, 3, 2, 2]))
  8. 1d[1, 3, 3, 6, 3, 2, 2] => 1d20 => 6( 1d128).(6))
  9. 1d128 => 55( 55.6)
  10. 55.6 => [55, 6]( [55, 6])
  11. [55, 6] => 61 (getan)

Referenzimplementierung

Diese Referenzimplementierung verwendet dieselbe Konstante seed ( 0), um jeden Ausdruck auf testbare, konsistente Ausgaben zu untersuchen. Es werden Eingaben in STDIN erwartet, wobei die einzelnen Ausdrücke durch Zeilenumbrüche voneinander getrennt sind.

#!/usr/bin/env python3

import re
from random import randint, seed
from collections import Iterable
from functools import total_ordering

def as_list(x):
    if isinstance(x, Iterable):
        return list(x)
    else:
        return [x]

def roll(num_sides):
    return Die(randint(1, num_sides), num_sides)

def roll_many(num_dice, num_sides):
    num_dice = sum(as_list(num_dice))
    num_sides = sum(as_list(num_sides))
    return [roll(num_sides) for _ in range(num_dice)]

def reroll(dice, values):
    dice, values = as_list(dice), as_list(values)
    return [die.reroll() if die in values else die for die in dice]

def reroll_all(dice, values):
    dice, values = as_list(dice), as_list(values)
    while any(die in values for die in dice):
        dice = [die.reroll() if die in values else die for die in dice]
    return dice

def take_low(dice, num_values):
    dice = as_list(dice)
    num_values = sum(as_list(num_values))
    return sorted(dice)[:num_values]

def take_high(dice, num_values):
    dice = as_list(dice)
    num_values = sum(as_list(num_values))
    return sorted(dice, reverse=True)[:num_values]

def add(a, b):
    a = sum(as_list(a))
    b = sum(as_list(b))
    return a+b

def sub(a, b):
    a = sum(as_list(a))
    b = sum(as_list(b))
    return max(a-b, 1)

def concat(a, b):
    return as_list(a)+as_list(b)

def list_diff(a, b):
    return [x for x in as_list(a) if x not in as_list(b)]

@total_ordering
class Die:
    def __init__(self, value, sides):
        self.value = value
        self.sides = sides
    def reroll(self):
        self.value = roll(self.sides).value
        return self
    def __int__(self):
        return self.value
    __index__ = __int__
    def __lt__(self, other):
        return int(self) < int(other)
    def __eq__(self, other):
        return int(self) == int(other)
    def __add__(self, other):
        return int(self) + int(other)
    def __sub__(self, other):
        return int(self) - int(other)
    __radd__ = __add__
    __rsub__ = __sub__
    def __str__(self):
        return str(int(self))
    def __repr__(self):
        return "{} ({})".format(self.value, self.sides)

class Operator:
    def __init__(self, str, precedence, func):
        self.str = str
        self.precedence = precedence
        self.func = func
    def __call__(self, *args):
        return self.func(*args)
    def __str__(self):
        return self.str
    __repr__ = __str__

ops = {
    'd': Operator('d', 3, roll_many),
    'r': Operator('r', 2, reroll),
    'R': Operator('R', 2, reroll_all),
    't': Operator('t', 2, take_low),
    'T': Operator('T', 2, take_high),
    '+': Operator('+', 1, add),
    '-': Operator('-', 1, sub),
    '.': Operator('.', 1, concat),
    '_': Operator('_', 1, list_diff),
}

def evaluate_dice(expr):
    return max(sum(as_list(evaluate_rpn(shunting_yard(tokenize(expr))))), 1)

def evaluate_rpn(expr):
    stack = []
    while expr:
        tok = expr.pop()
        if isinstance(tok, Operator):
            a, b = stack.pop(), stack.pop()
            stack.append(tok(b, a))
        else:
            stack.append(tok)
    return stack[0]

def shunting_yard(tokens):
    outqueue = []
    opstack = []
    for tok in tokens:
        if isinstance(tok, int):
            outqueue = [tok] + outqueue
        elif tok == '(':
            opstack.append(tok)
        elif tok == ')':
            while opstack[-1] != '(':
                outqueue = [opstack.pop()] + outqueue
            opstack.pop()
        else:
            while opstack and opstack[-1] != '(' and opstack[-1].precedence > tok.precedence:
                outqueue = [opstack.pop()] + outqueue
            opstack.append(tok)
    while opstack:
        outqueue = [opstack.pop()] + outqueue
    return outqueue

def tokenize(expr):
    while expr:
        tok, expr = expr[0], expr[1:]
        if tok in "0123456789":
            while expr and expr[0] in "0123456789":
                tok, expr = tok + expr[0], expr[1:]
            tok = int(tok)
        else:
            tok = ops[tok] if tok in ops else tok
        yield tok

if __name__ == '__main__':
    import sys
    while True:
        try:
            dice_str = input()
            seed(0)
            print("{} => {}".format(dice_str, evaluate_dice(dice_str)))
        except EOFError:
            exit()

[1]: Unsere Definition adbfür Matrixargumente war, AdXfür jedes Xin a * b, wo zu rollen A = det(a * b). Das ist natürlich zu absurd für diese Herausforderung.



Mit der Garantie, -dass dies bimmer weniger ist, als aich sehe, gibt es keine Möglichkeit, nicht positive ganze Zahlen zu erhalten, daher scheint die zweite zusätzliche Regel sinnlos. OTOH, _könnte zu einer leeren Liste führen, was in den gleichen Fällen nützlich erscheint. Was bedeutet es jedoch, wenn eine Ganzzahl benötigt wird? Normalerweise würde ich sagen, die Summe ist 0...
Christian Sievers

@ChristianSievers 1) Der Klarheit halber habe ich den zusätzlichen Hinweis zu nicht positiven Ganzzahlen hinzugefügt. 2) Die Summe einer leeren Liste ist 0. Nach der No-Non-Positives-Regel würde es als ausgewertet 1.
Mego

Okay, aber ist es okay als Zwischenergebnis? So [1,2]_([1]_[1])ist [1,2]?
Christian Sievers

@ChristianSievers Nein. Das würde dazu führen [2], weil [1]_[1] -> [] -> 0 -> 1 -> [1].
Mego

Antworten:


9

Python 3, 803 788 753 749 744 748 745 740 700 695 682 Bytes

exec(r'''from random import*
import re
class k(int):
 p=0;j=Xl([d(randint(1,int(b)),b)Zrange(a)]);__mul__=Xl(sorted(Y)[:b]);__matmul__=Xl(sorted(Y)[-b:]);__truediv__=Xl([d(randint(1,int(_.i)),_.i)if _==b else _ ZY]);__sub__=Xk(max(1,int.__sub__(a,b)))
 def __mod__(a,b):
  x=[]
  while x!=Y:x=Y;a=a/b
  Wl(x)
 def V:
  if b.p:p=b.p;del b.p;Wl(Y+b.l)if~-p else l([_ZY if~-(_ in b.l)])
  Wk(int.V)
 def __neg__(a):a.p+=1;Wa
def l(x):a=k(sum(x)or 1);Y=x;Wa
def d(v,i):d=k(v);d.i=i;Wd
lambda a:eval(re.sub("(\d+)","(k(\\1))",a).translate({100:".j",116:"*",84:"@",114:"/",82:"%",46:"+--",95:"+-"}))'''.translate({90:" for _ in ",89:"a.l",88:"lambda a,b:",87:"return ",86:"__add__(a,b)"}))

-5 Bytes dank Mr.Xcoder

-5 weitere Bytes dank NGN

-ungefähr 40 Bytes dank Jonathan French

Yuck, was für ein Trottel! Dies funktioniert, indem ich einen regulären Ausdruck verwende, um alle Zahlen in meiner kKlasse umzubrechen , und alle Operatoren in Operatoren konvertiere, die Python versteht, und dann die magischen Methoden der kKlasse verwende, um die Mathematik zu handhaben. Das +-und +--am Ende für .und _sind ein Hack, um den Vorrang richtig zu halten. Ebenso kann ich den **Operator für d nicht verwenden, da dies dazu führen würde, 1d4d4dass analysiert wird als 1d(4d4). Stattdessen fasse ich alle Zahlen in einen zusätzlichen Satz von Parens ein und mache d as .j, da Methodenaufrufe Vorrang vor Operatoren haben. Die letzte Zeile wird als anonyme Funktion ausgewertet, die den Ausdruck auswertet.


def __mod__(a, b)... Warum der Raum zwischen a,und b?
Mr. Xcoder


@ Mr.Xcoder ich glaube , Sie ein Byte durch Entfernen eines unnötigen Speicherplatz sparen können: ; __sub__. Und möglicherweise auch hier: lambda a,b: l(.
Jonathan Frech

1
Sie können einige Bytes einsparen, indem Sie den gesamten Code in eine exec("""...""".replace("...","..."))Anweisung einschließen und häufig vorkommende Zeichenfolgen return durch ein einzelnes Zeichen ersetzen . Allerdings execscheint mir die Strategie immer ein bisschen unelegant ...
Jonathan Frech

die Körper von __mod__und __add__brauchen nicht so viel
Einrückung

3

APL (Dyalog Classic) , 367 Byte

d←{(⊢⍪⍨1+?)⍉⍪⊃⍴/⊃¨+/¨⍺⍵}⋄r←{z←⊣⌿⍺⋄((m×?n)+z×~m←z∊⊣⌿⍵)⍪n←⊢⌿⍺}⋄R←{⍬≡⊃∩/⊣⌿¨⍺⍵:⍺⋄⍵∇⍨⍺r⍵}
u←{⍺[;((⊃+/⍵)⌊≢⍉⍺)↑⍺⍺⊣⌿⍺]}⋄t←⍋u⋄T←⍒u⋄A←+/,⋄S←+/,∘-⋄C←,⋄D←{⍺/⍨~⊃∊/⊣⌿¨⍺⍵}
hv←⍬⋄o'drRtT+-._'f←{8<io⍳⊃⍵:0v⊢←(¯2v),(⍎i'drRtTASCD')/¯2v}
{⊃⍵∊⎕d:v,←⊂⍪2↑⍎⍵⋄'('=⍵:h,←⍵⋄')'=⍵:h↑⍨←i-1f¨⌽h↓⍨i←+/∨\⌽h='('⋄h,←⍵⊣h↓⍨←-i⊣f¨⌽h↑⍨-i←+/\⌽≤/(1 4 4 1/⌽⍳4)[o⍳↑⍵,¨h]}¨'\d+' '.'s'&'⊢⍞
f¨⌽h1⌈+/⊣⌿⊃v

Probieren Sie es online!

Dies ist der Rangierbahnhof-Algorithmus aus der Referenzimplementierung, mit dem zusammengeführt wird evaluate_dice(), ohne den kruft- und objektorientierten Unsinn. Es werden nur zwei Stapel verwendet: hfür die Operatoren und vfür die Werte. Analyse und Auswertung sind verschachtelt.

Zwischenergebnisse werden als 2 × N-Matrizen dargestellt, wobei die erste Reihe die Zufallswerte und die zweite Reihe die Anzahl der Seiten auf den Würfeln sind, die sie erzeugt haben. Wenn der Würfelwurfoperator "d" kein Ergebnis liefert, enthält die zweite Zeile beliebige Zahlen. Ein einzelner Zufallswert ist eine 2 × 1-Matrix und daher nicht von einer Liste mit 1 Element zu unterscheiden.


3

Python 3: 723 722 714 711 707 675 653 665 Bytes

import re
from random import*
S=re.subn
e=eval
r=randint
s=lambda a:sum(g(e(a)))or 1
g=lambda a:next(zip(*a))
def o(m):a,o,b=m.groups();A=sorted(e(a));t=g(e(b));return str(o in"rR"and([([v,(r(1,d)*(o>"R")or choice([n+1for n in range(d)if~-(n+1in t)]))][v in t],d)for(v,d)in A])or{"t":A[:s(b)],"T":A[-s(b):],"+":[(s(a)+s(b),0)],"-":[(s(a)-s(b),0)],".":e(a)+e(b),"_":[t for t in e(a)if~-(t[0]in g(e(b)))]}[o])
def d(m):a,b=map(s,m.groups());return str([(r(1,b),b)for _ in" "*a])
p=r"(\[[^]]+\])"
def E(D):
 D,n=S(r"(\d+)",r"[(\1,0)]",D)
 while n:
  n=0
  for e in[("\(("+p+")\)",r"\1"),(p+"d"+p,d),(p+"([tTrR])"+p,o),(p+"(.)"+p,o)]:
   if n<1:D,n=S(*e,D)
 return s(D)

Der Einstiegspunkt ist E. Dies gilt iterativ für reguläre Ausdrücke. Zuerst werden alle Ganzzahlen xdurch ein Tupel mit einer Singleton-Liste ersetzt [(x,0)]. Dann führt der erste reguläre Ausdruck die dOperation aus, indem alle [(x,0)]d[(b,0)]durch die Zeichenfolgendarstellung eines Arrays von Tupeln wie ersetzt werden [(1,b),(2,b),(3,b)]. Das zweite Element jedes Tupels ist der zweite Operand von d. Anschließend führen nachfolgende reguläre Ausdrücke die anderen Operatoren aus. Es gibt einen speziellen regulären Ausdruck, um Parens aus vollständig berechneten Ausdrücken zu entfernen.


3

Clojure, 731 720 Bytes

(wenn Zeilenumbrüche entfernt werden)

Update: eine kürzere Implementierung von F.

(defn N[i](if(seq? i)(apply + i)i))
(defn g[i](let[L(fn[i](let[v(g i)](if(seq? v)v(list v))))R remove T take](if(seq? i)(let[[o a b :as A]i](if(some symbol? A)(case o d(repeatedly(N(g a))(fn[](inc(rand-int(N(g b))))))t(T(N(g b))(sort(g a)))T(T(N(g b))(sort-by -(g a)))r(for[i(L a)](if((set(L b))i)(nth(L a)0)i))R(T(count(L a))(R(set(L b))(for[_(range)i(L a)]i)))+(+(N(g a))(N(g b)))-(-(N(g a))(N(g b))).(into(L a)(L b))_(R(set(L b))(g a)))A))i)))
(defn F[T](if(seq? T)(if(next T)(loop[[s & S]'[_ . - + R r T t d]](let[R reverse[b a](map R(split-with(comp not #{s})(R T)))a(butlast a)](if a(cons s(map F[a b]))(recur S))))(F(first T)))T))
(defn f[i](N(g(F(read-string(clojure.string/replace(str"("i")")#"[^0-9]"" $0 "))))))

Dies besteht aus vier Hauptteilen:

  • N: zwingt eine Liste in eine Zahl
  • g: wertet einen abstrakten Syntaxbaum aus (S-Ausdrücke mit 3 Elementen)
  • F: Konvertiert ein AST-Infix in eine Präfixnotation (S-Ausdrücke). Wendet auch die Rangfolge der Operanden an
  • f: read-stringKonvertiert eine Zeichenfolge in eine verschachtelte Folge von Zahlen und Symbolen (Infix AST), leitet sie durch F -> g -> N und gibt die Ergebnisnummer zurück.

Ich bin mir nicht sicher, wie ich das gründlich testen soll, vielleicht über statistische Tests gegen eine Referenzimplementierung? Zumindest der AST und seine Bewertung ist relativ einfach zu verfolgen.

Beispiel S-Ausdruck von 1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)):

(. (d 1 (- (d (T (t (d 8 20) 4) 2)
              (R (d 6 6) (r 1 6)))
           (+ (d 2 4)
              (d 1 2))))
   (d 1 (_ (d 4 6) (d 3 3))))

Weniger Golf mit Zwischenergebnissen und Tests:

(def f #(read-string(clojure.string/replace(str"("%")")#"[^0-9]"" $0 ")))

(defn F [T]
  (println {:T T})
  (cond
    (not(seq? T))T
    (symbol?(first T))T
    (=(count T)1)(F(first T))
    1(loop [[s & S] '[_ . - + R r T t d]]
      (let[[b a](map reverse(split-with(comp not #{s})(reverse T)))
           _ (println [s a b])
           a(butlast a)]
        (cond
          a(do(println {:s s :a a :b b})(cons s(map F[a b])))
          S(recur S))))))


(->> "3d6" f F)
(->> "3d6t2" f F)
(->> "3d2R1" f F)
(->> "1d4d4" f F)
(->> "2d6.2d6" f F)
(->> "(3d2R1)d2" f F)
(->> "1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3))" f F)

(defn N[i](if(seq? i)(apply + i)i))

(defn g[i]
  (let[L(fn[i](let[v(g i)](if(seq? v)v(list v))))]
    (if(seq? i)
      (let[[o a b :as A] i]
        (println {:o o :a a :b b :all A})
        (if(every? number? A)(do(println{:A A})A)
           (case o
            d (repeatedly(N (g a))(fn[](inc(rand-int(N (g b))))))
            t (take(N (g b))(sort(g a)))
            T (take(N (g b))(sort-by -(g a)))
            r (for[i(L a)](if((set(L b))i)(nth(L a)0)i))
            R (take(count(g a))(remove(set(L b))(for[_(range)i(g a)]i)))
            + (+(N (g a))(N (g b)))
            - (-(N (g a))(N (g b)))
            . (into(L a)(L b))
            _ (remove(set(L b))(g a)))))
      (do(println {:i i})i))))


(g '(. (d 3 5) (d 4 3)))
(g '(. 1 (2 3)))
(g '(+ 1 (2 3)))
(g '(R (d 10 5) (d 1 3)))
(g '(T (d 5 20) 3))
(g '(t (d 5 20) 3))
(g '(d (d 3 4) 10))
(g '(d 4 3))
(g '(_ (d 4 6) (d 3 3)))

(->> "1d(4d6_3d3)" f F g)
(->> "1r6" f F g)
(->> "(8d20t4T2)d(6d6R1r6)" f F g)
(->> "(8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)" f F g)
(->> "1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3))" f F g))

2

Python 3, 695 Bytes

import random,tatsu
A=lambda x:sum(x)or 1
H=lambda x:[[d,D(d.s)][d in x[2]]for d in x[0]]
R=lambda x:R([H(x)]+x[1:])if{*x[0]}&{*x[2]}else x[0]
class D(int):
 def __new__(cls,s):o=super().__new__(cls,random.randint(1,s));o.s = s;return o
class S:
 o=lambda s,x:{'+':[A(x[0])+A(x[2])],'-':[A(x[0])-A(x[2])],'.':x[0]+x[2],'_':[d for d in x[0]if d not in x[2]]}[x[1]]
 f=lambda s,x:{'r':H(x),'R':R(x),'t':sorted(x[0])[:A(x[2])],'T':sorted(x[0])[-A(x[2]):]}[x[1]]
 d=lambda s,x:[D(A(x[2]))for _ in' '*A(x[0])]
 n=lambda s,x:[int(x)]
 l=lambda s,x:sum(x,[])
lambda i:tatsu.parse("s=e$;e=o|t;o=e/[-+._]/t;t=f|r;f=t/[rRtT]/r;r=d|a;d=r/d/a;a=n|l|p;n=/\d+/;l='['@:','.{n}']';p='('@:e')';",i,semantics=S())

Ein Interpreter, der mit tatsueiner PEG-Parser-Bibliothek erstellt wurde. Das erste Argument dafür tatsu.parser()ist die PEG-Grammatik.

class D(für Die) klassifiziert den eingebauten intTyp. Ihr Wert ist das Ergebnis einer Rolle. Das Attribut .sist die Anzahl der Seiten auf dem Würfel.

class S hat die semantischen Aktionen für den Parser und implementiert den Interpreter.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.