การแสดงออกของลูกเต๋าที่ซับซ้อน


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: หากมีองค์ประกอบใดbเข้ามาaให้ทำการตรวจสอบรายการเหล่านั้นซ้ำdคำสั่งสร้างขึ้นมา
    • ลำดับความสำคัญ: 2
    • ตัวถูกดำเนินการทั้งสองเป็นรายการ
    • Rerolling ทำเพียงครั้งเดียวดังนั้นจึงเป็นไปได้ที่จะยังมีองค์ประกอบของbผลลัพธ์อยู่
    • ตัวอย่าง: 3d6r1 => [1, 3, 4] => [6, 3, 4], 2d4r2 => [2, 2] => [3, 2],3d8r[1,8] => [1, 8, 4] => [2, 2, 4]
  • R: หากมีองค์ประกอบใดbเข้ามาaให้ทำการรวบรวมองค์ประกอบเหล่านั้นซ้ำ ๆ จนกว่าจะไม่มีองค์ประกอบของ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สำหรับอาร์กิวเมนต์เมทริกซ์คือการม้วนAdXสำหรับแต่ละXในที่a * b A = det(a * b)เห็นได้ชัดว่าไร้สาระเกินไปสำหรับความท้าทายนี้



ด้วยการรับประกัน-ว่ามันbจะน้อยกว่าที่aฉันไม่เห็นวิธีที่จะได้รับจำนวนเต็มบวกไม่ใช่ดังนั้นกฎเพิ่มเติมที่สองดูเหมือนไม่มีจุดหมาย OTOH _อาจส่งผลให้รายการว่างเปล่าซึ่งดูเหมือนว่ามีประโยชน์ในกรณีเดียวกัน แต่มันหมายความว่าอย่างไรเมื่อต้องการจำนวนเต็ม? ปกติผมว่ารวมเป็น0...
คริสเตียน Sievers

@ChristianSievers 1) ฉันเพิ่มบันทึกเพิ่มเติมเกี่ยวกับจำนวนเต็มที่ไม่เป็นบวกเพื่อความชัดเจน 2) 0ผลรวมของรายการที่ว่างเปล่าเป็น 1โดยบวก-no-ไม่ใช่กฎก็จะได้รับการประเมินเป็น
Mego

โอเค แต่มันก็โอเคเพราะผลลัพธ์ระดับกลาง? ดังนั้น[1,2]_([1]_[1])เป็น[1,2]?
Christian Sievers

@ChristianSievers ฉบับที่นั่นจะส่งผลให้เพราะ[2] [1]_[1] -> [] -> 0 -> 1 -> [1]
Mego

คำตอบ:


9

Python 3, 803 788 753 749 744 748 745 740 700 695 682 ไบต์

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 ไบต์ขอบคุณ Mr.Xcoder

-5 ไบต์เพิ่มเติมขอบคุณ NGN

ประมาณ 40 ไบต์ขอบคุณ Jonathan French

Yuck ช่างเป็นอะไร! วิธีนี้ใช้งานได้โดยใช้นิพจน์ทั่วไปเพื่อตัดตัวเลขทั้งหมดในkชั้นเรียนของฉันและแปลงตัวดำเนินการทั้งหมดให้เป็นตัวดำเนินการของหลามที่เข้าใจแล้วใช้วิธีเวทมนต์ของkชั้นเรียนเพื่อจัดการกับคณิตศาสตร์ +-และ+--ในตอนท้ายสำหรับ.และ_มีสับเพื่อให้ลำดับความสำคัญที่ถูกต้อง ในทำนองเดียวกันผมไม่สามารถใช้**ประกอบการสำหรับ d เพราะการทำเช่นนั้นจะทำให้สามารถแยกวิเคราะห์เป็น1d4d4 1d(4d4)แต่ฉันห่อตัวเลขทั้งหมดใน parens ชุดพิเศษและทำ d .jเนื่องจากการเรียกใช้เมธอดมีลำดับความสำคัญสูงกว่าที่โอเปอเรเตอร์ บรรทัดสุดท้ายประเมินว่าเป็นฟังก์ชันที่ไม่ระบุตัวตนซึ่งประเมินค่านิพจน์


