Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Language agnostic 计算骰子滚动符号字符串 规则_Language Agnostic_Dice_Expression Evaluation - Fatal编程技术网

Language agnostic 计算骰子滚动符号字符串 规则

Language agnostic 计算骰子滚动符号字符串 规则,language-agnostic,dice,expression-evaluation,Language Agnostic,Dice,Expression Evaluation,编写一个接受字符串作为参数的函数,返回 中表达式的计算值, 包括加法和乘法 为了澄清问题,下面是EBNF对法律表达的定义: roll ::= [positive integer], "d", positive integer entity ::= roll | positive number expression ::= entity { [, whitespace], "+"|"*"[, whitespace], entity } 输入示例: “3d6+12” “4*d12+3” “d10

编写一个接受字符串作为参数的函数,返回 中表达式的计算值, 包括加法和乘法

为了澄清问题,下面是EBNF对法律表达的定义:

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

  • “3d6+12”
  • “4*d12+3”
  • “d100”
使用eval功能或类似功能不是禁止的,但我鼓励 不使用这些来解决问题。欢迎再次进入

我不能提供测试用例,因为输出应该是随机的;)

设置答案标题的格式:语言,n个字符(重要注释-无评估等)


Myruby解决方案,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

JavaScript解决方案,压缩时340个字符(无eval,支持前缀乘法器和后缀加法):

压缩版本(非常确定您可以做得更短):


