36
\$\begingroup\$

Your task is to make a program that takes in an integer n > 1, and outputs the roll of a single n-sided die. However, this dice follows the rules for exploding dice.

When you roll the die, check what value you rolled. If you got the maximum for that kind of die (on a standard d4 that would be 4, or 6 on a d6, etc.), roll again and add the new roll to that total. Each roll continues adding to the total, until you don't roll the max number anymore. That final number is still added though.

Your program should take in a single integer n, and roll the exploding n-sided die. Here's an example distribution to show what it should look like for n=4. Note that you should never output any multiples of n, since they will always explode.

You can assume the stack size for any recursion you do is infinite, and your random function must meet our standards for randomness (built-in random generator or time/date). Your random function should also be as uniform as possible, vs. something like a geometric distribution, since these are dice we're talking about.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ does the program have to be perfect? Like can its distribution be off by some extremely low amount? \$\endgroup\$
    – Maltysen
    Apr 12, 2019 at 14:38
  • \$\begingroup\$ To: Riker; RE: @Maltysen's comment above; or extremely high amount? \$\endgroup\$
    – Miriam
    Apr 12, 2019 at 15:09
  • 3
    \$\begingroup\$ @ArtemisFowl See our standards for randomness. Also, here. \$\endgroup\$
    – Riker
    Apr 12, 2019 at 22:46

54 Answers 54

1
2
2
\$\begingroup\$

CJam, 19 bytes

qi{__mr)_T+:T;=}g;T

Explanation:

T is pre-set to 0

qi{__mr)_T+:T;=}g;T - whole code
qi                  - read input as integer (n) | Stack: n
  {            }    - block
   __               - Duplicate twice | Stack: n n n
     mr)            - Choose a random number from 1 to n (r). Since 'mr' picks a
                      number from 0 to n-1, the number has to be incremented with ')' 
                      Stack: n n r
        _           - Duplicate |  Stack: n n r r
         T          - push T | Stack: n n r r T
          +         - add the random number to T (t) | Stack: n n r t
           :T;      - pop the value and store in T | Stack: n n r
              =     - are the top two stack values the same (c) | Stack: n c
               }
                g   - do while loop that pops the condition from the stack after each
                      iteration | Stack: n
                 ;  - pop the top stack element and discard | Stack: T
                  T - push T | Stack: T
                    - implicit output

Or in pseudocode:

input n
var sum = 0
do {
    var random_number = pick random number from 1 to n
    sum = sum + random_number
} while (random_number == n)
output n

As a flowchart:

Flowchart of code

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Excel (pure), 43 bytes

=A1*INT(-LOG(RAND(),A1))-INT(RAND()*(1-A1))

Takes input from A1, outputs in the cell you put this formula.

Explanation

This uses the observation from Robin Ryder's R solution that the requested distribution is the sum of n times a Geom(1-1/n) and an independent Uniform(1..n-1) distribution. The clever bit is that

INT(-LOG(RAND(),A1))

uses a logarithm to scale a Uniform(0,1) distribution exactly to the desired geometric distribution: for instance, with A1=6, the LOG maps the interval (1/6,1) to the interval (-1,0), the interval (1/36,1/6) to the interval (-2, -1), and so on. I also save a +1 on the uniform distribution by scaling a random number "backwards" to (1-A1, 0), then subtracting the floor of this negative number.

\$\endgroup\$
2
\$\begingroup\$

Octave/MATLAB with Statistics Package/Toolbox, 30 bytes

@(n)geornd(1-1/n)*n+randi(n-1)

Try it online!

How it works

This is an anonymous function which takes n as input and produces a number obtained as follows. The function generates a geometric random variable with parameter 1-1/n (this models the number of rolls that produce n), multiplies by n, and adds a random variable uniformly distributed from 1 to n-1 (this models the last roll).

\$\endgroup\$
2
+100
\$\begingroup\$

Vyxal, 25 10 bytes

