Search code examples
prologswi-prolog

Sum of random digits in Prolog


The scenario is to mimic rolling 3 six-sided die in Prolog while trying to obey the recursive nature of prolog. This is easily done with the Fibonacci series

n_factorial(N, F) :- 
  N #> 0,
  F #= N*F1,
  N1 #= N-1,
  n_factorial(N1, F1).

I'm having difficulty translating this to the dice paradigm, where we add a random number to the sum.

# N = number of dice, S = number of sides, R = result
roll_dice(N, S, R) :-  
  N1 #> 0,
  R = random_between(1, S, R1),
  N1 #= N-1,
  roll_dice(N1, S, R1).

throws an error but neglects the sum anyway. I would normally use += in other languages.


Solution

  • A few things not quite right there:

    • R = random_between(1, S, R1) stores that structure in R, it does not generate random numbers.

    • R1 is used for "the random number you get for rolling one die" and "the sum of all the random numbers from rolling all the remaining dice" and it is unlikely those will be the same value, and when they isn't the same, that will fail.

    • There is no code to do the summing. (The equivalent of the Factorial video using F #= N * F1)

    • No recursive base case (the first line of the factorial from the video is not shown in your question, it is n_factorial(0, 1).). When N dice remaining gets down to 0, there is nothing to handle that case.

    Here is one implementation:

    :- use_module(library(clpfd)).
    
    roll_dice(0, _, 0).
    
    roll_dice(NDice, Sides, Sum) :-  
      NDice #> 0,
    
      random_between(1, Sides, Roll),
      Sum #= Roll + RunningTotal,
    
      NDiceLeft #= NDice - 1,
      roll_dice(NDiceLeft, Sides, RunningTotal).
    

    while trying to obey the recursive nature of Prolog.

    I guess that's important to understand; this can be done with less bolierplate and fewer temporary variables to name, e.g.:

    roll_dice(NDice, Sides, Sum) :-  
        length(Rolls, NDice),
        maplist(random(1, Sides), Rolls),
        sum_list(Rolls, Sum).
    

    which makes a list Rolls the right size to hold all the rolls, fills them in using maplist to apply random on each of them, and sums that list for the answer.