函数c(s,m,n,f,a){m=parseInt(m);if(isNaN(m))m=1;n=parseInt(n);if(isNaN(n))n=1;f=parseInt(f);a=typeof(a)='string'?parseInt(a.replace(/\s/g'):0;if(isNaN(a))a=0;var r=0;对于(var i=0;iPython),压缩版本中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")

perleval版本,72个字符

sub e{$_=pop;s/(\d+)?d(\d+)/\$a/i;$a+=1+int rand$2-1for 0...$1-1;eval$_}

print e("4*d12+3"),"\n";
基于ruby解决方案,只能运行一次(在两次运行之间
undef$a

较短版本,68个字符,时髦(基于0)骰子

编辑

Perl5.8.8不喜欢上一个版本,这里是一个73字符的版本

sub e{$_=pop;s/(\d+)?d(\d+)/\$a/i;$a+=1+int rand$2-1for 1...$1||1;eval$_}
支持多卷的70字符版本

sub e{$_=pop;s/(\d*)d(\d+)/$a=$1||1;$a+=int rand$a*$2-($a-1)/ieg;eval}

perl,无evals,144个字符,工作多次,支持多掷骰子

sub e{($c=pop)=~y/+* /PT/d;$b='(\d+)';map{$a=0while$c=~s!$b?$_$b!$d=$1||1;$a+=1+int rand$2for 1..$d;$e=$2;/d/?$a:/P/?$d+$e:$d*$e!e}qw(d T P);$c}
扩展版本,带注释

sub f {
    ($c = pop); #assign first function argument to $c
    $c =~ tr/+* /PT/d;  #replace + and * so we won't have to escape them later.
                        #also remove spaces
    #for each of 'd','T' and 'P', assign to $_ and run the following
    map {
        #repeatedly replace in $c the first instance of <number> <operator> <number> with
        #the result of the code in second part of regex, capturing both numbers and
        #setting $a to zero after every iteration
        $a=0 while $c =~ s[(\d+)?$_(\d+)][
            $d = $1 || 1;   #save first parameter (or 1 if not defined) as later regex 
                            #will overwrite it
            #roll $d dice, sum in $a
            for (1..$d)
            {
                $a += 1 + int rand $2;
            }
            $e = $2;        #save second parameter, following regexes will overwrite
            #Code blocks return the value of their last statement
            if (/d/)
            {
                $a; #calculated dice throw
            }
            elsif (/P/)
            {
                $d + $e;
            }
            else
            {
                $d * $e;
            }
        ]e;
    } qw(d T P);
    return $c;
}
subf{
($c=pop)#将第一个函数参数赋值给$c
$c=~tr/+*/PT/d;#replace+和*这样我们以后就不必逃避它们了。
#还要删除空格
#对于'd'、'T'和'P'中的每一个,分配给$并运行以下命令
地图{
#重复将$c中的第一个实例替换为
#正则表达式第二部分中的代码结果,捕获数字和
#每次迭代后将$a设置为零
$a=0,而$c=~s[(\d+)?$\uD+][
$d=$1 | | 1;#将第一个参数(或1,如果未定义)保存为后面的正则表达式
#将覆盖它
#掷$d骰子,以$a求和
费用(1..$d)
{
$a+=1+整数兰特$2;
}
$e=$2;#保存第二个参数,以下正则表达式将覆盖
#代码块返回最后一条语句的值
如果(/d/)
{
$a;#计算掷骰
}
elsif(/P/)
{
$d+e;
}
其他的
{
$d*$e;
}
]e;
}qw(dtp);
返回$c;
}

编辑清理,更新了最新版本的解释

python,模糊版本中有197个字符

可读版本:369个字符。无需评估,直接解析

import random
def dice(s):
    return sum(term(x) for x in s.split('+'))
def term(t):
    p = t.split('*')
    return factor(p[0]) if len(p)==1 else factor(p[0])*factor(p[1])
def factor(f):
    p = f.split('d')
    if len(p)==1:
        return int(f)
    return sum(random.randint(1, int(g[1]) if g[1] else 6) for \
               i in range(int(g[0]) if g[0] else 1))
压缩版本:258个字符,单字符名称,滥用的条件表达式,逻辑表达式中的快捷方式:

import random
def d(s):
 return sum(t(x.split('*')) for x in s.split('+'))
def t(p):
 return f(p[0])*f(p[1]) if p[1:] else f(p[0])
def f(s):
 g = s.split('d')
 return sum(random.randint(1, int(g[1] or 6)) for i in range(int(g[0] or 1))) if g[1:] else int(s)
模糊版本:216个字符,使用reduce,大量映射以避免“def”,“return”

最后一个版本:197个字符,折叠在@Brain的评论中,添加了测试运行

import random
R=reduce;D=lambda s:sum(map(lambda t:R(int.__mul__,map(lambda f:R(lambda x,y:sum(random.randint(1,int(y or 6))for i in[0]*int(x or 1)),f.split('d')+[1]),t.split('*'))),s.split('+')))
测试:

>>> for dice_expr in ["3d6 + 12", "4*d12 + 3","3d+12", "43d29d16d21*9+d7d9*91+2*d24*7"]: print dice_expr, ": ", list(D(dice_expr) for i in range(10))
... 
3d6 + 12 :  [22, 21, 22, 27, 21, 22, 25, 19, 22, 25]
4*d12 + 3 :  [7, 39, 23, 35, 23, 23, 35, 27, 23, 7]
3d+12 :  [16, 25, 21, 25, 20, 18, 27, 18, 27, 25]
43d29d16d21*9+d7d9*91+2*d24*7 :  [571338, 550124, 539370, 578099, 496948, 525259, 527563, 546459, 615556, 588495]
此解决方案无法处理没有相邻数字的空白。因此“43d29d16d21*9+d7d9*91+2*d24*7”将起作用,但“43d29d16d21*9+d7d9*91+2*d24*7”将不起作用,因为第二个空格(在“+”和“d”之间)。它可以通过首先从s中删除空格来更正,但这会使代码长度超过200个字符,因此我将保留该错误。

C#类。它递归计算加法和乘法,从左到右计算链式掷骰

编辑:

  • 删除
    。在每次通话中替换(“,”)
  • int.TryParse
    上添加了
    .Trim()
  • 现在所有的工作都是用一种方法完成的
  • 如果未指定模具面计数,则假定为6(参见Wiki文章)
  • 重构冗余调用以解析“d”的左侧
  • 重构不必要的
    if
    语句
缩小:(411字节)

class D{Random r=new Random();public int r(string s){int t=0;var a=s.Split('+');if(a.Count()>1)foreach(var b in a)t+=r(b);else{var m=a[0]。Split('*');if(m.Count()>1{t=1;foreach(var n in m)t*=r(n);}else{var D=m[0]。Split('D');if(!int.trype(D[0]),t=0);Trim=0;int=1;int=1)
{
t=1;//这样我们就不会把结果归零。。。
foreach(变量n,单位为m)
t*=R(n);
}
其他的
{
//模具定义是我们的最高优先顺序
var d=m[0]。拆分('d');
//这个操作数将是我们的骰子计数、静态数字或其他我们需要的数字
sub f {
    ($c = pop); #assign first function argument to $c
    $c =~ tr/+* /PT/d;  #replace + and * so we won't have to escape them later.
                        #also remove spaces
    #for each of 'd','T' and 'P', assign to $_ and run the following
    map {
        #repeatedly replace in $c the first instance of <number> <operator> <number> with
        #the result of the code in second part of regex, capturing both numbers and
        #setting $a to zero after every iteration
        $a=0 while $c =~ s[(\d+)?$_(\d+)][
            $d = $1 || 1;   #save first parameter (or 1 if not defined) as later regex 
                            #will overwrite it
            #roll $d dice, sum in $a
            for (1..$d)
            {
                $a += 1 + int rand $2;
            }
            $e = $2;        #save second parameter, following regexes will overwrite
            #Code blocks return the value of their last statement
            if (/d/)
            {
                $a; #calculated dice throw
            }
            elsif (/P/)
            {
                $d + $e;
            }
            else
            {
                $d * $e;
            }
        ]e;
    } qw(d T P);
    return $c;
}
import random
def dice(s):
    return sum(term(x) for x in s.split('+'))
def term(t):
    p = t.split('*')
    return factor(p[0]) if len(p)==1 else factor(p[0])*factor(p[1])
def factor(f):
    p = f.split('d')
    if len(p)==1:
        return int(f)
    return sum(random.randint(1, int(g[1]) if g[1] else 6) for \
               i in range(int(g[0]) if g[0] else 1))
import random
def d(s):
 return sum(t(x.split('*')) for x in s.split('+'))
def t(p):
 return f(p[0])*f(p[1]) if p[1:] else f(p[0])
def f(s):
 g = s.split('d')
 return sum(random.randint(1, int(g[1] or 6)) for i in range(int(g[0] or 1))) if g[1:] else int(s)
import random
def d(s):
 return sum(map(lambda t:reduce(lambda x,y:x*y,map(lambda f:reduce(lambda x,y:sum(random.randint(1,int(y or 6)) for i in range(int(x or 1))), f.split('d')+[1]),t.split('*')),1),s.split('+')))
import random
R=reduce;D=lambda s:sum(map(lambda t:R(int.__mul__,map(lambda f:R(lambda x,y:sum(random.randint(1,int(y or 6))for i in[0]*int(x or 1)),f.split('d')+[1]),t.split('*'))),s.split('+')))
>>> for dice_expr in ["3d6 + 12", "4*d12 + 3","3d+12", "43d29d16d21*9+d7d9*91+2*d24*7"]: print dice_expr, ": ", list(D(dice_expr) for i in range(10))
... 
3d6 + 12 :  [22, 21, 22, 27, 21, 22, 25, 19, 22, 25]
4*d12 + 3 :  [7, 39, 23, 35, 23, 23, 35, 27, 23, 7]
3d+12 :  [16, 25, 21, 25, 20, 18, 27, 18, 27, 25]
43d29d16d21*9+d7d9*91+2*d24*7 :  [571338, 550124, 539370, 578099, 496948, 525259, 527563, 546459, 615556, 588495]
class D{Random r=new Random();public int R(string s){int t=0;var a=s.Split('+');if(a.Count()>1)foreach(var b in a)t+=R(b);else{var m=a[0].Split('*');if(m.Count()>1){t=1;foreach(var n in m)t*=R(n);}else{var d=m[0].Split('d');if(!int.TryParse(d[0].Trim(),out t))t=0;int f;for(int i=1;i<d.Count();i++){if(!int.TryParse(d[i].Trim(),out f))f=6;int u=0;for(int j=0;j<(t== 0?1:t);j++)u+=r.Next(1,f);t+=u;}}}return t;}}
    class D
    {
        /// <summary>Our Random object.  Make it a first-class citizen so that it produces truly *random* results</summary>
        Random r = new Random();

        /// <summary>Roll</summary>
        /// <param name="s">string to be evaluated</param>
        /// <returns>result of evaluated string</returns>
        public int R(string s)
        {
            int t = 0;

            // Addition is lowest order of precedence
            var a = s.Split('+');

            // Add results of each group
            if (a.Count() > 1)
                foreach (var b in a)
                    t += R(b);
            else
            {
                // Multiplication is next order of precedence
                var m = a[0].Split('*');

                // Multiply results of each group
                if (m.Count() > 1)
                {
                    t = 1; // So that we don't zero-out our results...

                    foreach (var n in m)
                        t *= R(n);
                }
                else
                {
                    // Die definition is our highest order of precedence
                    var d = m[0].Split('d');

                    // This operand will be our die count, static digits, or else something we don't understand
                    if (!int.TryParse(d[0].Trim(), out t))
                        t = 0;

                    int f;

                    // Multiple definitions ("2d6d8") iterate through left-to-right: (2d6)d8
                    for (int i = 1; i < d.Count(); i++)
                    {
                        // If we don't have a right side (face count), assume 6
                        if (!int.TryParse(d[i].Trim(), out f))
                            f = 6;

                        int u = 0;

                        // If we don't have a die count, use 1
                        for (int j = 0; j < (t == 0 ? 1 : t); j++)
                            u += r.Next(1, f);

                        t += u;
                    }
                }
            }

            return t;
        }
    }
    static void Main(string[] args)
    {
        var t = new List<string>();
        t.Add("2d6");
        t.Add("2d6d6");
        t.Add("2d8d6 + 4d12*3d20");
        t.Add("4d12");
        t.Add("4*d12");
        t.Add("4d"); // Rolls 4 d6

        D d = new D();
        foreach (var s in t)
            Console.WriteLine(string.Format("{0}\t{1}", d.R(s), s));
    }
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
(defn roll-dice
  [string]
  (let [parts (. (. (. string replace "-" "+-") replaceAll "\\s" "") split "\\+")
        dice-func (fn [d-notation]
                    (let [bits (. d-notation split "d")]
                      (if (= 1 (count bits))
                        (Integer/parseInt (first bits))  ; Just a number, like 12
                        (if (zero? (count (first bits)))
                          (inc (rand-int (Integer/parseInt (second bits))))  ; Just d100 or some such
                          (if (. (first bits) contains "*")
                            (* (Integer/parseInt (. (first bits) replace "*" ""))
                                (inc (rand-int (Integer/parseInt (second bits)))))
                            (reduce +
                              (map #(+ 1 %)
                                    (map rand-int
                                        (repeat
                                          (Integer/parseInt (first bits))
                                          (Integer/parseInt (second bits)))))))))))]      
    (reduce + (map dice-func parts))))
$ jconsole e=:".@([`('%'"_)@.(=&'/')"0@,)@:(3 :'":(1".r{.y)([:+/>:@?@$) ::(y&[)0".}.y}.~r=.y i.''d'''@>)@;: e '3d6 + 12' 20 e 10$,:'3d6 + 12' 19 23 20 26 24 20 20 20 24 27 e 10$,:'4*d12 + 3' 28 52 56 16 52 52 52 36 44 56 e 10$,:'d100' 51 51 79 58 22 47 95 6 5 64
import random,re
f=lambda s:eval(re.sub(r'(\d*)d(\d+)',lambda m:int(m.group(1)or 1)*('+random.randint(1,%s)'%m.group(2)),s))
import random,re
f=lambda s:sum(int(c or 0)+sum(random.randint(1,int(b))for i in[0]*int(a or 1))for a,b,c in re.findall(r'(\d*)d(\d+)(\s*[+-]\s*\d+)?',s))
def g s,o=%w{\+ \* d}
o[a=0]?s[/#{p=o.pop}/]?g(s.sub(/(\d+)?\s*(#{p})\s*(\d+)/i){c=$3.to_i
o[1]?($1||1).to_i.times{a+=rand c}+a:$1.to_i.send($2,c)},o<<p):g(s,o):s
end
def evaluate(string, opers = ["\\+","\\*","d"])
  if opers.empty?
    string
  else
    if string.scan(opers.last[/.$/]).empty? # check if string contains last element of opers array

      # Proceed to next operator from opers array.

      opers.pop
      evaluate(string, opers)

    else # string contains that character...

      # This is hard to deobfuscate. It substitutes subexpression with highest priority with
      # its value (e.g. chooses random value for XdY, or counts value of N+M or N*M), and
      # calls recursively evaluate with substituted string.

      evaluate(string.sub(/(\d+)?\s*(#{opers.last})\s*(\d+)/i) { a,c=0,$3.to_i; ($2 == 'd') ? ($1||1).to_i.times{a+=rand c}+a : $1.to_i.send($2,c) }, opers)

    end
  end
end
let r = new System.Random()
let f(s:string)=let g d o i p (t:string)=t.Split([|d|])|>Array.fold(fun s x->o s (p x))i in g '+'(+)0(g '*' (*) 1 (fun s->let b=ref true in g 'd'(+)1(fun t->if !b then b:=false;(if t.Trim()=""then 1 else int t)else r.Next(int t))s))s
let f (s:string) =
    let g d o i p (t:string) =
        t.Split([|d|]) |> Array.fold (fun s x -> o s (p x)) i
    g '+' (+) 0 (g '*' (*) 1 (fun s ->
                                        let b = ref true
                                        g 'd' (+) 1 (fun t ->
                                        if !b then b := false; (if t.Trim() = "" then 1 else int t)
                                        else r.Next(int t)) s)) s
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]];
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;}
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)