Rename Test
to check
, etc., restructure calculation code.
This commit is contained in:
parent
c26e4bab9f
commit
fc88ce6c52
3 changed files with 52 additions and 39 deletions
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue