पृष्ठभूमि
मैं कुछ दोस्तों के साथ नियमित आधार पर डी एंड डी खेलता हूं। जबकि कुछ प्रणालियों / संस्करणों की जटिलता के बारे में बात करते हुए जब यह पासा को लुढ़काने और बोनस और दंड लागू करने की बात आती है, तो हम मजाक में पासा रोलिंग अभिव्यक्तियों के लिए कुछ अतिरिक्त जटिलता के साथ आए। उनमें से कुछ बहुत ही अपमानजनक थे (जैसे 2d6
कि मैट्रिक्स तर्कों 1 की तरह सरल पासा के भावों का विस्तार करना ), लेकिन बाकी एक दिलचस्प प्रणाली के लिए बनाते हैं।
चुनौती
एक जटिल पासा अभिव्यक्ति को देखते हुए, निम्नलिखित नियमों के अनुसार इसका मूल्यांकन करें और परिणाम का उत्पादन करें।
बुनियादी मूल्यांकन नियम
- जब भी कोई ऑपरेटर एक पूर्णांक की उम्मीद करता है, लेकिन एक ऑपरेंड के लिए एक सूची प्राप्त करता है, तो उस सूची का योग उपयोग किया जाता है
- जब भी कोई ऑपरेटर एक सूची की उम्मीद करता है, लेकिन एक ऑपरेंड के लिए एक पूर्णांक प्राप्त करता है, तो पूर्णांक को उस पूर्णांक की एक-तत्व सूची के रूप में माना जाता है
ऑपरेटर्स
सभी ऑपरेटर बाइनरी इन्फिक्स ऑपरेटर हैं। स्पष्टीकरण के उद्देश्य के a
लिए, बाएं ऑपरेंड b
होगा , और सही ऑपरेंड होगा। सूची संकेतन का उपयोग उन उदाहरणों के लिए किया जाएगा जहां ऑपरेटर ऑपरेंड के रूप में सूची ले सकते हैं, लेकिन वास्तविक अभिव्यक्तियों में केवल सकारात्मक पूर्णांक और ऑपरेटर होते हैं।
d
:a
रेंज में स्वतंत्र वर्दी रैंडम पूर्णांक आउटपुट[1, b]
- वरीयता: ३
- दोनों ऑपरेंड पूर्णांक हैं
- उदाहरण:
3d4 => [1, 4, 3]
,[1, 2]d6 => [3, 2, 6]
t
:b
सबसे कम मान लेंa
- वरीयता: २
a
एक सूची है,b
एक पूर्णांक है- यदि
b > len(a)
, सभी मान वापस कर दिए जाते हैं - उदाहरण:
[1, 5, 7]t1 => [1]
,[5, 18, 3, 9]t2 => [3, 5]
,3t5 => [3]
T
:b
उच्चतम मान लेंa
- वरीयता: २
a
एक सूची है,b
एक पूर्णांक है- यदि
b > len(a)
, सभी मान वापस कर दिए जाते हैं - उदाहरण:
[1, 5, 7]T1 => [7]
,[5, 18, 3, 9]T2 => [18, 9]
,3T5 => [3]
r
: यदि कोई तत्व अंदरb
हैंa
, तो उन तत्वों को फिर से सींचें, जो कुछ भीd
बयान उन्हें उत्पन्न करता है- वरीयता: २
- दोनों ऑपरेंड लिस्ट हैं
- रीरोलिंग केवल एक बार किया जाता है, इसलिए
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
कथन ने उन्हें उत्पन्न किया है- वरीयता: २
- दोनों ऑपरेंड लिस्ट हैं
- उदाहरण:
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
एक साथ- वरीयता: १
- दोनों ऑपरेंड पूर्णांक हैं
- उदाहरण:
2+2 => 4
,[2]+[2] => 4
,[3, 1]+2 => 6
-
: घटानाb
सेa
- वरीयता: १
- दोनों ऑपरेंड पूर्णांक हैं
b
हमेशा से कम होगीa
- उदाहरण:
2-1 => 1
,5-[2] => 3
,[8, 3]-1 => 10
.
: समवर्तीa
औरb
एक साथ- वरीयता: १
- दोनों ऑपरेंड लिस्ट हैं
- उदाहरण:
2.2 => [2, 2]
,[1].[2] => [1, 2]
,3.[4] => [3, 4]
_
: निकाले गएa
सभी तत्वों के साथ आउटपुटb
- वरीयता: १
- दोनों ऑपरेंड लिस्ट हैं
- उदाहरण:
[3, 4]_[3] => [4]
,[2, 3, 3]_3 => [2]
,1_2 => [1]
अतिरिक्त नियम
- यदि किसी अभिव्यक्ति का अंतिम मूल्य एक सूची है, तो इसे आउटपुट से पहले संक्षेपित किया जाता है
- शर्तों का मूल्यांकन केवल सकारात्मक पूर्णांक या सकारात्मक पूर्णांक की सूची के परिणामस्वरूप होगा - कोई भी अभिव्यक्ति जिसके परिणामस्वरूप गैर-सकारात्मक पूर्णांक या कम से कम एक गैर-सकारात्मक पूर्णांक वाली सूची में उन मानों को
1
s द्वारा प्रतिस्थापित किया जाएगा। - कोष्ठक का उपयोग समूह की शर्तों और मूल्यांकन के आदेश को निर्दिष्ट करने के लिए किया जा सकता है
- संचालकों का मूल्यांकन सबसे कम पूर्वता से सबसे कम पूर्वता के क्रम में किया जाता है, मूल्यांकन के साथ बंधी हुई स्थिति के मामले में बाएं से दाएं कार्यवाही होती है (इसलिए
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))
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))
)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
(किया हुआ)
संदर्भ कार्यान्वयन
यह संदर्भ कार्यान्वयन 0
परीक्षण योग्य, सुसंगत आउटपुट के लिए प्रत्येक अभिव्यक्ति के मूल्यांकन के लिए एक ही निरंतर बीज ( ) का उपयोग करता है । यह एसटीडीआईएन पर इनपुट की उम्मीद करता है, जिसमें प्रत्येक अभिव्यक्ति को अलग करने वाली नई रूपरेखाएँ हैं।
#!/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()
[१]: adb
मैट्रिक्स तर्कों के लिए हमारी परिभाषा AdX
प्रत्येक X
में a * b
, जहाँ, के लिए रोल करने की थी A = det(a * b)
। जाहिर है कि इस चुनौती के लिए बहुत बेतुका है।
-
कि मैं b
हमेशा a
गैर-सकारात्मक पूर्णांक प्राप्त करने का कोई रास्ता नहीं देखूंगा, इसलिए दूसरा अतिरिक्त नियम निरर्थक लगता है। OTOH, _
एक रिक्त सूची में परिणाम कर सकता है, जो समान मामलों में उपयोगी लगता है लेकिन जब पूर्णांक की आवश्यकता होती है तो इसका क्या अर्थ है? आम तौर पर मैं कहूंगा कि योग है 0
...
0
। नॉन-पॉज़िटिव नियम द्वारा, इसका मूल्यांकन ए के रूप में किया जाएगा 1
।
[1,2]_([1]_[1])
है [1,2]
?
[2]
, क्योंकि [1]_[1] -> [] -> 0 -> 1 -> [1]
।