λ?:ɾ℅~=[x+

Try it Online!

-15 thanks to Aaron Miller. This is a fully functional program as lambdas are automatically called at the end.

\$\endgroup\$
10
  • \$\begingroup\$ recursion would probably make this simpler. \$\endgroup\$
    – Razetime
    May 8, 2021 at 13:46
  • \$\begingroup\$ @Razetime Yes probably, I'll try \$\endgroup\$
    – emanresu A
    May 8, 2021 at 20:21
  • \$\begingroup\$ 11 bytes with recursion \$\endgroup\$ Jun 22, 2021 at 20:28
  • \$\begingroup\$ @AaronMiller 10 as you can close the if in the footer. \$\endgroup\$
    – emanresu A
    Jun 22, 2021 at 20:41
  • \$\begingroup\$ Ok, I wasn't sure. I haven't really done many submissions that were just functions. Thanks for saying so! \$\endgroup\$ Jun 22, 2021 at 20:43
2
\$\begingroup\$

Excel VBA, 46 bytes

Thanks to @TaylorRaine

Do:v=-Int(-[A1]*Rnd):t=t+v:Loop While[A1]=v:?t

Executed in the command window.

As a user-defined function.

Excel VBA, 108 67 bytes

Function z(i)
Do
v=Int((i*Rnd)+1)
z=z+v
Loop While v=i
End Function
\$\endgroup\$
2
  • \$\begingroup\$ You can get this down quite a bit by using a do..loop while loop, and converting the function into an immediate window function. - Do:v=-Int(-[A1]*Rnd):t=t+v:Loop While[A1]=v:?t - 46 Bytes \$\endgroup\$ Apr 15, 2019 at 19:03
  • 1
    \$\begingroup\$ @TaylorScott Thanks, I forgot that Do x While y existed in Excel VBA. \$\endgroup\$
    – willuwontu
    Apr 15, 2019 at 20:23
1
\$\begingroup\$

><>, 90 bytes

0&  v
v:::<             <
v/1>{1-:?!v}
>x0^v10~  <
 ^
5=?v>:@}}*{+{2*l
&n;>,*:1%-1+:&+&=?^

Try it online!

The whitespace on the second line is bugging me. I'll work on golfing that out.


><> doesn't have a nice method for producing uniform random integers. This approach generates, for input \$N\$, a random number produced by generating \$N\$ random bits, then taking the resulting binary integer and dividing it by \$2^N\$. This process is repeated until \$N\$ is not generated by this process.

\$\endgroup\$
1
1
\$\begingroup\$

Attache, 30 bytes

f:=${If[x=y,f@x+y,y]}#1&Random

Try it online!

Direct implementation of the process.

Alternatives

36 bytes: ${NestWhile[{_+Random[1,x]},x&`|,0]}

37 bytes: ${NestWhile[{_+Random[1,x]},{x|_},0]}

\$\endgroup\$
1
\$\begingroup\$

Perl 6, 26 bytes

{[+] {^$_ .roll+1}...$_>*}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Python 3, 80 bytes

This is pretty much just a tail-call recursive version of @Artemis Fowl's answer, but I liked doing it without unrolling into a while loop.

Uses an accumulator parameter to return the total rolled value once the exploding stops.

from random import*
def r(n,a=0):v=randint(1,n);a+=v;return r(n,a)if v==n else a
\$\endgroup\$
2
  • \$\begingroup\$ I don’t think python does tail-call optimization, does it? Regardless, good answer anyways! \$\endgroup\$
    – JPeroutek
    Apr 15, 2019 at 17:21
  • \$\begingroup\$ Unfortunately not. I've heard that Guido doesn't want python to turn into a functional language 😜 \$\endgroup\$ Apr 15, 2019 at 17:23
1
\$\begingroup\$

Charcoal, 17 bytes

NθW⁼Lυ№υθ⊞υ⊕‽θIΣυ

Try it online! Link is to verbose version of code. Explanation:

Nθ

Input n.

W⁼Lυ№υθ

Repeat while the predefined empty list only contains ns (i.e. its length equals its count of ns)...

⊞υ⊕‽θ

... push a random integer from 1 to n to the list.

IΣυ

Print the total.

\$\endgroup\$
1
\$\begingroup\$

C# (Visual C# Interactive Compiler), 60 bytes

int f(int n,int k=1)=>(k=new Random().Next(n)+1)<n?k:k+f(n);

Saved 4 bytes thanks to @ExpiredData!

Try it online!

\$\endgroup\$
5
  • \$\begingroup\$ 60 bytes \$\endgroup\$ Apr 12, 2019 at 19:48
  • \$\begingroup\$ @ExpiredData Using new Random() gives it the same seed if you call it again, which doesn't make it truly random \$\endgroup\$
    – Gymhgy
    Apr 12, 2019 at 20:11
  • \$\begingroup\$ Nah it's a new seed, run my tio a few times. But even if it was same seed random() isn't true random anyway it's a prng.... \$\endgroup\$ Apr 12, 2019 at 20:40
  • \$\begingroup\$ I've noticed when running on TIO, you either get values less than n or way, way, higher. I think creating a new Random in a tight loop causes the same seed to be used? In any case, this might save a byte? int f(int n,int k=1)=>(k+=new Random().Next(n))<n?k:k+f(n); \$\endgroup\$
    – dana
    Apr 13, 2019 at 5:38
  • 1
    \$\begingroup\$ This is longer, but seems to give better results? var r=new Random();int k;int f(int n)=>(k=r.Next(n)+1)<n?k:k+f(n); \$\endgroup\$
    – dana
    Apr 13, 2019 at 5:42
1
\$\begingroup\$

Perl 6, 25 bytes

{sum {.rand+|0+1}...$_>*}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

F#, 83 bytes

let r=System.Random()
let e d=
 let mutable t=0
 while t%d=0 do t<-t+r.Next(d)+1
 t

Try it online! The first argument in the TIO program is the number of sides on the die, the second is the amount of tests to make. The output is printed to the console and shows each total and the number of times it has been rolled.

The random number generator r is initialised outside of the function. Initialising it within the function, combined with calling this function many times in succession, will cause it to return the same random numbers over and over again (try it for yourself!)

For the life of me I could not figure out how to write this without using mutable, especially since the last roll must be counted (Seq.takeWhile would not include the terminating element). I thought about using Seq.mapFold but it seems like it evaluates the input sequence first, which was a no-go for me.

\$\endgroup\$
5
  • \$\begingroup\$ You're implementing so many extra things. They don't appear to be in the code you're counting, though, so...okay. \$\endgroup\$
    – Stackstuck
    Apr 17, 2019 at 8:18
  • \$\begingroup\$ What do you mean by extra things? \$\endgroup\$ Apr 17, 2019 at 20:04
  • \$\begingroup\$ "shows ...the number of times it has been rolled", not a requirement. \$\endgroup\$
    – Stackstuck
    Apr 17, 2019 at 21:14
  • \$\begingroup\$ ..You need to roll one exploding die. Not 10,000. I'm not sure this is valid...? \$\endgroup\$
    – Stackstuck
    Apr 17, 2019 at 21:22
  • \$\begingroup\$ The submission does only roll one die. The TIO program demonstrates the function being run 10000 times to show that its results are distributed similar to the example distribution. It's common to have a TIO to show the function being used and that its output is valid. And that is not included in the byte count. \$\endgroup\$ Apr 18, 2019 at 21:34
1
\$\begingroup\$

C (gcc), 36 32 bytes

-4 bytes thanks to Ben Voigt

f(n,x){x=rand()%n;x=x?x:n+f(n);}

Try it online!

Here a Test with 100k d4

\$\endgroup\$
9
  • 1
    \$\begingroup\$ Shouldn't srand(time(0)) be included in the byte count? AFAIK rand() will always return the same value if it was never seeded \$\endgroup\$ Apr 12, 2019 at 16:42
  • 1
    \$\begingroup\$ I'm not entirely sure, but I remember the meta consensus being that using an unseed PRNG is acceptable @Tau \$\endgroup\$ Apr 12, 2019 at 16:50
  • 1
    \$\begingroup\$ This is not even close to being C code. Your recursive call has the wrong number of parameters, and you are missing a return. \$\endgroup\$
    – Ben Voigt
    Apr 13, 2019 at 18:46
  • 1
    \$\begingroup\$ Even on gcc it returns 0 all the time (if compiled with -O3) or total garbage (if compiled with -O1). If you have optimization-unstable code that depends on particular compile settings, that should be mentioned. \$\endgroup\$
    – Ben Voigt
    Apr 15, 2019 at 14:32
  • 1
    \$\begingroup\$ @BenVoigt unless mentioned in the post, it is assumed compilers use default options. The default for gcc is -O0, which works perfectly fine for this code. The purpose of codegolfing is to write code in as few bytes as possible, not write functional, production-ready code. If it works, it works. \$\endgroup\$
    – Mayube
    Apr 15, 2019 at 14:55
1
\$\begingroup\$

Ruby, 28 bytes

f=->n{(a=rand n)>0?a:n+f[n]}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Java (JDK), 61 bytes

int f(int n){int r=1;r+=Math.random()*n;return r<n?r:r+f(n);}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Funky, 38 bytes

f=n=>ifn==x=math.random(n)f(n)+x elsex

Try it online!

\$\endgroup\$
1
\$\begingroup\$

C# (.NET Core), 155 153 145 144 bytes

using C=System.Console;class A{static void Main(){int i=int.Parse(C.ReadLine()),j=0;while((j+=new System.Random().Next(i)+1)%i<1){}C.Write(j);}}

Try it online!

This code instantiates a new RNG every time, but that uses time as a seed anyway so it should still satisfy randomness requirements.

I wonder if console input is cheaper in the long run.

\$\endgroup\$
3
  • \$\begingroup\$ Saved 2 bytes by using [0,i)+1 rather than [1,i+1) \$\endgroup\$
    – Stackstuck
    Apr 17, 2019 at 19:35
  • \$\begingroup\$ Saved 8 bytes by forgoing principles about RNG and just making a new one in the loop. \$\endgroup\$
    – Stackstuck
    Apr 17, 2019 at 21:19
  • \$\begingroup\$ Saved one byte w/ <1 instead of ==0 \$\endgroup\$
    – Stackstuck
    Apr 20, 2019 at 7:35
1
\$\begingroup\$

Clojure, 63 bytes

#(loop[n 0](let[r(+ n(rand-int %)1)](if(<(- r n)%)r(recur r))))

A naive solution to the problem.

Expanded:

(defn exploding-die [sides]
  (loop [total 0]
    (let [new-total (+ total (rand-int sides) 1)]
      (if (< (- new-total total) sides) new-total
        (recur new-total)))))
\$\endgroup\$
1
\$\begingroup\$

VTL-2, 54 53 51 bytes

1 A=?
2 C=C+B
3 B='/A*0+%+1
4 #=B=A*2
5 ?=C+B

Line 1 takes input into variable A, this will be our die's sidedness. Line 2 does what it appears to, though it's important to note that in VTL-2, referencing a variable that hasn't been initialized assumes 0, so for the first pass, this is C=0+0. Line 3 divides a random number by our dice sides (' is the system variable for a random number) and then turns this into a roll - % is the remainder of the last division operation; add 1 and put this in B. Line 4 is a little cryptic: B=A is evaluated first. It evaluates to 1 if the two are equal (if our die roll is the same as its number of sides), otherwise it evaluates to 0. This result is multiplied by two (*2), and then the final value here is handed to #=, which is equivalent to a GOTO. If this is given a zero, it ignores it; otherwise we GOTO 2, adding the roll to the total and rolling again. Line 6 prints the total of C+B.

Had an epiphany right after posting this, and golfed off one byte. I was doing 3 B='/A and 4 B=%+1 because % is a system variable with the remainder of the last division operation; you need to do a division operation to get that value. But it occurred to me that I could do the division and then multiply that by zero since I don't need it. But since I've done it, the remainder is now in % and so I can add that to the zero I just made, and add 1 to get the die roll. This is long, but still shorter than two lines - line numbers always take two bytes in VTL-2, plus a space to separate the line number, plus a CR.

Second edit, did my math wildly wrong on byte count both times. Eesh. Third, golfed off two leftover parens.

\$\endgroup\$
3
  • \$\begingroup\$ Is this the language from the Altair systems? Google seems to imply so. But this is really cool! \$\endgroup\$
    – Riker
    May 4, 2019 at 20:55
  • \$\begingroup\$ @Rɪᴋᴇʀ Yup! Stumbled across it recently while looking for some totally unrelated 680 info. I'm running the interpreter in an 8800 emulator. It's a very interesting language, a lot of fun design decisions were made to fit it in ~1KB (VTL-1 was closer to ~700B!). \$\endgroup\$
    – brhfl
    May 4, 2019 at 21:29
  • 1
    \$\begingroup\$ Huh, that's pretty cool. I'm glad you decided to answer my challenge in that language, of all things. \$\endgroup\$
    – Riker
    May 4, 2019 at 23:38
0
\$\begingroup\$

MMIX, 40 bytes (10 instrs)

Assumes TRAP 82,78,71¹ gets a randomly-generated uint64_t, by some method the OS knows about, but we don't have to.

(jxd -T)

00000000: e3010000 f7010000 00524e47 1effff00  ẉ¢¡¡ẋ¢¡¡¡RNGœ””¡
00000010: feff0006 220101ff 22010100 43fffffb  “”¡©"¢¢”"¢¢¡C””»
00000020: 26000100 f8010000                    &¡¢¡ẏ¢¡¡

Disassembled:

expl    SETL $1,0
        PUT  rD,0
0H      TRAP 82,78,71       // loop: tmp = sysrand()
        DIVU $255,$255,0
        GET  $255,rR        // tmp = tmp % n
        ADDU $1,$1,$255
        ADDU $1,$1,$0       // t += tmp + n
        BZ   $255,0B        // if(!tmp) goto loop
        SUBU $0,$1,$0
        POP  1,0            // return t - n

The main hack here is to treat every roll as being in the range \$[n,2n-1]\$, then subtracting \$n\$ once after everything is over.

¹ Yes, those numbers were carefully picked.

\$\endgroup\$
0
\$\begingroup\$

Ly, 16 bytes

00ns[p+1l?l=]p+u

Try it online!

This one is pretty much a direct translations of the rules of the challenge.

00n              # Push "0", "0" and the input number on the stack
   s             # Save the input number in the backup cell
    [       ]    # Loop, quits when the random number doesn't match the die size
     p+          # Delete loop iterator check, add the remain two entries
                 # (The stack holds the accumulated value and the last roll)
       1l?       # Generate a random number between 1 and die size, inclusive
          l=     # Compare the random roll to the saved die size
             p+u # Add the last role and print as a number
\$\endgroup\$
0
\$\begingroup\$

Thunno 2 s, 6 bytes

($=;$ɼ

Attempt This Online!

Explanation

($=;$ɼ  # Implicit input
(       # (while)  
 $=     #   equals input
   ;    # (do)
    $ɼ  #   random item from [1..input]
        # Implicit output of summed stack
\$\endgroup\$
0
\$\begingroup\$

Perl 5 -Minteger -p, 25 bytes

1until($\+=1+rand$_)%$_}{

Try it online!

\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.