복잡한 주사위 롤링 표현


23

배경

나는 친구들과 정기적으로 D & D를한다. 주사위 굴림과 보너스 및 페널티 적용과 관련하여 일부 시스템 / 버전의 복잡성에 대해 이야기하는 동안, 우리는 농담으로 주사위 굴림 표현에 대한 추가 복잡성을 생각해 냈습니다. 그들 중 일부는 너무 거칠기 때문에 ( 2d6매트릭스 인수 1 과 같은 간단한 주사위 표현을 확장하는 것과 같이 ), 나머지는 흥미로운 시스템을 만듭니다.

도전

복잡한 주사위 표현이 주어지면 다음 규칙에 따라 평가하고 결과를 출력하십시오.

기본 평가 규칙

  • 연산자가 정수를 기대하지만 피연산자에 대한 목록을 수신 할 때마다 해당 목록의 합계가 사용됩니다
  • 연산자가 목록을 기대하지만 피연산자에 대한 정수를 수신 할 때마다 정수는 해당 정수를 포함하는 단일 요소 목록으로 처리됩니다.

연산자

모든 연산자는 이진 접두사입니다. 설명을 a위해 왼쪽 피연산자 b가되고 오른쪽 피연산자가됩니다. 연산자가리스트를 피연산자로 사용할 수있는 예제에는리스트 표기법이 사용되지만 실제 표현식은 양의 정수와 연산자로만 구성됩니다.

  • d: a범위 내에서 독립적 인 균일 난수를 출력[1, b]
    • 우선 순위 : 3
    • 두 피연산자 모두 정수
    • 예 : 3d4 => [1, 4, 3],[1, 2]d6 => [3, 2, 6]
  • t:에서 b가장 낮은 값을 가져 옵니다a
    • 우선 순위 : 2
    • a리스트, b정수
    • 인 경우 b > len(a)모든 값이 반환됩니다
    • 예 : [1, 5, 7]t1 => [1], [5, 18, 3, 9]t2 => [3, 5],3t5 => [3]
  • T:에서 b가장 높은 값을 가져 옵니다a
    • 우선 순위 : 2
    • a리스트, b정수
    • 인 경우 b > len(a)모든 값이 반환됩니다
    • 예 : [1, 5, 7]T1 => [7], [5, 18, 3, 9]T2 => [18, 9],3T5 => [3]
  • r: 어떤 요소가있는 경우 ba, 어떤 사용하여 이러한 요소를 굴릴 d을 생성 문
    • 우선 순위 : 2
    • 두 피연산자 모두 목록입니다
    • 재 롤링은 한 번만 수행되므로 b결과에 여전히 요소가 있을 수 있습니다
    • 예 : 3d6r1 => [1, 3, 4] => [6, 3, 4], 2d4r2 => [2, 2] => [3, 2],3d8r[1,8] => [1, 8, 4] => [2, 2, 4]
  • R: 어떤 요소가있는 경우 ba어떠한 요소가 될 때까지 반복적으로 이러한 요소를 굴릴 b존재하지 않는 어떤 사용 d을 생성 문
    • 우선 순위 : 2
    • 두 피연산자 모두 목록입니다
    • 예 : 3d6R1 => [1, 3, 4] => [6, 3, 4], 2d4R2 => [2, 2] => [3, 2] => [3, 1],3d8R[1,8] => [1, 8, 4] => [2, 2, 4]
  • +: 추가 a하고 b함께
    • 우선 순위 : 1
    • 두 피연산자 모두 정수
    • 예 : 2+2 => 4, [2]+[2] => 4,[3, 1]+2 => 6
  • -: 빼기 b에서a
    • 우선 순위 : 1
    • 두 피연산자 모두 정수
    • b 항상보다 작을 것입니다 a
    • 예 : 2-1 => 1, 5-[2] => 3,[8, 3]-1 => 10
  • .: 연결 a하고 b함께
    • 우선 순위 : 1
    • 두 피연산자 모두 목록입니다
    • 예 : 2.2 => [2, 2], [1].[2] => [1, 2],3.[4] => [3, 4]
  • _: a모든 요소가 b제거 된 출력
    • 우선 순위 : 1
    • 두 피연산자 모두 목록입니다
    • 예 : [3, 4]_[3] => [4], [2, 3, 3]_3 => [2],1_2 => [1]

