Lý lịch
Tôi chơi D & D thường xuyên với một số người bạn. Trong khi nói về sự phức tạp của một số hệ thống / phiên bản khi nói đến việc gieo xúc xắc và áp dụng các phần thưởng và hình phạt, chúng tôi đã đùa rằng đã đưa ra một số phức tạp bổ sung cho các biểu thức lăn xúc xắc. Một số trong số chúng quá thái quá (như mở rộng các biểu thức xúc xắc đơn giản như 2d6đối số ma trận 1 ), nhưng phần còn lại tạo nên một hệ thống thú vị.
Các thách thức
Đưa ra một biểu thức xúc xắc phức tạp, đánh giá nó theo các quy tắc sau và đưa ra kết quả.
Quy tắc đánh giá cơ bản
- Bất cứ khi nào một toán tử mong đợi một số nguyên nhưng nhận được một danh sách cho toán hạng, tổng của danh sách đó được sử dụng
- Bất cứ khi nào một toán tử mong đợi một danh sách nhưng nhận được một số nguyên cho toán hạng, số nguyên được coi là một danh sách một phần tử có chứa số nguyên đó
Người vận hành
Tất cả các toán tử là toán tử infix nhị phân. Với mục đích giải thích, asẽ là toán hạng bên trái và bsẽ là toán hạng bên phải. Ký hiệu danh sách sẽ được sử dụng cho các ví dụ trong đó các toán tử có thể lấy danh sách làm toán hạng, nhưng các biểu thức thực tế chỉ bao gồm các số nguyên và toán tử dương.
d: đầu raasố nguyên ngẫu nhiên thống nhất độc lập trong phạm vi[1, b]- Ưu tiên: 3
- Cả hai toán hạng đều là số nguyên
- Ví dụ:
3d4 => [1, 4, 3],[1, 2]d6 => [3, 2, 6]
t: lấy cácbgiá trị thấp nhất từa- Ưu tiên: 2
alà một danh sách,blà một số nguyên- Nếu
b > len(a), tất cả các giá trị được trả về - Ví dụ:
[1, 5, 7]t1 => [1],[5, 18, 3, 9]t2 => [3, 5],3t5 => [3]
T: lấy cácbgiá trị cao nhất từa- Ưu tiên: 2
alà một danh sách,blà một số nguyên- Nếu
b > len(a), tất cả các giá trị được trả về - Ví dụ:
[1, 5, 7]T1 => [7],[5, 18, 3, 9]T2 => [18, 9],3T5 => [3]
r: nếu có bất kỳ phần tửbnào tronga, hãy chạy lại các phần tử đó, sử dụng bất kỳdcâu lệnh nào đã tạo ra chúng- Ưu tiên: 2
- Cả hai toán hạng đều là danh sách
- Việc ghi lại chỉ được thực hiện một lần, do đó có thể vẫn có các yếu tố
btrong kết quả - Ví dụ:
3d6r1 => [1, 3, 4] => [6, 3, 4],2d4r2 => [2, 2] => [3, 2],3d8r[1,8] => [1, 8, 4] => [2, 2, 4]
R: nếu có bất kỳ phần tử nào ởbtronga, hãy lặp lại các phần tử đó cho đến khi không có phần tửbnào xuất hiện, sử dụng bất kỳdcâu lệnh nào tạo ra chúng- Ưu tiên: 2
- Cả hai toán hạng đều là danh sách
- Ví dụ:
3d6R1 => [1, 3, 4] => [6, 3, 4],2d4R2 => [2, 2] => [3, 2] => [3, 1],3d8R[1,8] => [1, 8, 4] => [2, 2, 4]
+: thêmavàbcùng nhau- Ưu tiên: 1
- Cả hai toán hạng đều là số nguyên
- Ví dụ:
2+2 => 4,[2]+[2] => 4,[3, 1]+2 => 6
-: Trừbtừa- Ưu tiên: 1
- Cả hai toán hạng đều là số nguyên
bsẽ luôn luôn ít hơna- Ví dụ:
2-1 => 1,5-[2] => 3,[8, 3]-1 => 10
.: nốiavàbcùng nhau- Ưu tiên: 1
- Cả hai toán hạng đều là danh sách
- Ví dụ:
2.2 => [2, 2],[1].[2] => [1, 2],3.[4] => [3, 4]
_: đầu raavới tất cả các yếu tốbbị loại bỏ- Ưu tiên: 1
- Cả hai toán hạng đều là danh sách
- Ví dụ:
[3, 4]_[3] => [4],[2, 3, 3]_3 => [2],1_2 => [1]
Quy tắc bổ sung
- Nếu giá trị cuối cùng của biểu thức là một danh sách, nó được tính tổng trước khi xuất
- Việc đánh giá các thuật ngữ sẽ chỉ dẫn đến các số nguyên dương hoặc danh sách các số nguyên dương - bất kỳ biểu thức nào dẫn đến một số nguyên không dương hoặc một danh sách chứa ít nhất một số nguyên không dương sẽ có các giá trị đó được thay thế bằng
1s - Dấu ngoặc đơn có thể được sử dụng để nhóm các thuật ngữ và chỉ định thứ tự đánh giá
- Các toán tử được đánh giá theo thứ tự ưu tiên cao nhất đến ưu tiên thấp nhất, với việc tiến hành đánh giá từ trái sang phải trong trường hợp ưu tiên ràng buộc (vì vậy
1d4d4sẽ được đánh giá là(1d4)d4) - Thứ tự của các phần tử trong danh sách không thành vấn đề - hoàn toàn chấp nhận được đối với toán tử sửa đổi danh sách để trả về nó với các phần tử theo thứ tự tương đối khác
- Các thuật ngữ không thể được đánh giá hoặc sẽ dẫn đến một vòng lặp vô hạn (như
1d1R1hoặc3d6R[1, 2, 3, 4, 5, 6]) không hợp lệ
Các trường hợp thử nghiệm
Định dạng: 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
Tất cả trừ trường hợp thử nghiệm cuối cùng được tạo ra với việc thực hiện tham chiếu.
Ví dụ làm việc
Biểu hiện: 1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3))
8d20t4T2 => [19, 5, 11, 6, 19, 15, 4, 20]t4T2 => [4, 5, 6, 11]T2 => [11, 6](đầy đủ1d(([11, 6])d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)):)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)))[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)))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)))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)))[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)))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]))1d[1, 3, 3, 6, 3, 2, 2] => 1d20 => 6(1d128).(6))1d128 => 55(55.6)55.6 => [55, 6]([55, 6])[55, 6] => 61(làm xong)
Thực hiện tham khảo
Việc triển khai tham chiếu này sử dụng cùng một hạt giống hằng ( 0) để đánh giá từng biểu thức cho các đầu ra nhất quán, có thể kiểm tra được. Nó mong đợi đầu vào trên STDIN, với các dòng mới phân tách từng biểu thức.
#!/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]: Định nghĩa của chúng tôi về các adbđối số ma trận là cuộn AdXcho từng Xtrong a * b, ở đâu A = det(a * b). Rõ ràng đó là quá vô lý cho thử thách này.
-điều đó bsẽ luôn luôn ít hơn atôi thấy không có cách nào để có được số nguyên không tích cực, vì vậy quy tắc bổ sung thứ hai dường như vô nghĩa. OTOH, _có thể dẫn đến một danh sách trống, có vẻ hữu ích trong các trường hợp tương tự nhưng điều đó có nghĩa là gì khi cần một số nguyên? Thông thường tôi sẽ nói tổng là 0...
0. Theo quy tắc không tích cực, nó sẽ được đánh giá là a 1.
[1,2]_([1]_[1])là [1,2]sao?
[2], bởi vì [1]_[1] -> [] -> 0 -> 1 -> [1].