Evaluate dice rolling notation strings

2019-03-08 10:54发布

Rules

Write a function that accepts string as a parameter, returning evaluated value of expression in dice notation, including addition and multiplication.

To clear the things up, here comes EBNF definition of legal expressions:

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

Example inputs:

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

Using eval functions, or similar, is not forbidden, but I encourage to solving without using these. Re-entrancy is welcome.

I cannot provide test-cases, as output should be random ;).

Format your answers' titles: language, n characters (important notes — no eval, etc.)


My ruby solution, 92 81 characters, using eval:

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

Another ruby solution, not shorter (92 characters), but I find it interesting — it still uses eval but this time in quite creative way.

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

14条回答
乱世女痞
2楼-- · 2019-03-08 11:24

perl eval version, 72 chars

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

runs like

print e("4*d12+3"),"\n";

Based on the ruby solution, can only run once(you should undef $a between runs).

shorter version, 68 chars, funky (0 based) dice

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

EDIT

perl 5.8.8 didn't like the previous version, here's a 73 char version that works

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

70 char version supporting multiple rolls

sub e{$_=pop;s/(\d*)d(\d+)/$a=$1||1;$a+=int rand$a*$2-($a-1)/ieg;eval}
查看更多
混吃等死
3楼-- · 2019-03-08 11:25

F# (no eval and no regex)

233 characters

This solution should be fully generic, in that it can handle just about any string you throw at it, even something crazy such as:

43d29d16d21*9 + d7d9*91 + 2*d24*7

Need to define this globally:

let r = new System.Random()

Fully obfuscated version:

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

Readable version:

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

I'm challenging someone to beat this solution (in any language) without using eval or regular expressions. I think it's likely to be possible, but I am interested in seeing the approach still.

查看更多
登录 后发表回答