export function assignSubChecksToDice( dice: number[], checkTargetNumber: number, { maximumCoupResult = 1, }: { maximumCoupResult?: number; } = {}, ): { result: number; checkTargetNumber: number }[] { const requiredNumberOfDice = Math.ceil(checkTargetNumber / 20); if (dice.length !== requiredNumberOfDice || requiredNumberOfDice < 1) { throw new Error(game.i18n.localize("DS4.ErrorInvalidNumberOfDice")); // TODO: add i18n } const checkTargetNumberForLastSubCheck = checkTargetNumber - 20 * (requiredNumberOfDice - 1); const indexOfSmallestNonCoup = findIndexOfSmallestNonCoup(dice, maximumCoupResult); const indexOfFirstCoup = dice.findIndex((die) => die <= maximumCoupResult); const indexForLastSubCheck = shouldUseCoupForLastSubCheck( indexOfSmallestNonCoup, indexOfFirstCoup, dice, checkTargetNumberForLastSubCheck, ) ? indexOfFirstCoup : indexOfSmallestNonCoup; return dice.map((die, index) => ({ result: die, checkTargetNumber: index === indexForLastSubCheck ? checkTargetNumberForLastSubCheck : 20, })); } function findIndexOfSmallestNonCoup(dice: number[], maximumCoupResult: number): number { return dice .map((die, index) => [die, index]) .filter((indexedDie) => indexedDie[0] > maximumCoupResult) .reduce( (smallestIndexedDie, indexedDie) => indexedDie[0] < smallestIndexedDie[0] ? indexedDie : smallestIndexedDie, [Infinity, -1], )[1]; } function shouldUseCoupForLastSubCheck( indexOfSmallestNonCoup: number, indexOfFirstCoup: number, dice: number[], checkTargetNumberForLastSubCheck: number, ) { return ( indexOfFirstCoup !== -1 && (indexOfSmallestNonCoup === -1 || (dice[indexOfSmallestNonCoup] > checkTargetNumberForLastSubCheck && dice[indexOfSmallestNonCoup] + checkTargetNumberForLastSubCheck > 20)) ); }