Rename Test to check, etc., restructure calculation code.

This commit is contained in:
Oliver Rümpelein 2021-01-04 19:38:26 +01:00
parent c26e4bab9f
commit fc88ce6c52
3 changed files with 52 additions and 39 deletions

View file

@ -93,7 +93,7 @@ describe("DS4 Rolls with one die and slaying dice, followup throw.", () => {
);
});
it("Should do a regular success on `20` with a test value of 20", () => {
it("Should do a regular success on `20` with a CTN of 20", () => {
const rollProvider = mockSingleThrow(20);
expect(rollCheckSingleDie(20, { useSlayingDice: true, slayingDiceRepetition: true }, rollProvider)).toEqual(
@ -209,7 +209,7 @@ describe("DS4 Rolls with multiple dice and no modifiers.", () => {
);
});
it("Should maximize on 'lowest dice higher than last test and crit success thrown'-Edge case, no change required.", () => {
it("Should maximize on 'lowest dice higher than last CTN and crit success thrown'-Edge case, no change required.", () => {
const rollProvider = mockMultipleThrows([15, 1, 8]);
expect(rollCheckMultipleDice(46, {}, rollProvider)).toEqual(
@ -217,7 +217,7 @@ describe("DS4 Rolls with multiple dice and no modifiers.", () => {
);
});
it("Should maximize on 2-dice 'lowest dice higher than last test and crit success thrown'-Edge case, no change required.", () => {
it("Should maximize on 2-dice 'lowest dice higher than last CTN and crit success thrown'-Edge case, no change required.", () => {
const rollProvider = mockMultipleThrows([1, 8]);
expect(rollCheckMultipleDice(24, {}, rollProvider)).toEqual(
@ -225,7 +225,7 @@ describe("DS4 Rolls with multiple dice and no modifiers.", () => {
);
});
it("Should maximize on 2-dice 'lowest dice higher than last test and crit success thrown'-Edge case, change required.", () => {
it("Should maximize on 2-dice 'lowest dice higher than last CTN and crit success thrown'-Edge case, change required.", () => {
const rollProvider = mockMultipleThrows([1, 19]);
expect(rollCheckMultipleDice(38, {}, rollProvider)).toEqual(

View file

@ -1,18 +1,19 @@
import { DefaultRollOptions, RollOptions, RollResult, RollResultStatus } from "./roll-data";
import { DS4RollProvider, RollProvider } from "./roll-provider";
import { isDiceSwapNecessary, isSlayingDiceRepetition } from "./roll-utils";
import { calculateRollResult, isDiceSwapNecessary, isSlayingDiceRepetition } from "./roll-utils";
export function ds4test(testValue: number, rollOptions: Partial<RollOptions> = {}): RollResult {
const finalRollValue = testValue;
if (finalRollValue <= 20) {
return rollCheckSingleDie(finalRollValue, rollOptions);
export function ds4roll(checkTargetValue: number, rollOptions: Partial<RollOptions> = {}): RollResult {
// TODO: Add CTN modifiers from options.
const finalTargetValue = checkTargetValue;
if (finalTargetValue <= 20) {
return rollCheckSingleDie(finalTargetValue, rollOptions);
} else {
return rollCheckMultipleDice(finalRollValue, rollOptions);
return rollCheckMultipleDice(finalTargetValue, rollOptions);
}
}
export function rollCheckSingleDie(
testValue: number,
checkTargetValue: number,
rollOptions: Partial<RollOptions>,
provider: RollProvider = new DS4RollProvider(),
): RollResult {
@ -21,11 +22,11 @@ export function rollCheckSingleDie(
const dice = [roll];
if (roll <= usedOptions.maxCritSucc) {
return new RollResult(testValue, RollResultStatus.CRITICAL_SUCCESS, dice);
return new RollResult(checkTargetValue, RollResultStatus.CRITICAL_SUCCESS, dice);
} else if (roll >= usedOptions.minCritFail && !isSlayingDiceRepetition(usedOptions)) {
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice);
} else {
if (roll <= testValue) {
if (roll <= checkTargetValue) {
return new RollResult(roll, RollResultStatus.SUCCESS, dice);
} else {
return new RollResult(0, RollResultStatus.FAILURE, dice);
@ -51,13 +52,13 @@ function separateCriticalHits(dice: Array<number>, usedOptions: RollOptions): [A
}
export function rollCheckMultipleDice(
testValue: number,
targetValue: number,
rollOptions: Partial<RollOptions>,
provider: RollProvider = new DS4RollProvider(),
): RollResult {
const usedOptions = new DefaultRollOptions().mergeWith(rollOptions);
const finalCheck = testValue % 20;
const numberOfDice = Math.ceil(testValue / 20);
const remainderTargetValue = targetValue % 20;
const numberOfDice = Math.ceil(targetValue / 20);
const dice = provider.getNextRolls(numberOfDice);
@ -71,7 +72,7 @@ export function rollCheckMultipleDice(
const [critSuccesses, otherRolls] = separateCriticalHits(dice, usedOptions);
const swapLastWithCrit: boolean = isDiceSwapNecessary(critSuccesses, otherRolls, finalCheck);
const swapLastWithCrit: boolean = isDiceSwapNecessary(critSuccesses, otherRolls, remainderTargetValue);
let sortedRollResults: Array<number>;
@ -83,25 +84,7 @@ export function rollCheckMultipleDice(
sortedRollResults = critSuccesses.concat(otherRolls);
}
const evaluationResult = sortedRollResults
.map((value, index) => {
if (index == numberOfDice - 1) {
if (value <= usedOptions.maxCritSucc) {
return finalCheck;
} else if (value <= finalCheck) {
return value;
} else {
return 0;
}
} else {
if (value <= usedOptions.maxCritSucc) {
return 20;
} else {
return value;
}
}
})
.reduce((a, b) => a + b);
const evaluationResult = calculateRollResult(sortedRollResults, remainderTargetValue, usedOptions);
if (usedOptions.useSlayingDice && firstResult <= usedOptions.maxCritSucc) {
return new RollResult(evaluationResult, RollResultStatus.CRITICAL_SUCCESS, sortedRollResults);

View file

@ -3,20 +3,50 @@ import { RollOptions } from "./roll-data";
export function isDiceSwapNecessary(
critSuccesses: Array<number>,
otherRolls: Array<number>,
lastTestValue: number,
remainingTargetValue: number,
): boolean {
if (critSuccesses.length == 0 || otherRolls.length == 0) {
return false;
}
const amountOfOtherRolls = otherRolls.length;
const lastDice = otherRolls[amountOfOtherRolls - 1];
if (lastDice <= lastTestValue) {
if (lastDice <= remainingTargetValue) {
return false;
}
return lastDice + lastTestValue > 20;
return lastDice + remainingTargetValue > 20;
}
export function isSlayingDiceRepetition(opts: RollOptions): boolean {
return opts.useSlayingDice && opts.slayingDiceRepetition;
}
export function calculateRollResult(
assignedRollResults: Array<number>,
remainderTargetValue: number,
rollOptions: RollOptions,
): number {
const numberOfDice = assignedRollResults.length;
const maxResultPerDie: Array<number> = Array(numberOfDice).fill(20);
maxResultPerDie[numberOfDice - 1] = remainderTargetValue;
const rollsAndMaxValues = zip(assignedRollResults, maxResultPerDie);
return rollsAndMaxValues
.map(([v, m]) => {
return v <= rollOptions.maxCritSucc ? [m, m] : [v, m];
})
.filter(([v, m]) => v <= m)
.map(([v]) => v)
.reduce((a, b) => a + b);
}
// TODO: Move to generic utils method?
function zip<T, U>(a1: Array<T>, a2: Array<U>): Array<[T, U]> {
if (a1.length <= a2.length) {
return a1.map((e1, i) => [e1, a2[i]]);
} else {
return a2.map((e2, i) => [a1[i], e2]);
}
}