Arka fon
Bazı arkadaşlarımla düzenli olarak D & D oynarım. Zar atma ve bonus ve ceza uygulamalarına gelince, bazı sistemlerin / versiyonların karmaşıklığından bahsederken, zar atma ifadeleri için şaka olarak bazı ek karmaşıklıklar bulduk. Bazıları (gibi basit zar ifadeleri uzanan gibi çok çirkin 2d6matris argümanları 1 ), ancak ilginç bir sistem için dinlenme yapmak.
Meydan okuma
Karmaşık bir zar ifadesi verildiğinde, aşağıdaki kurallara göre değerlendirin ve sonucu çıkartın.
Temel Değerlendirme Kuralları
- Bir operatör bir tam sayı beklediğinde ancak bir işlenen için bir liste aldığında, bu listenin toplamı kullanılır.
- Bir operatör bir liste beklese de, bir işlenen için bir tamsayı aldığında, tamsayı bu tamsayıyı içeren tek elemanlı bir liste olarak değerlendirilir.
Operatörler
Tüm operatörler ikili infix operatörleridir. Açıklama amacıyla a, sol operand bolacak ve sağ operand olacaktır. Operatörlerin operand olarak listeleri alabileceği örnekler için liste notasyonu kullanılacaktır, ancak gerçek ifadeler sadece pozitif tamsayı ve operatörlerden oluşur.
d:aaralıktaki çıktı bağımsız düzgün rasgele tamsayılar[1, b]- Öncelik: 3
- Her iki işlenen de tamsayıdır.
- Örnekler:
3d4 => [1, 4, 3],[1, 2]d6 => [3, 2, 6]
t: enbdüşük değerleri ala- Öncelik: 2
abir liste,bbir tamsayı- Eğer
b > len(a), tüm değerler iade edilir - Örnekler:
[1, 5, 7]t1 => [1],[5, 18, 3, 9]t2 => [3, 5],3t5 => [3]
T: enbyüksek değerleri ala- Öncelik: 2
abir liste,bbir tamsayı- Eğer
b > len(a), tüm değerler iade edilir - Örnekler:
[1, 5, 7]T1 => [7],[5, 18, 3, 9]T2 => [18, 9],3T5 => [3]
r: Herhangi bir element halindebolana, her şeyin kullanılarak, bu öğeleri rerolldonları oluşturulan deyimi- Öncelik: 2
- Her iki işlenen listedir
- Yine unsurlarını sahip olmak mümkündür, böylece Rerolling, yalnızca bir kez yapılır
bsonuç - Örnekler:
3d6r1 => [1, 3, 4] => [6, 3, 4],2d4r2 => [2, 2] => [3, 2],3d8r[1,8] => [1, 8, 4] => [2, 2, 4]
R: Herhangi bir element halindebolanayasal unsurları kadar, art arda bu öğeleri rerollbmevcut her şeyin kullanılarak,donları oluşturulan deyimi- Öncelik: 2
- Her iki işlenen listedir
- Örnekler:
3d6R1 => [1, 3, 4] => [6, 3, 4],2d4R2 => [2, 2] => [3, 2] => [3, 1],3d8R[1,8] => [1, 8, 4] => [2, 2, 4]
+: ekleavebbirlikte- Öncelik: 1
- Her iki işlenen de tamsayıdır.
- Örnekler:
2+2 => 4,[2]+[2] => 4,[3, 1]+2 => 6
-: Çıkarmabdana- Öncelik: 1
- Her iki işlenen de tamsayıdır.
bher zaman daha az olacaka- Örnekler:
2-1 => 1,5-[2] => 3,[8, 3]-1 => 10
.: birleştirmekavebbirlikte- Öncelik: 1
- Her iki işlenen listedir
- Örnekler:
2.2 => [2, 2],[1].[2] => [1, 2],3.[4] => [3, 4]
_: çıkarılanatüm elemanlarla çıktıb- Öncelik: 1
- Her iki işlenen listedir
- Örnekler:
[3, 4]_[3] => [4],[2, 3, 3]_3 => [2],1_2 => [1]
Ek Kurallar
- Bir ifadenin son değeri bir liste ise, çıktıdan önce toplanır.
- Terimlerin değerlendirilmesi yalnızca pozitif tamsayılar veya pozitif tamsayıların listesiyle sonuçlanır - pozitif olmayan bir tamsayıyla sonuçlanan herhangi bir ifade veya en az bir pozitif olmayan tamsayı içeren bir liste
1s ile değiştirilmiş olan değerlere sahip olacaktır. - Parantezler, terimleri gruplamak ve değerlendirme sırasını belirlemek için kullanılabilir
- Operatörler, en düşük öncelikten en düşük öncelik sırasına göre değerlendirilir, bağlı öncelik durumunda değerlendirme soldan sağa doğru ilerler (bu
1d4d4şekilde değerlendirilir(1d4)d4) - Listelerdeki elemanların sırası farketmez - bir listeyi elemanlarıyla farklı bir göreceli sırayla geri getirecek şekilde değiştiren bir operatör için tamamen kabul edilebilir
- Değerlendirilemeyen veya sonsuz bir döngüyle sonuçlanabilecek terimler (gibi
1d1R1veya gibi3d6R[1, 2, 3, 4, 5, 6]) geçerli değil
Test Kılıfları
Biçim: 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
Son test durumu hariç tümü referans uygulaması ile oluşturulmuştur.
Çalıştı örnek
İfade: 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](tam: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(Yapıldı)
Referans uygulaması
Bu referans uygulaması, 0her ifadeyi test edilebilir, tutarlı çıktılar için değerlendirmek için aynı sabit tohumu ( ) kullanır . Her ifadeyi ayıran yeni satırlarla STDIN'de girdi bekliyor.
#!/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]: Bizim tanımı adbmatris bağımsız değişkenler için silindirine olan AdXher biri için Xde a * bburada, A = det(a * b). Açıkçası bu meydan okuma için çok saçma.
-bu bdaima daha az olacaktır a, ikinci ek kural anlamsız görünüyor bu yüzden, pozitif olmayan tamsayılar almak için hiçbir şekilde görüyoruz. OTOH, _aynı durumlarda yararlı görünen boş bir listeye neden olabilir, ancak bir tamsayı gerektiğinde ne anlama geliyor? Normalde toplamın olduğunu söyleyebilirim 0...
0. Olumlu olmayan kuralla, a olarak değerlendirilir 1.
[1,2]_([1]_[1])bir [1,2]?
[2]nedeni sonuçlanacak [1]_[1] -> [] -> 0 -> 1 -> [1].