Оценить строки обозначения костей - PullRequest
21 голосов
/ 23 июня 2009

Правила

Напишите функцию, которая принимает строку в качестве параметра, возвращая оцененное значение выражения в нотации в кости , включая сложение и умножение.

Чтобы прояснить ситуацию, приведем определение юридических выражений EBNF:

roll ::= [positive integer], "d", positive integer
entity ::= roll | positive number
expression ::= entity { [, whitespace], "+"|"*"[, whitespace], entity }

Пример ввода:

  • "3d6 + 12"
  • "4 * d12 + 3"
  • "d100"

Использование функций eval или аналогичных не запрещено, но я рекомендую к решению без использования этих. Повторный вход приветствуется.

Я не могу предоставить контрольные примеры, поскольку выходные данные должны быть случайными;).

Отформатируйте заголовки ваших ответов: язык, n символов (важные примечания - нет оценки и т. Д.)


My ruby ​​ solution, 92 81 символов, используя eval:

def f s
eval s.gsub(/(\d+)?d(\d+)/i){eval"a+=rand $2.to_i;"*a=($1||1).to_i}
end

Другое решение ruby ​​, не короче (92 символа), но я нахожу это интересным - оно все еще использует eval, но на этот раз весьма креативно.

class Fixnum
def**b
eval"a+=rand b;"*a=self
end
end
def f s
eval s.gsub(/d/,'**')
end

Ответы [ 14 ]

1 голос
/ 24 июля 2014

JAVASCRIPT , 1399 символов, без оценки

старый пост, я знаю. но я стараюсь внести свой вклад

Roll = window.Roll || {};

Roll.range = function (str) {
    var rng_min, rng_max, str_split,
        delta, value;

    str = str.replace(/\s+/g, "");
    str_split = str.split("-");
    rng_min = str_split[0];
    rng_max = str_split[1];

    rng_min = parseInt(rng_min) || 0;
    rng_max = Math.max(parseInt(rng_max), rng_min) || rng_min;

    delta = (rng_max - rng_min + 1);

    value = Math.random() * delta;
    value = parseInt(value);

    return value + rng_min;
};

Roll.rollStr = function (str) {
    var check,
        qta, max, dice, mod_opts, mod,
        rng_min, rng_max,
        rolls = [], value = 0;

    str = str.replace(/\s+/g, "");
    check = str.match(/(?:^[-+]?(\d+)?(?:\/(\d+))?[dD](\d+)(?:([-+])(\d+)\b)?$|^(\d+)\-(\d+)$)/);

    if (check == null) {return "ERROR"}
    qta = check[1];
    max = check[2];
    dice = check[3];
    mod_opts = check[4];
    mod = check[5];
    rng_min = check[6];
    rng_max = check[7];
    check = check[0];

    if (rng_min && rng_max) {return Roll.range(str)}

    dice = parseInt(dice);
    mod_opts = mod_opts || "";
    mod = parseInt(mod) || 0;
    qta = parseInt(qta) || 1;
    max = Math.max(parseInt(max), qta) || qta;

    for (var val; max--;) {
        val = Math.random() * dice;
        val = Math.floor(val) + 1;
        rolls.push(val);
    }

    if (max != qta) {
        rolls.sort(function (a, b) {return a < b});
        rolls.unshift(rolls.splice(0, qta));
    }

    while (rolls[0][0]) {value += rolls[0].shift();}

    if (mod_opts == "-") {value -= mod;}
    else {value += mod;}

    return value
};

if (!window.diceRoll) {window.diceRoll= Roll.rollStr;}

это один бросок костей, например, "2d8 + 2" или "4-18", "3 / 4d6" (лучше всего 3 из 4 d6)

diceRoll("2d8+2"); 
diceRoll("4-18");
diceRoll("3/4d6");

для проверки кумулятивных бросков, лучший цикл для сопоставленного результата над входной строкой, такой как

r = "2d8+2+3/4d6"
r.match(/([-+])?(\d+)?(?:\/(\d+))?[dD](\d+)(?:([-+])(\d+)\b)?/g);
// => ["2d8+2", "+3/4d6"]
// a program can manage the "+" or "-" on the second one (usually is always an addiction)
1 голос
/ 24 июня 2009

