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"] } }