def __mod__(a, b)... ทำไมถึงมีช่องว่างระหว่างa,และb?
Mr. Xcoder


@ Mr.Xcoder ; __sub__ฉันคิดว่าคุณสามารถบันทึกไบต์โดยการเอาพื้นที่ที่ไม่จำเป็น: และอาจเป็นเช่นกันที่นี่: lambda a,b: l(.
Jonathan Frech

1
คุณสามารถบันทึกบางไบต์ด้วยการห่อรหัสทั้งหมดของคุณในexec("""...""".replace("...","..."))คำสั่งและแทนที่สตริงที่เกิดขึ้นบ่อยครั้ง (เช่นreturn ) ด้วยอักขระตัวเดียว อย่างไรก็ตามสำหรับฉันexec-strategy ดูเหมือนจะไม่ได้รับการยกเว้น ...
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 และไร้สาระเชิงวัตถุ จะใช้เพียงสองกองเท่านั้น: hสำหรับผู้ปฏิบัติงานและvสำหรับค่า การแยกและการประเมินผลจะถูกอินเตอร์ลี

ผลลัพธ์ระดับกลางจะแสดงเป็นเมทริกซ์ 2 × N โดยที่แถวแรกคือค่าสุ่มและแถวที่สองคือจำนวนของด้านบนลูกเต๋าที่สร้างพวกเขา เมื่อไม่มีการสร้างผลลัพธ์โดยตัวดำเนินการ "d" การโยนลูกเต๋าแถวที่สองจะมีตัวเลขโดยพลการ ค่าสุ่มเดียวคือเมทริกซ์ 2 × 1 และแยกไม่ออกจากรายการ 1 องค์ประกอบ


3

Python 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กับ [(x,0)]tuple จากนั้นการแสดงออกปกติครั้งแรกที่ดำเนินการdการดำเนินงานโดยการแทนที่ทั้งหมด[(x,0)]d[(b,0)]ที่มีการแสดงสตริงของอาร์เรย์ของ tuples [(1,b),(2,b),(3,b)]เช่น องค์ประกอบที่สองของแต่ละ tuple dเป็นตัวถูกดำเนินการที่สองเพื่อ จากนั้นนิพจน์ทั่วไปที่ตามมาจะดำเนินการกับตัวดำเนินการแต่ละตัว มี regex พิเศษเพื่อลบ parens ออกจากนิพจน์ที่คำนวณได้อย่างสมบูรณ์


3

Clojure, 731 720 ไบต์

(เมื่อมีการลบบรรทัดใหม่)

อัพเดท: 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: coerces รายการเป็นตัวเลข
  • g: ประเมินต้นไม้ไวยากรณ์ที่เป็นนามธรรม (S- นิพจน์ที่มี 3 รายการ)
  • F: แปลง infix AST เป็นเครื่องหมายคำนำหน้า (S-expressions) นอกจากนี้ยังใช้ลำดับความสำคัญของตัวถูกดำเนินการ
  • f: ใช้read-stringในการแปลงสตริงเป็นลำดับของตัวเลขและสัญลักษณ์ (infix AST) โดยเรียงผ่าน F -> g -> N แล้วส่งคืนหมายเลขผลลัพธ์

ฉันไม่แน่ใจว่าจะทดสอบอย่างละเอียดได้อย่างไรโดยผ่านการทดสอบทางสถิติกับการใช้งานอ้างอิง อย่างน้อย AST และการประเมินผลนั้นค่อนข้างง่ายที่จะติดตาม

ตัวอย่าง S-expression จาก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))))

golfed น้อยกว่าด้วยผลลัพธ์ระหว่างประเทศและการทดสอบ:

(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 ไบต์

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

ตัวแปลที่สร้างขึ้นโดยใช้tatsuไลบรารีตัวแยกวิเคราะห์ PEG อาร์กิวเมนต์แรกtatsu.parser()คือไวยากรณ์ PEG

class D(สำหรับ Die) subclasses ประเภทที่มีอยู่intแล้ว ค่าของมันคือผลลัพธ์ของการหมุน คุณลักษณะ.sคือจำนวนของด้านบนตาย

class S มีการกระทำแบบ semantic สำหรับ parser และใช้ล่าม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.