Ruby, 87 символов, использует eval

Вот мое решение Ruby, частично основанное на ОП. Он на пять символов короче и использует eval только один раз.

def f s
eval s.gsub(/(\d+)?[dD](\d+)/){n=$1?$1.to_i: 1;n.times{n+=rand $2.to_i};n}
end

Читаемая версия кода:

def f s
    eval (s.gsub /(\d+)?[dD](\d+)/ do
        n = $1 ? $1.to_i : 1
        n.times { n += rand $2.to_i }
        n
    end)
end
1 голос
/ 23 июня 2009

Python , 452 байта в сжатой версии

Я не уверен, что это круто, безобразно или просто глупо, но было весело писать это.

Что мы делаем следующим образом: мы используем регулярные выражения (которые обычно не подходят для такого рода вещей), чтобы преобразовать строку обозначений кубиков в список команд на небольшом, основанном на стеке языке. Этот язык имеет четыре команды:

  • mul умножает два верхних числа в стеке и выводит результат
  • add добавляет два старших числа в стек и выводит результат
  • roll извлекает размер кубика из стека, затем подсчитывает, бросает количество раз на стороне размера кости и выталкивает результат
  • число просто помещается в стек

Затем этот список команд оценивается.

import re, random

def dice_eval(s):
    s = s.replace(" ","")
    s = re.sub(r"(\d+|[d+*])",r"\1 ",s) #seperate tokens by spaces
    s = re.sub(r"(^|[+*] )d",r"\g<1>1 d",s) #e.g. change d 6 to 1 d 6
    while "*" in s:
        s = re.sub(r"([^+]+) \* ([^+]+)",r"\1 \2mul ",s,1)
    while "+" in s:
        s = re.sub(r"(.+) \+ (.+)",r"\1 \2add ",s,1)
    s = re.sub(r"d (\d+) ",r"\1 roll ",s)

    stack = []

    for token in s.split():
        if token == "mul":
            stack.append(stack.pop() * stack.pop())
        elif token == "add":
            stack.append(stack.pop() + stack.pop())
        elif token == "roll":
            v = 0
            dice = stack.pop()
            for i in xrange(stack.pop()):
                v += random.randint(1,dice)
            stack.append(v)
        elif token.isdigit():
            stack.append(int(token))
        else:
            raise ValueError

    assert len(stack) == 1

    return stack.pop() 

print dice_eval("2*d12+3d20*3+d6")

Кстати (это обсуждалось в комментариях к вопросу), эта реализация позволит использовать строки типа "2d3d6", понимая это как "дважды бросить d3, а затем бросить d6 столько раз, сколько получено в результате двух бросков. «

Кроме того, хотя есть некоторая проверка ошибок, она все еще ожидает допустимого ввода. Например, пропуск "* 4" приведет к бесконечному циклу.

Вот сжатая версия (не очень):

import re, random
r=re.sub
def e(s):
 s=r(" ","",s)
 s=r(r"(\d+|[d+*])",r"\1 ",s)
 s=r(r"(^|[+*] )d",r"\g<1>1 d",s)
 while"*"in s:s=r(r"([^+]+) \* ([^+]+)",r"\1 \2M ",s)
 while"+"in s:s=r(r"(.+) \+ (.+)",r"\1 \2A ",s)
 s=r(r"d (\d+)",r"\1 R",s)
 t=[]
 a=t.append
 p=t.pop
 for k in s.split():
  if k=="M":a(p()*p())
  elif k=="A":a(p()+p())
  elif k=="R":
   v=0
   d=p()
   for i in [[]]*p():v+=random.randint(1,d)
   a(v)
  else:a(int(k))
 return p()
0 голосов
/ 25 июня 2009

PHP , 147 символов, без оценки:

</p> <pre><code>preg_match('/(\d+)?d(\d+)[\s+]?([\+\*])?[\s+]?(\d+)?/',$i,$a);$d=rand(1,$a[2])*((!$a[1])?1:$a[1]);$e['+']=$d+$a[4];$e['*']=$d*$a[4];print$e[$a[3]];

$i содержит входную строку.

Редактировать: упс, забыл про префиксную операцию. BRB.

...