추가 규칙

  • 식의 최종 값이 목록이면 출력 전에 합산됩니다.
  • 항을 평가하면 양의 정수 또는 양의 정수 목록 만 나타납니다. 양수가 아닌 정수 또는 하나 이상의 양수가 아닌 정수를 포함하는 목록의 결과는 1s 로 대체됩니다.
  • 괄호를 사용하여 용어를 그룹화하고 평가 순서를 지정할 수 있습니다.
  • 우선 순위가 가장 높은 순서대로 연산자를 평가하며 우선 순위가 묶인 경우 왼쪽에서 오른쪽으로 평가가 진행됩니다 (따라서 1d4d4로 평가됨 (1d4)d4)
  • 목록의 요소 순서는 중요하지 않습니다. 목록을 수정하는 연산자가 다른 상대 순서로 요소와 함께 목록을 반환하도록 완벽하게 허용됩니다.
  • 평가할 수 없거나 무한 루프 ( 1d1R1또는 같은 3d6R[1, 2, 3, 4, 5, 6])를 초래하는 용어 는 유효하지 않습니다

테스트 사례

체재: 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

마지막 테스트 케이스를 제외한 모든 것은 참조 구현으로 생성되었습니다.

작동 예

표현: 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](전체 : 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 (끝난)

참조 구현

이 참조 구현은 0테스트 가능한 일관된 출력에 대한 각 표현식을 평가하기 위해 동일한 상수 시드 ( )를 사용합니다 . STDIN에 대한 입력을 기대하며 개행은 각 표현식을 구분합니다.

#!/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] : adb행렬 인수 에 대한 정의는 , where 에서 AdX각각 X에 대해 롤 하는 것이 었습니다 . 분명히이 도전에는 너무 터무니 없습니다.a * bA = det(a * b)



에 대한 보증과 함께 -b보다 항상 적을 것이다 a번째 추가 규칙은 무의미한 것 같다, 그래서 내가 아닌 양의 정수를 얻을 수있는 방법을 볼 수 없습니다. OTOH, _빈 목록이 생길 수 있습니다. 같은 경우에 유용하지만 정수가 필요할 때의 의미는 무엇입니까? 일반적으로 나는 합계가 0... 라고 말하고 싶습니다
Christian Sievers

@ChristianSievers 1) 명확성을 위해 비 양의 정수에 대한 추가 메모를 추가했습니다. 2) 빈 목록의 합은입니다 0. 양성이 아닌 규칙에 따라로 평가됩니다 1.
Mego

좋아,하지만 중간 결과로 괜찮아? 그래서 [1,2]_([1]_[1])이다 [1,2]?
Christian Sievers

@ChristianSievers 아뇨 초래 [2]하기 때문에 [1]_[1] -> [] -> 0 -> 1 -> [1].
Mego

답변:


9

Python 3, 803788 753749744 748745740 70070095682 바이트

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)"}))

Xcoder 덕분에 -5 바이트

NGN 덕분에 -5 바이트 더

Jonathan French 덕분에 약 40 바이트

ck! 이것은 정규 표현식을 사용하여 k클래스의 모든 숫자를 래핑하고 모든 연산자를 파이썬이 이해하는 연산자로 변환 한 다음 k클래스 의 마법 메소드를 사용 하여 수학을 처리합니다. +-+--의 말 .과는 _올바른 우선 순위를 유지하는 해킹입니다. 마찬가지로 **d에 연산자를 사용할 수 없으므로 1d4d4로 해석됩니다 1d(4d4). 대신 .j메소드 호출이 연산자보다 우선 순위가 높기 때문에 모든 숫자를 여분의 parens 세트로 감싸고 d를 수행 합니다. 마지막 줄은 표현식을 평가하는 익명 함수로 평가됩니다.


