From 7800f0af8f8bc056dfe57e4d0bad4545cac173f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 17:50:01 +0100 Subject: [PATCH 01/17] Add recommendet plugins, fix lint stuff. --- package-lock.json | 2 +- src/module/rolls/ds4rolls.ts | 96 ++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/module/rolls/ds4rolls.ts diff --git a/package-lock.json b/package-lock.json index 58ddfec0..d1ee44ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2678,7 +2678,7 @@ } }, "foundry-pc-types": { - "version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#4ae5653e74e79bb6b4bd23b094dee066a0c7723a", + "version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#4e20e5c9cb1b3cd2e44555d7acfa89a3cf63f6ce", "from": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes", "dev": true, "requires": { diff --git a/src/module/rolls/ds4rolls.ts b/src/module/rolls/ds4rolls.ts new file mode 100644 index 00000000..50c6fed6 --- /dev/null +++ b/src/module/rolls/ds4rolls.ts @@ -0,0 +1,96 @@ +export function roll(testValue: number, rollOptions: RollOptions = new RollOptions()): Promise { + const finalRollValue = testValue; + if (finalRollValue <= 20) { + return rollCheckSingleDie(finalRollValue, rollOptions); + } else { + return rollCheckMultipleDice(finalRollValue, rollOptions); + } +} + +async function rollCheckSingleDie(testValue: number, rollOptions: RollOptions): Promise { + const roll = new Roll("1d20"); + roll.roll(); + + const pool = new DicePool({ rolls: [roll] }); + + if (roll.total <= rollOptions.maxCritSucc) { + return createRollResultPromise(testValue, RollResultStatus.CRITICAL_SUCCESS, pool); + } else if (roll.total >= rollOptions.minCritFail) { + return createRollResultPromise(0, RollResultStatus.CRITICAL_FAILURE, pool); + } else { + if (roll.total <= testValue) { + return createRollResultPromise(roll.total, RollResultStatus.SUCCESS, pool); + } else { + return createRollResultPromise(0, RollResultStatus.FAILURE, pool); + } + } +} + +async function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): Promise { + const finalCheck = testValue % 20; + const numberOfDice = Math.ceil(testValue / 20); + const rolls = new Array(); + for (let i = 0; i < numberOfDice; i++) { + const roll = new Roll("1d20"); + roll.roll(); + rolls.concat(roll); + } + const pool = new DicePool({ rolls: rolls }); + + const firstResult = rolls[1].total; + + // TODO: Special stuff (Gnomes!) + if (firstResult == 20) { + createRollResultPromise(0, RollResultStatus.CRITICAL_FAILURE, pool); + } + + const [otherRolls, critSuccesses] = pool.rolls + .partition((r) => r.total <= rollOptions.maxCritSucc) + .map((a) => a.sort((r1, r2) => r2.total - r1.total)); + + const sortedRolls: DicePool = new DicePool({ rolls: critSuccesses.concat(otherRolls) }); + + const evaluationResult = sortedRolls.rolls + .map((r) => r.total) + .map((value, index) => { + if (index == numberOfDice - 1) { + if (value == 1) { + return finalCheck; + } else { + return value >= finalCheck ? value : 0; + } + } else { + if (value <= rollOptions.maxCritSucc) { + return 20; + } else { + return value; + } + } + }) + .reduce((a, b) => a + b); + + return createRollResultPromise(evaluationResult, RollResultStatus.SUCCESS, sortedRolls); +} + +function createRollResultPromise( + totalValue: number, + rollResult: RollResultStatus, + dicePool: DicePool, +): Promise { + return new Promise((resolve) => resolve(new RollResult(totalValue, RollResultStatus.SUCCESS, dicePool))); +} + +export class RollOptions { + constructor(public minCritFail: number = 20, public maxCritSucc: number = 1) {} +} + +export class RollResult { + constructor(public value: number, public status: RollResultStatus, public dice: DicePool) {} +} + +enum RollResultStatus { + FAILURE, + SUCCESS, + CRITICAL_FAILURE, + CRITICAL_SUCCESS, +} From 7089178a0d569708210745853d0cec71960e66a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 17:50:36 +0100 Subject: [PATCH 02/17] Add recom. plugins. --- .vscode/extensions.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..3b460754 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "gruntfuggly.todo-tree" + ] +} From e0b65ca0616a6c0a609909bb2addc2a8a16e60ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 21:27:41 +0100 Subject: [PATCH 03/17] Make tests and implementation interact. --- .vscode/extensions.json | 3 +- package-lock.json | 84 +++++++++++++++++++++++++++++++++++ package.json | 6 ++- spec/support/ds4rolls.spec.ts | 19 ++++++++ spec/support/jasmine.json | 7 +++ src/module/rolls/ds4rolls.ts | 76 ++++++++++++++++++------------- tsconfig.json | 2 +- 7 files changed, 163 insertions(+), 34 deletions(-) create mode 100644 spec/support/ds4rolls.spec.ts create mode 100644 spec/support/jasmine.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 3b460754..fab51eba 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", - "gruntfuggly.todo-tree" + "gruntfuggly.todo-tree", + "eg2.vscode-npm-script" ] } diff --git a/package-lock.json b/package-lock.json index d1ee44ef..8a60e388 100644 --- a/package-lock.json +++ b/package-lock.json @@ -110,6 +110,12 @@ "fastq": "^1.6.0" } }, + "@types/jasmine": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.2.tgz", + "integrity": "sha512-AzfesNFLvOs6Q1mHzIsVJXSeUnqVh4ZHG8ngygKJfbkcSLwzrBVm/LKa+mR8KrOfnWtUL47112gde1MC0IXqpQ==", + "dev": true + }, "@types/jquery": { "version": "3.5.5", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.5.tgz", @@ -610,6 +616,12 @@ "readable-stream": "^2.0.6" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1571,6 +1583,12 @@ } } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", @@ -1733,6 +1751,12 @@ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3748,6 +3772,22 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "jasmine": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.6.3.tgz", + "integrity": "sha512-Th91zHsbsALWjDUIiU5d/W5zaYQsZFMPTdeNmi8GivZPmAaUAK8MblSG3yQI4VMGC/abF2us7ex60NH1AAIMTA==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "jasmine-core": "~3.6.0" + } + }, + "jasmine-core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", + "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", + "dev": true + }, "js-base64": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", @@ -4326,6 +4366,12 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -5993,6 +6039,24 @@ "urix": "^0.1.0" } }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -6514,6 +6578,20 @@ "glob": "^7.1.2" } }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -7102,6 +7180,12 @@ "object.assign": "^4.1.0" } }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, "zip-stream": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.0.4.tgz", diff --git a/package.json b/package.json index 7099f55d..2125458d 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,13 @@ "clean": "gulp clean && gulp link --clean", "update": "npm install --save-dev git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes", "lint": "eslint 'src/**/*.ts' --cache", - "lint:fix": "eslint 'src/**/*.ts' --cache --fix" + "lint:fix": "eslint 'src/**/*.ts' --cache --fix", + "test": "ts-node ./node_modules/jasmine/bin/jasmine" }, "author": "", "license": "", "devDependencies": { + "@types/jasmine": "^3.6.2", "@typescript-eslint/eslint-plugin": "^4.11.0", "@typescript-eslint/parser": "^4.11.0", "archiver": "^5.1.0", @@ -31,10 +33,12 @@ "gulp-sass": "^4.1.0", "gulp-typescript": "^6.0.0-alpha.1", "husky": "^4.3.6", + "jasmine": "^3.6.3", "json-stringify-pretty-compact": "^2.0.0", "lint-staged": "^10.5.3", "prettier": "^2.2.1", "sass": "^1.30.0", + "ts-node": "^9.1.1", "typescript": "^4.1.3", "yargs": "^16.2.0" }, diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts new file mode 100644 index 00000000..c467c296 --- /dev/null +++ b/spec/support/ds4rolls.spec.ts @@ -0,0 +1,19 @@ +import { + rollCheckSingleDie, + RollOptions, + RollProvider, + RollResult, + RollResultStatus, +} from "../../src/module/rolls/ds4rolls"; + +describe("DS4 Rolls", () => { + it("Should do a proper single success role", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(4); + + expect(rollCheckSingleDie(12, new RollOptions(), rollProvider)).toEqual( + new RollResult(4, RollResultStatus.SUCCESS, [4]), + ); + }); +}); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 00000000..6d97768a --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,7 @@ +{ + "spec_dir": "spec", + "spec_files": ["**/*[sS]pec.ts"], + "helpers": ["helpers/**/*.ts"], + "stopSpecOnExpectationFailure": false, + "random": true +} diff --git a/src/module/rolls/ds4rolls.ts b/src/module/rolls/ds4rolls.ts index 50c6fed6..74c76a43 100644 --- a/src/module/rolls/ds4rolls.ts +++ b/src/module/rolls/ds4rolls.ts @@ -1,4 +1,4 @@ -export function roll(testValue: number, rollOptions: RollOptions = new RollOptions()): Promise { +export function ds4test(testValue: number, rollOptions: RollOptions = new RollOptions()): RollResult { const finalRollValue = testValue; if (finalRollValue <= 20) { return rollCheckSingleDie(finalRollValue, rollOptions); @@ -7,26 +7,45 @@ export function roll(testValue: number, rollOptions: RollOptions = new RollOptio } } -async function rollCheckSingleDie(testValue: number, rollOptions: RollOptions): Promise { - const roll = new Roll("1d20"); - roll.roll(); +export class DS4RollProvider { + getNextRoll(): number { + return new Roll("1d20").roll().total; + } - const pool = new DicePool({ rolls: [roll] }); + getNextRolls(amount: number): Array { + return Array(amount) + .fill(0) + .map(() => this.getNextRoll()); + } +} - if (roll.total <= rollOptions.maxCritSucc) { - return createRollResultPromise(testValue, RollResultStatus.CRITICAL_SUCCESS, pool); - } else if (roll.total >= rollOptions.minCritFail) { - return createRollResultPromise(0, RollResultStatus.CRITICAL_FAILURE, pool); +export interface RollProvider { + getNextRoll(): number; + getNextRolls(number): Array; +} + +export function rollCheckSingleDie( + testValue: number, + rollOptions: RollOptions, + provider: RollProvider = new DS4RollProvider(), +): RollResult { + const roll = provider.getNextRoll(); + const dice = [roll]; + + if (roll <= rollOptions.maxCritSucc) { + return createRollResult(testValue, RollResultStatus.CRITICAL_SUCCESS, dice); + } else if (roll >= rollOptions.minCritFail) { + return createRollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); } else { - if (roll.total <= testValue) { - return createRollResultPromise(roll.total, RollResultStatus.SUCCESS, pool); + if (roll <= testValue) { + return createRollResult(roll, RollResultStatus.SUCCESS, dice); } else { - return createRollResultPromise(0, RollResultStatus.FAILURE, pool); + return createRollResult(0, RollResultStatus.FAILURE, dice); } } } -async function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): Promise { +function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): RollResult { const finalCheck = testValue % 20; const numberOfDice = Math.ceil(testValue / 20); const rolls = new Array(); @@ -35,23 +54,22 @@ async function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions roll.roll(); rolls.concat(roll); } - const pool = new DicePool({ rolls: rolls }); + const dice = rolls.map((r) => r.total); - const firstResult = rolls[1].total; + const firstResult = dice[1]; // TODO: Special stuff (Gnomes!) if (firstResult == 20) { - createRollResultPromise(0, RollResultStatus.CRITICAL_FAILURE, pool); + createRollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); } - const [otherRolls, critSuccesses] = pool.rolls - .partition((r) => r.total <= rollOptions.maxCritSucc) - .map((a) => a.sort((r1, r2) => r2.total - r1.total)); + const [otherRolls, critSuccesses] = dice + .partition((r) => r <= rollOptions.maxCritSucc) + .map((a) => a.sort((r1, r2) => r2 - r1)); - const sortedRolls: DicePool = new DicePool({ rolls: critSuccesses.concat(otherRolls) }); + const sortedRollResults: Array = critSuccesses.concat(otherRolls); - const evaluationResult = sortedRolls.rolls - .map((r) => r.total) + const evaluationResult = sortedRollResults .map((value, index) => { if (index == numberOfDice - 1) { if (value == 1) { @@ -69,15 +87,11 @@ async function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions }) .reduce((a, b) => a + b); - return createRollResultPromise(evaluationResult, RollResultStatus.SUCCESS, sortedRolls); + return createRollResult(evaluationResult, RollResultStatus.SUCCESS, sortedRollResults); } -function createRollResultPromise( - totalValue: number, - rollResult: RollResultStatus, - dicePool: DicePool, -): Promise { - return new Promise((resolve) => resolve(new RollResult(totalValue, RollResultStatus.SUCCESS, dicePool))); +function createRollResult(totalValue: number, rollResult: RollResultStatus, dice: Array): RollResult { + return new RollResult(totalValue, RollResultStatus.SUCCESS, dice); } export class RollOptions { @@ -85,10 +99,10 @@ export class RollOptions { } export class RollResult { - constructor(public value: number, public status: RollResultStatus, public dice: DicePool) {} + constructor(public value: number, public status: RollResultStatus, public dice: Array) {} } -enum RollResultStatus { +export enum RollResultStatus { FAILURE, SUCCESS, CRITICAL_FAILURE, diff --git a/tsconfig.json b/tsconfig.json index 9293923b..64707c38 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,6 @@ "compilerOptions": { "target": "ES2017", "lib": ["DOM", "ES6", "ES2017"], - "types": ["foundry-pc-types"] + "types": ["foundry-pc-types", "jasmine"] } } From 0522f55f9d029d942be6c1932ad12f6160711e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 21:30:31 +0100 Subject: [PATCH 04/17] Add tests to CI runner. --- .gitlab-ci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 86353bc6..976b8d38 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,12 +24,14 @@ lint: <<: *global_cache policy: pull-push -# Test: -# stage: Test -# script: -# - npm test -# cache: -# <<: *global_cache +test: + stage: test + before_script: + - npm ci --cache .npm --prefer-offline + script: + - npm test + cache: + <<: *global_cache build: stage: build From 1c0ca4ff47f24941027530a7caaf2fe16df57cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 21:31:35 +0100 Subject: [PATCH 05/17] Fix YAML error. --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 976b8d38..c00ab63a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,8 +30,8 @@ test: - npm ci --cache .npm --prefer-offline script: - npm test - cache: - <<: *global_cache + cache: + <<: *global_cache build: stage: build From 8d274ed83033c7f78e52d2d18bb4473e630cdb7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 21:50:12 +0100 Subject: [PATCH 06/17] Ruhu, Cleanups! --- spec/support/ds4rolls.spec.ts | 9 +---- .../rolls/{ds4rolls.ts => roll-executor.ts} | 38 +++++-------------- src/module/rolls/roll-provider.ts | 16 ++++++++ 3 files changed, 27 insertions(+), 36 deletions(-) rename src/module/rolls/{ds4rolls.ts => roll-executor.ts} (68%) create mode 100644 src/module/rolls/roll-provider.ts diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts index c467c296..b32ca97d 100644 --- a/spec/support/ds4rolls.spec.ts +++ b/spec/support/ds4rolls.spec.ts @@ -1,10 +1,5 @@ -import { - rollCheckSingleDie, - RollOptions, - RollProvider, - RollResult, - RollResultStatus, -} from "../../src/module/rolls/ds4rolls"; +import { rollCheckSingleDie, RollOptions, RollResult, RollResultStatus } from "../../src/module/rolls/roll-executor"; +import { RollProvider } from "../../src/module/rolls/roll-provider"; describe("DS4 Rolls", () => { it("Should do a proper single success role", () => { diff --git a/src/module/rolls/ds4rolls.ts b/src/module/rolls/roll-executor.ts similarity index 68% rename from src/module/rolls/ds4rolls.ts rename to src/module/rolls/roll-executor.ts index 74c76a43..1cf680f6 100644 --- a/src/module/rolls/ds4rolls.ts +++ b/src/module/rolls/roll-executor.ts @@ -1,3 +1,5 @@ +import { DS4RollProvider, RollProvider } from "./roll-provider"; + export function ds4test(testValue: number, rollOptions: RollOptions = new RollOptions()): RollResult { const finalRollValue = testValue; if (finalRollValue <= 20) { @@ -7,23 +9,6 @@ export function ds4test(testValue: number, rollOptions: RollOptions = new RollOp } } -export class DS4RollProvider { - getNextRoll(): number { - return new Roll("1d20").roll().total; - } - - getNextRolls(amount: number): Array { - return Array(amount) - .fill(0) - .map(() => this.getNextRoll()); - } -} - -export interface RollProvider { - getNextRoll(): number; - getNextRolls(number): Array; -} - export function rollCheckSingleDie( testValue: number, rollOptions: RollOptions, @@ -33,14 +18,14 @@ export function rollCheckSingleDie( const dice = [roll]; if (roll <= rollOptions.maxCritSucc) { - return createRollResult(testValue, RollResultStatus.CRITICAL_SUCCESS, dice); + return new RollResult(testValue, RollResultStatus.CRITICAL_SUCCESS, dice); } else if (roll >= rollOptions.minCritFail) { - return createRollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); + return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); } else { if (roll <= testValue) { - return createRollResult(roll, RollResultStatus.SUCCESS, dice); + return new RollResult(roll, RollResultStatus.SUCCESS, dice); } else { - return createRollResult(0, RollResultStatus.FAILURE, dice); + return new RollResult(0, RollResultStatus.FAILURE, dice); } } } @@ -58,9 +43,8 @@ function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): Rol const firstResult = dice[1]; - // TODO: Special stuff (Gnomes!) - if (firstResult == 20) { - createRollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); + if (firstResult >= rollOptions.minCritFail) { + return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); } const [otherRolls, critSuccesses] = dice @@ -87,11 +71,7 @@ function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): Rol }) .reduce((a, b) => a + b); - return createRollResult(evaluationResult, RollResultStatus.SUCCESS, sortedRollResults); -} - -function createRollResult(totalValue: number, rollResult: RollResultStatus, dice: Array): RollResult { - return new RollResult(totalValue, RollResultStatus.SUCCESS, dice); + return new RollResult(evaluationResult, RollResultStatus.SUCCESS, sortedRollResults); } export class RollOptions { diff --git a/src/module/rolls/roll-provider.ts b/src/module/rolls/roll-provider.ts new file mode 100644 index 00000000..8aad7c82 --- /dev/null +++ b/src/module/rolls/roll-provider.ts @@ -0,0 +1,16 @@ +export class DS4RollProvider { + getNextRoll(): number { + return new Roll("1d20").roll().total; + } + + getNextRolls(amount: number): Array { + return Array(amount) + .fill(0) + .map(() => this.getNextRoll()); + } +} + +export interface RollProvider { + getNextRoll(): number; + getNextRolls(number): Array; +} From 2178e745bcdd30cbaca9bfcfa28ddecd1d564fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 22:19:36 +0100 Subject: [PATCH 07/17] Add additional single-roll tests. --- spec/support/ds4rolls.spec.ts | 52 ++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts index b32ca97d..be0f7147 100644 --- a/spec/support/ds4rolls.spec.ts +++ b/spec/support/ds4rolls.spec.ts @@ -2,7 +2,7 @@ import { rollCheckSingleDie, RollOptions, RollResult, RollResultStatus } from ". import { RollProvider } from "../../src/module/rolls/roll-provider"; describe("DS4 Rolls", () => { - it("Should do a proper single success role", () => { + it("Should do a proper single success roll.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(4); @@ -11,4 +11,54 @@ describe("DS4 Rolls", () => { new RollResult(4, RollResultStatus.SUCCESS, [4]), ); }); + + it("Should do a proper single success roll on success edge case.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(4); + + expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual( + new RollResult(4, RollResultStatus.SUCCESS, [4]), + ); + }); + + it("Should do a proper single failure roll, lower bound", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(5); + + expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual( + new RollResult(0, RollResultStatus.FAILURE, [5]), + ); + }); + + it("Should do a proper single failure roll, upper bound", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(19); + + expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual( + new RollResult(0, RollResultStatus.FAILURE, [19]), + ); + }); + + it("Should do a proper single crit success roll.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(1); + + expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual( + new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [1]), + ); + }); + + it("Should do a proper single crit failure roll.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(20); + + expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual( + new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]), + ); + }); }); From 02214b507eeaa9d4026a8e82bf020829c97cdfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 22:24:12 +0100 Subject: [PATCH 08/17] Remove now obsolete test preScript. --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ec7ea651..5b45d1e4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,8 +31,6 @@ lint: test: stage: test - before_script: - - npm ci --cache .npm --prefer-offline script: - npm test cache: From df7915790bccc328c7366926ac3b7206e5730529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 22:37:34 +0100 Subject: [PATCH 09/17] Add test result report. --- .gitlab-ci.yml | 5 +++++ package-lock.json | 25 +++++++++++++++++++++++++ package.json | 3 ++- results.xml | 11 +++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 results.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5b45d1e4..2d24f708 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,6 +35,11 @@ test: - npm test cache: <<: *global_cache + artifacts: + when: always + reports: + junit: + - results.xml build: stage: build diff --git a/package-lock.json b/package-lock.json index 8a60e388..e22e1242 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3788,6 +3788,25 @@ "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", "dev": true }, + "jasmine-reporters": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/jasmine-reporters/-/jasmine-reporters-2.3.2.tgz", + "integrity": "sha512-u/7AT9SkuZsUfFBLLzbErohTGNsEUCKaQbsVYnLFW1gEuL2DzmBL4n8v90uZsqIqlWvWUgian8J6yOt5Fyk/+A==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1", + "xmldom": "^0.1.22" + } + }, + "jasmine-xml-reporter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jasmine-xml-reporter/-/jasmine-xml-reporter-1.2.1.tgz", + "integrity": "sha1-fKoqUYAv7+2+JLPqinrUJ8nqfbM=", + "dev": true, + "requires": { + "jasmine-reporters": "^2.2.0" + } + }, "js-base64": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", @@ -7027,6 +7046,12 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "xmldom": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", + "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 2125458d..f2dea27b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "update": "npm install --save-dev git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes", "lint": "eslint 'src/**/*.ts' --cache", "lint:fix": "eslint 'src/**/*.ts' --cache --fix", - "test": "ts-node ./node_modules/jasmine/bin/jasmine" + "test": "ts-node ./node_modules/jasmine-xml-reporter/bin/jasmine --junitreport" }, "author": "", "license": "", @@ -34,6 +34,7 @@ "gulp-typescript": "^6.0.0-alpha.1", "husky": "^4.3.6", "jasmine": "^3.6.3", + "jasmine-xml-reporter": "^1.2.1", "json-stringify-pretty-compact": "^2.0.0", "lint-staged": "^10.5.3", "prettier": "^2.2.1", diff --git a/results.xml b/results.xml new file mode 100644 index 00000000..a94c300d --- /dev/null +++ b/results.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From c5fe9d6963dcb66aa9f6d8dce606a3a7bb822da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 22:41:55 +0100 Subject: [PATCH 10/17] Create explicit ci test task. --- .gitignore | 3 +++ .gitlab-ci.yml | 2 +- package.json | 3 ++- results.xml | 11 ----------- 4 files changed, 6 insertions(+), 13 deletions(-) delete mode 100644 results.xml diff --git a/.gitignore b/.gitignore index e63e3301..f4078b46 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ dist # ESLint .eslintcache + +# Junit results +results.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2d24f708..c9f148e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,7 +32,7 @@ lint: test: stage: test script: - - npm test + - npm test:ci cache: <<: *global_cache artifacts: diff --git a/package.json b/package.json index f2dea27b..fa711656 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "update": "npm install --save-dev git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes", "lint": "eslint 'src/**/*.ts' --cache", "lint:fix": "eslint 'src/**/*.ts' --cache --fix", - "test": "ts-node ./node_modules/jasmine-xml-reporter/bin/jasmine --junitreport" + "test": "ts-node ./node_modules/jasmine/bin/jasmine", + "test:ci": "ts-node ./node_modules/jasmine-xml-reporter/bin/jasmine --junitreport" }, "author": "", "license": "", diff --git a/results.xml b/results.xml deleted file mode 100644 index a94c300d..00000000 --- a/results.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file From 4209b8eb14ebdd214d5934820bdf14322c6d4dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 22:46:01 +0100 Subject: [PATCH 11/17] Add 'run'. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9f148e6..c3370fa4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,7 +32,7 @@ lint: test: stage: test script: - - npm test:ci + - npm run test:ci cache: <<: *global_cache artifacts: From b3c89b4d3258b9078c46fa7452e7e25cf7ad6c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 23:03:08 +0100 Subject: [PATCH 12/17] Add missing cases for test <20. --- spec/support/ds4rolls.spec.ts | 54 +++++++++++++++++++++++++++++++ src/module/rolls/roll-executor.ts | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts index be0f7147..c351c165 100644 --- a/spec/support/ds4rolls.spec.ts +++ b/spec/support/ds4rolls.spec.ts @@ -61,4 +61,58 @@ describe("DS4 Rolls", () => { new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]), ); }); + + it("Should do a proper crit success with changed bounds, lower bound", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(1); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( + new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [1]), + ); + }); + + it("Should do a proper crit success with changed bounds, upper bound", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(2); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( + new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [2]), + ); + }); + + it("Should do a proper success with changed bounds, lower bound", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(3); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( + new RollResult(3, RollResultStatus.SUCCESS, [3]), + ); + }); + + it("Should do a proper success with changed bounds, lower bound", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(18); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( + new RollResult(0, RollResultStatus.FAILURE, [18]), + ); + }); + + it("Should do a proper crit fail with changed bounds, lower bound", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(19); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( + new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19]), + ); + }); + + it("Should do a proper crit fail with changed bounds, upper bound", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); + + rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(20); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( + new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]), + ); + }); }); diff --git a/src/module/rolls/roll-executor.ts b/src/module/rolls/roll-executor.ts index 1cf680f6..a4b67e32 100644 --- a/src/module/rolls/roll-executor.ts +++ b/src/module/rolls/roll-executor.ts @@ -75,7 +75,7 @@ function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): Rol } export class RollOptions { - constructor(public minCritFail: number = 20, public maxCritSucc: number = 1) {} + constructor(public maxCritSucc: number = 1, public minCritFail: number = 20) {} } export class RollResult { From 3526e6ab9913d859ee37fce81ae78e7cbe2b367c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Wed, 30 Dec 2020 23:58:01 +0100 Subject: [PATCH 13/17] Restructure single-dice tests. --- spec/support/ds4rolls.spec.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts index c351c165..093f3157 100644 --- a/spec/support/ds4rolls.spec.ts +++ b/spec/support/ds4rolls.spec.ts @@ -1,8 +1,8 @@ import { rollCheckSingleDie, RollOptions, RollResult, RollResultStatus } from "../../src/module/rolls/roll-executor"; import { RollProvider } from "../../src/module/rolls/roll-provider"; -describe("DS4 Rolls", () => { - it("Should do a proper single success roll.", () => { +describe("DS4 Rolls with one die and no modifications.", () => { + it("Should do a regular success roll.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(4); @@ -12,7 +12,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper single success roll on success edge case.", () => { + it("Should do a single success roll on success upper edge case.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(4); @@ -22,7 +22,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper single failure roll, lower bound", () => { + it("Should do a single failure roll on lower edge case.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(5); @@ -32,7 +32,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper single failure roll, upper bound", () => { + it("Should do a single failure roll on upper edge case '19'.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(19); @@ -42,7 +42,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper single crit success roll.", () => { + it("Should do a single crit success roll on '1'.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(1); @@ -52,7 +52,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper single crit failure roll.", () => { + it("Should do a single crit failure roll on '20'.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(20); @@ -61,8 +61,10 @@ describe("DS4 Rolls", () => { new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]), ); }); +}); - it("Should do a proper crit success with changed bounds, lower bound", () => { +describe("DS4 Rolls with one die and crit roll modifications.", () => { + it("Should do a crit success on `1`.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(1); @@ -71,7 +73,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper crit success with changed bounds, upper bound", () => { + it("Should do a crit success on `maxCritSucc`.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(2); @@ -80,7 +82,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper success with changed bounds, lower bound", () => { + it("Should do a success on lower edge case `3`.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(3); @@ -89,7 +91,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper success with changed bounds, lower bound", () => { + it("Should do a success on upper edge case `18`.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(18); @@ -98,7 +100,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper crit fail with changed bounds, lower bound", () => { + it("Should do a crit fail on `minCritFail`.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(19); @@ -107,7 +109,7 @@ describe("DS4 Rolls", () => { ); }); - it("Should do a proper crit fail with changed bounds, upper bound", () => { + it("Should do a crit fail on `20`", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(20); From c3157424128f59d3c06686cdb50a08d1e2273fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Thu, 31 Dec 2020 00:47:31 +0100 Subject: [PATCH 14/17] Add tests for standard mutli dice rolls. --- spec/support/ds4rolls.spec.ts | 66 ++++++++++++++++++++++++++++++- src/module/rolls/roll-executor.ts | 38 ++++++++++++------ 2 files changed, 91 insertions(+), 13 deletions(-) diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts index 093f3157..6b8e3228 100644 --- a/spec/support/ds4rolls.spec.ts +++ b/spec/support/ds4rolls.spec.ts @@ -1,4 +1,10 @@ -import { rollCheckSingleDie, RollOptions, RollResult, RollResultStatus } from "../../src/module/rolls/roll-executor"; +import { + rollCheckMultipleDice, + rollCheckSingleDie, + RollOptions, + RollResult, + RollResultStatus, +} from "../../src/module/rolls/roll-executor"; import { RollProvider } from "../../src/module/rolls/roll-provider"; describe("DS4 Rolls with one die and no modifications.", () => { @@ -68,6 +74,7 @@ describe("DS4 Rolls with one die and crit roll modifications.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(1); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [1]), ); @@ -77,6 +84,7 @@ describe("DS4 Rolls with one die and crit roll modifications.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(2); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [2]), ); @@ -86,6 +94,7 @@ describe("DS4 Rolls with one die and crit roll modifications.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(3); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( new RollResult(3, RollResultStatus.SUCCESS, [3]), ); @@ -95,6 +104,7 @@ describe("DS4 Rolls with one die and crit roll modifications.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(18); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( new RollResult(0, RollResultStatus.FAILURE, [18]), ); @@ -104,6 +114,7 @@ describe("DS4 Rolls with one die and crit roll modifications.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(19); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19]), ); @@ -113,8 +124,61 @@ describe("DS4 Rolls with one die and crit roll modifications.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(20); + expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual( new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]), ); }); }); + +describe("DS4 Rools with multiple dice and no modifiers.", () => { + it("Should do a crit fail on `20` for first roll.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([20, 15, 6]); + + expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual( + new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20, 15, 6]), + ); + }); + + it("Should succeed with all rolls crit successes.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([1, 1, 1]); + + expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual( + new RollResult(48, RollResultStatus.SUCCESS, [1, 1, 1]), + ); + }); + + it("Should succeed with the last roll not being suficient.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 15, 15]); + + expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual( + new RollResult(30, RollResultStatus.SUCCESS, [15, 15, 15]), + ); + }); + + it("Should succeed with the last roll a crit success.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 15, 1]); + + expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual( + new RollResult(35, RollResultStatus.SUCCESS, [1, 15, 15]), + ); + }); + + it("Should succeed with the last roll being 20 and one crit success.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 1, 20]); + + expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual( + new RollResult(40, RollResultStatus.SUCCESS, [1, 20, 15]), + ); + }); +}); diff --git a/src/module/rolls/roll-executor.ts b/src/module/rolls/roll-executor.ts index a4b67e32..1c23d3f2 100644 --- a/src/module/rolls/roll-executor.ts +++ b/src/module/rolls/roll-executor.ts @@ -30,27 +30,41 @@ export function rollCheckSingleDie( } } -function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): RollResult { +export function rollCheckMultipleDice( + testValue: number, + rollOptions: RollOptions, + provider: RollProvider = new DS4RollProvider(), +): RollResult { const finalCheck = testValue % 20; const numberOfDice = Math.ceil(testValue / 20); - const rolls = new Array(); - for (let i = 0; i < numberOfDice; i++) { - const roll = new Roll("1d20"); - roll.roll(); - rolls.concat(roll); - } - const dice = rolls.map((r) => r.total); - const firstResult = dice[1]; + const dice = provider.getNextRolls(numberOfDice); + + console.log(dice[0]); + console.log(rollOptions.minCritFail); + + const firstResult = dice[0]; if (firstResult >= rollOptions.minCritFail) { return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); } - const [otherRolls, critSuccesses] = dice - .partition((r) => r <= rollOptions.maxCritSucc) + const partitionCallback = (prev: [Array, Array], cur: number) => { + if (cur <= rollOptions.maxCritSucc) { + prev[0].push(cur); + } else { + prev[1].push(cur); + } + return prev; + }; + + const [critSuccesses, otherRolls] = dice + .reduce(partitionCallback, [[], []]) .map((a) => a.sort((r1, r2) => r2 - r1)); + console.log(critSuccesses); + console.log(otherRolls); + const sortedRollResults: Array = critSuccesses.concat(otherRolls); const evaluationResult = sortedRollResults @@ -59,7 +73,7 @@ function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): Rol if (value == 1) { return finalCheck; } else { - return value >= finalCheck ? value : 0; + return value <= finalCheck ? value : 0; } } else { if (value <= rollOptions.maxCritSucc) { From b569e5488ecaa02953de3588a074ab9eca30e7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Thu, 31 Dec 2020 00:52:59 +0100 Subject: [PATCH 15/17] Add test for multiple rolls with min/max modifiers --- spec/support/ds4rolls.spec.ts | 62 +++++++++++++++++++++++++++++++ src/module/rolls/roll-executor.ts | 6 --- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts index 6b8e3228..844ba622 100644 --- a/spec/support/ds4rolls.spec.ts +++ b/spec/support/ds4rolls.spec.ts @@ -182,3 +182,65 @@ describe("DS4 Rools with multiple dice and no modifiers.", () => { ); }); }); + +describe("DS4 Rools with multiple dice and min/max modifiers.", () => { + it("Should do a crit fail on `19` for first roll.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([19, 15, 6]); + + expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual( + new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]), + ); + }); + + it("Should succeed with all rolls crit successes (1 and 2).", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([2, 1, 2]); + + expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual( + new RollResult(48, RollResultStatus.SUCCESS, [2, 2, 1]), + ); + }); + + it("Should succeed with the last roll not being suficient.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 15, 15]); + + expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual( + new RollResult(30, RollResultStatus.SUCCESS, [15, 15, 15]), + ); + }); + + it("Should succeed with the last roll a crit success `2`.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 15, 2]); + + expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual( + new RollResult(35, RollResultStatus.SUCCESS, [2, 15, 15]), + ); + }); + + it("Should succeed with the last roll being `20` and one crit success '2'.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 2, 20]); + + expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual( + new RollResult(40, RollResultStatus.SUCCESS, [2, 20, 15]), + ); + }); + + it("Should succeed with the last roll being `19` and one crit success '2'.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 2, 19]); + + expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual( + new RollResult(39, RollResultStatus.SUCCESS, [2, 19, 15]), + ); + }); +}); diff --git a/src/module/rolls/roll-executor.ts b/src/module/rolls/roll-executor.ts index 1c23d3f2..1d383a62 100644 --- a/src/module/rolls/roll-executor.ts +++ b/src/module/rolls/roll-executor.ts @@ -40,9 +40,6 @@ export function rollCheckMultipleDice( const dice = provider.getNextRolls(numberOfDice); - console.log(dice[0]); - console.log(rollOptions.minCritFail); - const firstResult = dice[0]; if (firstResult >= rollOptions.minCritFail) { @@ -62,9 +59,6 @@ export function rollCheckMultipleDice( .reduce(partitionCallback, [[], []]) .map((a) => a.sort((r1, r2) => r2 - r1)); - console.log(critSuccesses); - console.log(otherRolls); - const sortedRollResults: Array = critSuccesses.concat(otherRolls); const evaluationResult = sortedRollResults From 6b18e720a106e8904e84a6e09b2d6645c8dee053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Thu, 31 Dec 2020 02:27:21 +0100 Subject: [PATCH 16/17] Additional tests and features as per Request. --- spec/support/ds4rolls.spec.ts | 24 ++++++++++++++++++++++++ src/module/rolls/roll-executor.ts | 3 ++- src/module/rolls/roll-provider.ts | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts index 844ba622..e729b6e2 100644 --- a/spec/support/ds4rolls.spec.ts +++ b/spec/support/ds4rolls.spec.ts @@ -244,3 +244,27 @@ describe("DS4 Rools with multiple dice and min/max modifiers.", () => { ); }); }); + +describe("DS4 Rools with multiple dice and fail modifiers.", () => { + it("Should do a crit fail on `19` for first roll.", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([19, 15, 6]); + + expect(rollCheckMultipleDice(48, { minCritFail: 19 } as RollOptions, rollProvider)).toEqual( + new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]), + ); + }); +}); + +describe("DS4 Rools with multiple dice and success modifiers.", () => { + it("Should succeed with all rolls crit successes (1 and 2).", () => { + const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]); + + rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([2, 1, 2]); + + expect(rollCheckMultipleDice(48, { maxCritSucc: 2 } as RollOptions, rollProvider)).toEqual( + new RollResult(48, RollResultStatus.SUCCESS, [2, 2, 1]), + ); + }); +}); diff --git a/src/module/rolls/roll-executor.ts b/src/module/rolls/roll-executor.ts index 1d383a62..204f5edc 100644 --- a/src/module/rolls/roll-executor.ts +++ b/src/module/rolls/roll-executor.ts @@ -83,7 +83,8 @@ export function rollCheckMultipleDice( } export class RollOptions { - constructor(public maxCritSucc: number = 1, public minCritFail: number = 20) {} + public maxCritSucc = 1; + public minCritFail = 20; } export class RollResult { diff --git a/src/module/rolls/roll-provider.ts b/src/module/rolls/roll-provider.ts index 8aad7c82..2e4982e0 100644 --- a/src/module/rolls/roll-provider.ts +++ b/src/module/rolls/roll-provider.ts @@ -12,5 +12,5 @@ export class DS4RollProvider { export interface RollProvider { getNextRoll(): number; - getNextRolls(number): Array; + getNextRolls(number: number): Array; } From c76c2c23c3bb0b8696bf1e80ac0cd0373622b1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= Date: Thu, 31 Dec 2020 02:29:27 +0100 Subject: [PATCH 17/17] Fix jasmine imports/symbol visibility. --- spec/support/ds4rolls.spec.ts | 2 ++ tsconfig.json | 15 ++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts index e729b6e2..f56fa46c 100644 --- a/spec/support/ds4rolls.spec.ts +++ b/spec/support/ds4rolls.spec.ts @@ -7,6 +7,8 @@ import { } from "../../src/module/rolls/roll-executor"; import { RollProvider } from "../../src/module/rolls/roll-provider"; +import "jasmine"; + describe("DS4 Rolls with one die and no modifications.", () => { it("Should do a regular success roll.", () => { const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]); diff --git a/tsconfig.json b/tsconfig.json index 64707c38..f988dd5b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,8 @@ -{ - "compilerOptions": { - "target": "ES2017", - "lib": ["DOM", "ES6", "ES2017"], - "types": ["foundry-pc-types", "jasmine"] - } -} +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["DOM", "ES6", "ES2017"], + "types": ["foundry-pc-types"], + "esModuleInterop": true + } +}