Search code examples
javascriptalgorithmdice

Generate a specific number given X inputs of dice rolls


I'm trying to come up with a solution where I need to roll a number of dice (all of the same size) and come to a specified number. Provided I have all the validation in place to make sure the numbers are valid and could theoretically arrive at the desired result, does anyone have a good algorithm for solving this? Note it should appear random, not just a straight divide.

Some examples

roll 3 d6 and get 14 -> so it could output 5,3,6 or 6,6,2

roll 4 d20 and get 66 -> so it could output 16,14,19,17

I need a generic function that can accept a dice of any size, any amount to be rolled and the desired result.

My initial attempt is below, though this doesn't produce the desired output (you can ignore the mod for now, this was to also allow modifiers). This example is also missing the validation that the desired output is achievable,but that's not part of the question.

let desired = 19
let mod = 0
let dm = desired - mod
let n = 5;// number of dice
let d = 6 // dice sides
let nums = []

for(i =0; i< n; i++) {
    nums.push(Math.round(Math.random() * Math.round(d)) + 1)
}

let sum = nums.reduce((acc,val) => acc + val)


nums = nums.map(a => Math.round((a/sum) * dm))

let diff = dm - (nums.reduce((acc,val) => acc + val))
function recursive(diff) {
    let ran = nums[Math.random() * Math.round(nums.length -1)]
    if(nums[ran] + diff > d || nums[ran] + diff < 1) {
        recursive(diff)
    } else {
        nums[ran] += diff
    }
}
while(diff != 0) {
    recursive(diff)
    diff += diff < 0 ? 1 : -1;
}

alert(nums)

Solution

  • Soluton in ruby:

    def foo(count, dim, desired, results = [])
      return results if count == 0
      raise ArgumentError if count > desired
      raise ArgumentError if count * dim < desired
    
      max_roll = (dim <= desired - count) ? dim : desired - count + 1
      min_roll = [(desired - (count-1) * dim), 1].max
      roll = (rand(min_roll..max_roll))
      results << roll
    
      foo(count - 1, dim, desired - roll, results)
    
      results
    end
    
    puts foo(3, 6, 11).inspect
    puts foo(2, 6, 11).inspect
    puts foo(4, 4, 11).inspect
    

    Results:

    [3, 4, 4]
    [5, 6]
    [2, 3, 4, 2]
    

    So basically it is recursive function. For each step:

    • roll a dice (within allowed range min_roll..max_roll)
    • call same function but reduce count by already consumed number by dice and extend results array by value of roll

    Note one thing: with this behaviour you may have larger numbers in the beginning of result. To avoid this just shuffle result of function in the end of it