def __mod__(a, b)... 왜 사이의 공간 a,b?
Mr. Xcoder


@ Mr.Xcoder 불필요한 공간을 제거하여 바이트를 절약 할 수 있다고 생각합니다 ; __sub__. 그리고 아마도 여기도 있습니다 : lambda a,b: l(.
Jonathan Frech

1
exec("""...""".replace("...","..."))명령문에 전체 코드를 래핑 하고 자주 발생하는 문자열을 return 단일 문자로 바꾸면 바이트를 절약 할 수 있습니다 . 그러나 나에게 exec전략은 항상 조금 우아해 보인다.
Jonathan Frech

의 몸 __mod____add__필요하지 않은 많은 들여 쓰기
NGN

3

APL (Dyalog Classic) , 367 바이트

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

온라인으로 사용해보십시오!

이것은 evaluate_dice()cruft 및 object-oriented nonsense없이 와 통합 된 참조 구현의 분로 야드 알고리즘입니다 . h연산자와 v값에 대해 두 개의 스택 만 사용 됩니다. 파싱 ​​및 평가가 인터리브됩니다.

중간 결과는 2xN 행렬로 표시되며, 첫 번째 행은 임의의 값이고 두 번째 행은 주사위를 생성 한 주사위의 변의 수입니다. 주사위 던지기 "d"연산자가 결과를 생성하지 않으면 두 번째 행에 임의의 숫자가 포함됩니다. 단일 난수 값은 2 × 1 행렬이므로 1- 요소 목록과 구별 할 수 없습니다.


3

파이썬 3 : 723 722 714 711 707 675 653 665 바이트

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)

진입 점은 E입니다. 이것은 정규 표현식을 반복적으로 적용합니다. 먼저 모든 정수 x를 singleton list tuple로 바꿉니다 [(x,0)]. 그런 다음 첫 번째 정규 표현식은 d모든 [(x,0)]d[(b,0)]을 튜플 배열의 문자열 표현 으로 대체 하여 연산을 수행합니다 [(1,b),(2,b),(3,b)]. 각 튜플의 두 번째 요소는의 두 번째 피연산자 d입니다. 그런 다음 후속 정규식은 각각의 다른 연산자를 수행합니다. 완전히 계산 된 표현식에서 parens를 제거하는 특수 정규식이 있습니다.


3

클로저, 731720 바이트

(개행이 제거 될 때)

업데이트 :의 짧은 구현 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 "))))))

이것은 네 가지 주요 부분으로 구성됩니다.

  • N:리스트를 숫자로 강제 변환
  • g: 추상 구문 트리를 평가합니다 (3 개의 항목이있는 S- 표현식)
  • F: 접두사 AST를 접두사 표기법 (S- 표현식)으로 변환하고 피연산자 순서 우선 순위도 적용합니다.
  • f: read-string문자열을 숫자와 기호의 중첩 시퀀스 (접두어 AST)로 변환하고 F-> g-> N을 통해 파이프하여 결과 번호를 반환합니다.

참조 구현에 대한 통계 테스트를 통해이를 철저히 테스트하는 방법을 모르겠습니다. 적어도 AST와 그 평가는 비교적 따르기가 쉽습니다.

의 S- 표현 예 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))))

internediate 결과 및 테스트로 골프를 덜하십시오.

(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

파이썬 3, 695 바이트

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())

tatsuPEG 파서 라이브러리를 사용하여 빌드 한 인터프리터 첫 번째 주장 tatsu.parser()은 PEG 문법입니다.

class D(Die의 경우) 내장 int유형을 서브 클래스 화합니다 . 가치는 롤의 결과입니다. 속성 .s은 다이의 변의 수입니다.

class S 파서에 대한 시맨틱 조치를 가지며 인터프리터를 구현합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.