3

I am supposed to do a score calculator in Java for a dice game that rolls 6 dice (with 6 faces). The score is supposed to be calculated according to a list of options that the user has available. Options are 4,5,...,12. For choice "4", all combinations of dice whose values amount to 4 give points.

Each dice can be selected only once during scoring. It doesn't matter which dice are grouped together as long as their sum is equal to the choice value and the total value of the points is maximised. So for example the roll {1 2 4 2 3 3} gives 12 points if user chooses option "4" ([1 3]+[4]+[2 2]). 11 points ([4 3 3 1]) if user chooses option "11". 12 points if user chooses option "6".

I've tried several ways of calculating this but none gives me correct results in 100% of the cases and I've now been stuck with this for over a day.

My question is what would be a good solution/algorithm int calc(List<Integer> input, int sum) such that e.g.

calc({6,6,6,6,6,5}, 12)=24
calc({6,6,6,6,6,5}, 11)=11
calc({6,6,6,6,6,5}, 3)=0
calc({6,6,6,6,6,5}, 6)=30

Help much appreciated.

J.Dow
  • 103
  • 9
  • {1 2 4 2 3 3} gives 12 points if user chooses option "4". Why 12 points? Can you expand a little on how the score is computed? There are 4 combinations that give sum of 4: [(1,3),(1,3),(2,2),(4)]. So shouldn't the score be 4? – Hari Menon Jul 04 '20 at 19:26
  • Each dice can be selected only once for scoring. I amended the text. – J.Dow Jul 04 '20 at 19:38
  • What if you have {4 2 4 2 2 6} and sum is 6? Is the answer 12 (2+2+2, 6) or 18 (4+2, 4+2, 6)? – Yonlif Jul 04 '20 at 20:24
  • {4 2 4 2 2 6} you would have [4 2] [4 2] [6] yielding 18 if "6" is chosen correct? [2 2 2] [6] is also an option but results in a smaller score of 12. Thus 18 should be the answer here (?) – wcochran Jul 04 '20 at 20:27
  • wcochran correct. It doesn't matter which dice are chosen as long as their sum adds up to the choice option value and maximizes the number of points. – J.Dow Jul 04 '20 at 20:45
  • One good solution/algorithm/idea can be found at https://stackoverflow.com/questions/54615250/find-all-subsets-that-sum-to-a-particular-value-and-then-pick-the-most-valuable – LeadingEdger Jul 04 '20 at 20:52

2 Answers2

1

This is a combinatoric search problem. Here is the recursive algorithm which examines the entire search space. dice is a sequence of integers (each a number between 1 and 6), target is the number 4 .. 12 chosen by the player, and best is the best sum of previously totaled die (initially 0):

score(target, dice, best=0) {
    hi = best;
    for all subsets S of dice 
       if sum S = target
           val = score(target, dice - S, best + target)
           if val > hi
              hi = val;
    return hi;
}

And here is my Java implementation (I am a little rusty with Java):

import java.util.Vector;

public class DiceGame {
    public int targetSum;

    public DiceGame(int t) {targetSum = t;}

    public int sumOfDice(Vector<Integer> dice) {
        int s = 0;
        for (int d : dice)
            s += d;
        return s;
    }

    public int score(Vector<Integer> dice) {
        return score(dice, 0);
    }

    public int score(Vector<Integer> dice, int bestPrev) {
        int hi = bestPrev;
        for (int n = 1; n < (1 << dice.size()); n++) {
            Vector<Integer> subset = new Vector<Integer>();
            Vector<Integer> remaining = new Vector<Integer>();
            for (int i = 0; i < dice.size(); i++) {
                if ((n & (1 << i)) != 0)
                    subset.add(dice.get(i));
                else
                    remaining.add(dice.get(i));
            }
            if (sumOfDice(subset) == targetSum) {
                int s = score(remaining, bestPrev + targetSum);
                if (s > hi)
                    hi = s;
            }
        }
        return hi;
    }

    public static void main(String[] args) {
        Vector<Integer> dice = new Vector<Integer>();
        // 4 2 4 2 2 6
        dice.add(4);
        dice.add(2);
        dice.add(4);
        dice.add(2);
        dice.add(2);
        dice.add(6);
        DiceGame diceGame = new DiceGame(6);
        int s = diceGame.score(dice);
        System.out.println(s);
    }
}

And here is my thorough test :)

$ java DiceGame
18

Note: I used score/target where you used calc/sum and I used Vector where you used List .. I'll let you write the proper adapter,

wcochran
  • 10,089
  • 6
  • 61
  • 69
0

One state we could conceive of is how many of each "option" (sums from 1 to 12) do we have, as we iterate over the input. Here is a recursive approach that tries to add the current die to different sums to see which wins out.

The state (and return value) is an array, where the first element is the maximum achievable sum, and the rest are counts for the sums equaling their index in the array (only the ones totaling to the max are relevant).

JavaScript code (given the constraints, memoisation does not seem necessary):

function f(dice){
  function g(i, state){
    if (i == dice.length)
      return state;
      
    const die = dice[i];
      
    // Increment number of die parts
    const _state = state.slice();
    _state[die] += 1;
    _state[0] = Math.max(_state[0], _state[die] * die);
    let best = g(i + 1, _state);
    
    // Try adding die to other sums
    for (let j=1; j<=12-die; j++){
      if (state[j]){
        const _state = state.slice();
        const sum = j + die;
        _state[j] -= 1;
        _state[sum] += 1;
        _state[0] = Math.max(
          _state[0], _state[sum] * sum);
        const next = g(i + 1, _state);
        if (next[0] > best[0])
          best = next;
      }
    }
    
    return best;
  }
  
  return g(0, new Array(13).fill(0));
}

var games = [
  [1, 2, 4, 2, 3, 3],
  [4, 2, 4, 2, 2, 6],
  [6, 6, 6, 6, 6, 5]
];

for (let dice of games){
  console.log(JSON.stringify(dice));
  console.log(JSON.stringify(f(dice)));
  console.log('');
}

To find the max for a specific target, we can just tweak which max is compared:

function f(dice, t){
  function g(i, state){
    if (i == dice.length)
      return state;
      
    const die = dice[i];
      
    // Increment number of die parts
    const _state = state.slice();
    _state[die] += 1;
    _state[0] = Math.max(_state[0], _state[t] * t);
    let best = g(i + 1, _state);
    
    // Try adding die to other sums
    for (let j=1; j<=12-die; j++){
      if (state[j]){
        const _state = state.slice();
        const sum = j + die;
        _state[j] -= 1;
        _state[sum] += 1;
        _state[0] = Math.max(
          _state[0], _state[t] * t);
        const next = g(i + 1, _state);
        if (next[0] > best[0])
          best = next;
      }
    }
    
    return best;
  }
  
  return g(0, new Array(13).fill(0));
}

var games = [
  [[1, 2, 4, 2, 3, 3], 4],
  [[4, 2, 4, 2, 2, 6], 6],
  [[6, 6, 6, 6, 6, 5], 3]
];

for (let [dice, t] of games){
  console.log(JSON.stringify(dice, t) + " " + t);
  console.log(JSON.stringify(f(dice, t)));
  console.log('');
}
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61