diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 121f7bb..0000000 --- a/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@pkmn" -} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 31e8882..9b3acdb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,30 +1,14 @@ -# This workflow will do an install of node dependencies, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - name: Tests - on: push: branches: [ main ] pull_request: branches: [ main ] - jobs: build: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [14.x] - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: Zarel/setup-node@patch-1 - with: - node-version: ${{ matrix.node-version }} + - uses: actions/checkout@v3 - run: npm install - - run: npm run test - env: - CI: true + - run: npm test + - run: npm run lint diff --git a/.gitignore b/.gitignore index 08cf3f7..dc5afd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,27 @@ -node_modules/ -build/ +# Mac .DS_Store Thumbs.db +.vscode + +# Zig +zig-cache/ +zig-out/ +build/ +/docgen_tmp/ + +# C +*.o + +# JS +coverage/ +node_modules/ +dist/ *package-lock.json +*npm-debug.log .eslintcache .tsbuildinfo -.vscode -*-debug.log +.parcel-cache/ + +# Project +logs/ +release/ \ No newline at end of file diff --git a/LICENSE b/LICENSE index 66d6fbc..3469970 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2020-2022 pkmn contributors +Copyright (c) 2020-2023 pkmn contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index ec13d15..69f51d3 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,6 @@ perspective of the client. EPOké is designed to be useful in many domains, expo information from replays, extending the Pokémon Showdown client tooltips/UI, or inside the engine of a Pokemon AI. -- [`@pkmn/epoke`](epoke): a wrapper around - [`@pkmn/client`](https://github.com/pkmn/ps/tree/master/client) which ties together `@pkmn/gmd`, - `@pkmn/predictor` and `@pkmn/spreads` to produce an enhanced client representation of a battle -- [`@pkmn/gmd`](gmd): a reverse damage calculator built on top of - [`@pkmn/dmg`](https://github.com/pkmn/dmg) to deduce information about a Pokémon's moveset based - on the damage it deals or receives in battle -- [`@pkmn/predictor`](predictor): logic which combines usage stats information from - [`@pkmn/stats`](https://github.com/pkmn/stats) with minimal heuristics to predict likely instances - of an opponent's team -- [`@pkmn/spreads`](spreads): a lightweight, standalone package for manipulating and displaying - stats, spreads, and ranges +TODO Everything in this repository is distributed under the terms of the [MIT License](LICENSE). diff --git a/epoke/README.md b/epoke/README.md index 4197e8b..a4279ca 100644 --- a/epoke/README.md +++ b/epoke/README.md @@ -1,67 +1,18 @@ -

EPOké

- # `@pkmn/epoke` ![Test Status](https://github.com/pkmn/EPOke/workflows/Tests/badge.svg) ![License](https://img.shields.io/badge/License-MIT-blue.svg) -`@pkmn/epoke` extends [`@pkmn/client`](https://github.com/pkmn/ps/tree/master/client) with -information from [`@pkmn/gmd`](../gmd), [`@pkmn/predictor`](../predictor), and -[`@pkmn/spreads`](../spreads) to produce an enhanced client representation of a Pokémon battle. -EPOké offers several key features over `@pkmn/client`: - -- **spread information**: `@pkmn/client` can determine information about a side's `stats` from the - `|request|` protocol message and provides support for setting a side's `sets` to known values so - that its state can be used with [`@pkmn/dmg`](https://github.com/pkmn/dmg) in a limited number of - circumstances, but with EPOké `@pkmn/dmg` can *always* be used as it can additionally: - - reverse the `stats` from `|request|` into a spread using `@pkmn/spreads` - - track the `StatsRange` for a Pokémon, winnow it down with information from `@pkmn/gmd`, and - finally, return the most likely estimate for the spread with help from `@pkmn/predictor` and - usage stats - - allow for a user to override predicted values for stats (while still maintaining accurate tracking) -- **inferred state**: EPOké leverages `@pkmn/gmd` to deduce information about the battle state (eg. - an opponent Pokémon's item or ability) on top of what is already known from `@pkmn/client` -- **possible state instantiation**: EPOké provides the ability to generate possible instantiations - of the simulator's [`State`](https://github.com/pkmn/ps/blob/master/sim/sim/state.ts) using the - information it has collected and with help from `@pkmn/predictor` and usage stats - -If information in battle can be determined conclusively and without the help of anything in this -repository it is in scope for `@pkmn/client`, everything else is the domain of `@pkmn/epoke`. - +TODO ## Installation ```sh $ npm install @pkmn/epoke ``` -Alternatively, as [detailed below](#browser), if you are using `@pkmn/epoke` in the browser and -want a convenient way to get started, simply depend on a transpiled and minified version via -[unpkg](https://unpkg.com/): - -```html - -``` - ## Usage -FIXME - -```ts -``` - -### Browser - -The recommended way of using `@pkmn/epoke` in a web browser is to **configure your bundler** -([Webpack](https://webpack.js.org/), [Rollup](https://rollupjs.org/), -[Parcel](https://parceljs.org/), etc) to minimize it and package it with the rest of your -application. If you do not use a bundler, a minified `index.umd.js` bundle is included in the -package. You simply need to depend on `./node_modules/@pkmn/epoke/build/index.umd.js` in a -`script` tag (which is what the unpkg shortcut above is doing), after which **`epoke` will be -accessible as a global**: - -```html - -``` +TODO ## License diff --git a/epoke/package.json b/epoke/package.json index d8989d9..28aa2c4 100644 --- a/epoke/package.json +++ b/epoke/package.json @@ -1,53 +1,25 @@ { "name": "@pkmn/epoke", "version": "0.0.1", + "main": "build/index.js", + "types": "build/index.d.ts", "description": "Enhanced client representation of a Pokémon battle", "repository": "github:pkmn/EPOke", "license": "MIT", - "sideEffects": false, - "main": "build/index.js", - "module": "build/index.mjs", - "types": "build/index.d.ts", - "files": ["build"], "publishConfig": { "access": "public" }, "dependencies": { - "@pkmn/client": "^0.6.0", - "@pkmn/data": "^0.7.0", - "@pkmn/protocol": "^0.6.0", - "@pkmn/spreads": "^0.1.0" + "@pkmn/spreads": "file:../spreads" }, "scripts": { "lint": "eslint --cache src --ext ts", "fix": "eslint --fix src --ext ts", "compile": "tsc -p .", - "build": "npm run compile && tsup src/index.ts", - "test": "jest", - "prepare": "npm run build", - "pretest": "npm run build", - "posttest": "npm run lint" - }, - "tsup": { - "outDir": "build", - "format": ["cjs", "esm"], - "sourcemap": true, - "dts": true, - "clean": true - }, - "jest": { - "preset": "ts-jest", - "testEnvironment": "node", - "testPathIgnorePatterns": ["node_modules/", "build/"] + "build": "npm run compile", + "test": "vitest src" }, "eslintConfig": { - "extends": "@pkmn", - "ignorePatterns": ["node_modules/", "build/"], - "overrides": [{ - "files": ["src/index.ts"], - "rules": { - "@typescript-eslint/no-shadow": "off" - } - }] + "extends": "@pkmn" } } diff --git a/epoke/src/client.ts b/epoke/src/client.ts deleted file mode 100644 index 9c29852..0000000 --- a/epoke/src/client.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {BoostsTable, ID, StatsTable, StatusName, TypeName} from '@pkmn/data'; -import {Range} from '@pkmn/spreads'; -import {Slot} from './index'; - -interface Unknown { - known?: T; - override?: T; - range?: Range; -} - -export interface Battle { - sides: [Side, Side]; - turn: number; - lastDamage: Unknown; - prng: readonly Unknown[]; -} - -export interface Side { - active: Pokemon | undefined; - pokemon: Unknown[]; - lastUsedMove: Unknown; - lastSelectedMove: Unknown; - lastSelectedIndex: Unknown<1 | 2 | 3 | 4 | undefined>; -} - -export interface Pokemon { - species: ID; - types: readonly [TypeName, TypeName]; - level: number; - hp: Unknown; - status: StatusName | undefined; - statusData: {sleep: Unknown; self: boolean; toxic: number}; - stats: Unknown; - boosts: BoostsTable; - moves: MoveSlot[]; - volatiles: Volatiles; - stored: { - species: ID; - types: readonly [TypeName, TypeName]; - stats: Unknown; - moves: Iterable>; - }; - position: Slot; -} - -export interface MoveSlot { - id: Unknown; - pp: Unknown; - disabled?: Unknown; -} - -export interface Volatiles { - bide?: {duration: Unknown; damage: Unknown}; - thrashing?: {duration: Unknown; accuracy: Unknown}; - multihit?: boolean; - flinch?: boolean; - charging?: boolean; - trapping?: {duration: Unknown}; - invulnerable?: boolean; - confusion?: {duration: Unknown}; - mist?: boolean; - focusenergy?: boolean; - substitute?: {hp: Unknown}; - recharging?: boolean; - rage?: {accuracy: Unknown}; - leechseed?: boolean; - lightscreen?: boolean; - reflect?: boolean; - transform?: {player: 'p1' | 'p2'; slot: number}; -} - -// Doesnt extend or delegate to regular client (which ) -// Can't be used zero-copy with dmg - diff --git a/epoke/src/test/index.test.ts b/epoke/src/index.test.ts similarity index 100% rename from epoke/src/test/index.test.ts rename to epoke/src/index.test.ts diff --git a/epoke/src/index.ts b/epoke/src/index.ts index 80113bf..e69de29 100644 --- a/epoke/src/index.ts +++ b/epoke/src/index.ts @@ -1,70 +0,0 @@ -import {BoostsTable, ID, StatsTable, StatusName, TypeName} from '@pkmn/data'; - -export type Slot = 1 | 2 | 3 | 4 | 5 | 6; -export type Battle = Gen1.Battle; -export type Side = Gen1.Side; -export type Pokemon = Gen1.Pokemon; -export type MoveSlot = Gen1.MoveSlot; - -export namespace Gen1 { - export interface Battle { - sides: Iterable; - turn: number; - lastDamage: number; - prng: readonly number[]; - } - - export interface Side { - active: Pokemon | undefined; - pokemon: Iterable; - lastUsedMove: ID | undefined; - lastSelectedMove: ID | undefined; - lastSelectedIndex: 1 | 2 | 3 | 4 | undefined; - } - - export interface Pokemon { - species: ID; - types: readonly [TypeName, TypeName]; - level: number; - hp: number; - status: StatusName | undefined; - statusData: {sleep: number; self: boolean; toxic: number}; - stats: StatsTable; - boosts: BoostsTable; - moves: Iterable; - volatiles: Volatiles; - stored: { - species: ID; - types: readonly [TypeName, TypeName]; - stats: StatsTable; - moves: Iterable>; - }; - position: Slot; - } - - export interface MoveSlot { - id: ID; - pp: number; - disabled?: number; - } - - export interface Volatiles { - bide?: {duration: number; damage: number}; - thrashing?: {duration: number; accuracy: number}; - multihit?: unknown; - flinch?: unknown; - charging?: unknown; - trapping?: {duration: number}; - invulnerable?: unknown; - confusion?: {duration: number}; - mist?: unknown; - focusenergy?: unknown; - substitute?: {hp: number}; - recharging?: unknown; - rage?: {accuracy: number}; - leechseed?: unknown; - lightscreen?: unknown; - reflect?: unknown; - transform?: {player: 'p1' | 'p2'; slot: number}; - } -} diff --git a/epoke/test b/epoke/test deleted file mode 100644 index e8b6c61..0000000 --- a/epoke/test +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -try { - require('source-map-support').install(); -} catch (err) { - if (err.code !== 'MODULE_NOT_FOUND') throw err; -} - -const diff = require('json-diff'); - -const {Dex, TeamValidator, RandomPlayerAI, BattleStreams, PRNG} = require('@pkmn/sim'); -const {Battle} = require('@pkmn/client'); - -const {Generations} = require('@pkmn/data'); - -const prng = new PRNG(); -const seed = prng.seed; -const formatid = 'gen4ou'; // TODO -const validator = new TeamValidator(formatid, Dex); -// TODO: alternatively, pull teams from a team DB... -const teamA = dex.packTeam(predictor.generateTeam(formatid, prng, validator)); -const teamB = dex.packTeam(predictor.generateTeam(formatid, prng, validator)); - -const battleStream = new BattleStreams.BattleStream(); -const streams = BattleStreams.getPlayerStreams(battleStream); - -const p1 = new RandomPlayerAI(streams.p1); -const p2 = new RandomPlayerAI(streams.p2); - -void p1.start(); -void p2.start(); - -const gens = new Generations(Dex); -const battle = new Battle(gens); - -void (async () => { - for await (const chunk of streams.omniscient) { - battle.add(chunk); - // diff.diffString(battle.toJSON(), battleStream.battle); - } -})(); - -void streams.omniscient.write(`>start ${JSON.stringify({formatid})} ->player p1 ${JSON.stringify({name: 'Player A', team: teamA})} ->player p2 ${JSON.stringify({name: 'Player B', team: teamB})}`); \ No newline at end of file diff --git a/epoke/tsconfig.json b/epoke/tsconfig.json index 61b458c..36e101e 100644 --- a/epoke/tsconfig.json +++ b/epoke/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "outDir": "build", "tsBuildInfoFile": ".tsbuildinfo" }, "include": ["src/**/*"] diff --git a/epoke/vitest.config.ts b/epoke/vitest.config.ts new file mode 100644 index 0000000..dbfd933 --- /dev/null +++ b/epoke/vitest.config.ts @@ -0,0 +1,9 @@ +import {defineConfig} from 'vitest/config'; + +export default defineConfig({ + test: { + watch: false, + globals: true, + exclude: ['node_modules', 'build'], + }, +}); diff --git a/gmd/README.md b/gmd/README.md index 64973ee..8b4a98b 100644 --- a/gmd/README.md +++ b/gmd/README.md @@ -3,7 +3,7 @@ ![Test Status](https://github.com/pkmn/EPOke/workflows/Tests/badge.svg) ![License](https://img.shields.io/badge/License-MIT-blue.svg) -FIXME +TODO ## Installation @@ -11,34 +11,9 @@ FIXME $ npm install @pkmn/gmd ``` -Alternatively, as [detailed below](#browser), if you are using `@pkmn/gmd` in the browser and -want a convenient way to get started, simply depend on a transpiled and minified version via -[unpkg](https://unpkg.com/): - -```html - -``` - ## Usage -FIXME - -```ts -``` - -### Browser - -The recommended way of using `@pkmn/gmd` in a web browser is to **configure your bundler** -([Webpack](https://webpack.js.org/), [Rollup](https://rollupjs.org/), -[Parcel](https://parceljs.org/), etc) to minimize it and package it with the rest of your -application. If you do not use a bundler, a minified `index.umd.js` bundle is included in the -package. You simply need to depend on `./node_modules/@pkmn/gmd/build/index.umd.js` in a -`script` tag (which is what the unpkg shortcut above is doing), after which **`gmd` will be -accessible as a global**: - -```html - -``` +TODO ## License diff --git a/gmd/package.json b/gmd/package.json index cc7bd34..a889c9e 100644 --- a/gmd/package.json +++ b/gmd/package.json @@ -1,14 +1,11 @@ { "name": "@pkmn/gmd", "version": "0.0.1", + "main": "build/index.js", + "types": "build/index.d.ts", "description": "Reverse Pokémon damage calculator", "repository": "github:pkmn/EPOke", "license": "MIT", - "sideEffects": false, - "main": "build/index.js", - "module": "build/index.mjs", - "types": "build/index.d.ts", - "files": ["build"], "publishConfig": { "access": "public" }, @@ -16,22 +13,10 @@ "lint": "eslint --cache src --ext ts", "fix": "eslint --fix src --ext ts", "compile": "tsc -p .", - "build": "npm run compile && tsup src/index.ts", - "test": "jest", - "prepare": "npm run build", - "pretest": "npm run build", - "posttest": "npm run lint" - }, - "tsup": { - "outDir": "build", - "format": ["cjs", "esm"], - "sourcemap": true, - "dts": true, - "clean": true + "build": "npm run compile", + "test": "vitest src" }, - "jest": { - "preset": "ts-jest", - "testEnvironment": "node", - "testPathIgnorePatterns": ["node_modules/", "build/"] + "eslintConfig": { + "extends": "@pkmn" } } diff --git a/gmd/tsconfig.json b/gmd/tsconfig.json index 61b458c..36e101e 100644 --- a/gmd/tsconfig.json +++ b/gmd/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "outDir": "build", "tsBuildInfoFile": ".tsbuildinfo" }, "include": ["src/**/*"] diff --git a/gmd/vitest.config.ts b/gmd/vitest.config.ts new file mode 100644 index 0000000..dbfd933 --- /dev/null +++ b/gmd/vitest.config.ts @@ -0,0 +1,9 @@ +import {defineConfig} from 'vitest/config'; + +export default defineConfig({ + test: { + watch: false, + globals: true, + exclude: ['node_modules', 'build'], + }, +}); diff --git a/package.json b/package.json index b42ea2e..031d057 100644 --- a/package.json +++ b/package.json @@ -3,21 +3,15 @@ "repository": "github:pkmn/EPOke", "license": "MIT", "devDependencies": { - "@pkmn/eslint-config": "^2.12.0", - "@types/jest": "^29.2.3", - "@types/node": "^18.11.9", - "@typescript-eslint/eslint-plugin": "^5.43.0", - "@typescript-eslint/parser": "^5.43.0", - "eslint": "^8.28.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^27.1.5", - "jest": "^29.3.1", + "@pkmn/eslint-config": "^5.3.0", + "@types/jest": "^29.5.6", + "@types/node": "^20.8.9", + "eslint": "^8.52.0", "subpkg": "^4.1.0", - "ts-jest": "^29.0.3", - "tsup": "^6.5.0", - "typescript": "^4.9.3" + "vitest": "^0.34.6", + "typescript": "^5.2.2" }, - "subPackages": ["spreads", "epoke"], + "subPackages": ["epoke", "gmd", "predictor", "spreads"], "scripts": { "lint": "subpkg run lint", "fix": "subpkg run fix", diff --git a/predictor/README.md b/predictor/README.md index ff06b5d..e4c01b4 100644 --- a/predictor/README.md +++ b/predictor/README.md @@ -3,7 +3,7 @@ ![Test Status](https://github.com/pkmn/EPOke/workflows/Tests/badge.svg) ![License](https://img.shields.io/badge/License-MIT-blue.svg) -FIXME +TODO ## Installation @@ -11,34 +11,9 @@ FIXME $ npm install @pkmn/predictor ``` -Alternatively, as [detailed below](#browser), if you are using `@pkmn/predictor` in the browser and -want a convenient way to get started, simply depend on a transpiled and minified version via -[unpkg](https://unpkg.com/): - -```html - -``` - ## Usage -FIXME - -```ts -``` - -### Browser - -The recommended way of using `@pkmn/predictor` in a web browser is to **configure your bundler** -([Webpack](https://webpack.js.org/), [Rollup](https://rollupjs.org/), -[Parcel](https://parceljs.org/), etc) to minimize it and package it with the rest of your -application. If you do not use a bundler, a minified `index.umd.js` bundle is included in the -package. You simply need to depend on `./node_modules/@pkmn/predictor/build/index.umd.js` in a -`script` tag (which is what the unpkg shortcut above is doing), after which **`predictor` will be -accessible as a global**: - -```html - -``` +TODO ## License diff --git a/predictor/package.json b/predictor/package.json index 21cb77b..7bf7236 100644 --- a/predictor/package.json +++ b/predictor/package.json @@ -1,14 +1,11 @@ { "name": "@pkmn/predictor", "version": "0.0.1", + "main": "build/index.js", + "types": "build/index.d.ts", "description": "Library for predicting likely instances of an opponent's Pokémon team", "repository": "github:pkmn/EPOke", "license": "MIT", - "sideEffects": false, - "main": "build/index.js", - "module": "build/index.mjs", - "types": "build/index.d.ts", - "files": ["build"], "publishConfig": { "access": "public" }, @@ -16,22 +13,10 @@ "lint": "eslint --cache src --ext ts", "fix": "eslint --fix src --ext ts", "compile": "tsc -p .", - "build": "npm run compile && tsup src/index.ts", - "test": "jest", - "prepare": "npm run build", - "pretest": "npm run build", - "posttest": "npm run lint" - }, - "tsup": { - "outDir": "build", - "format": ["cjs", "esm"], - "sourcemap": true, - "dts": true, - "clean": true + "build": "npm run compile", + "test": "vitest src" }, - "jest": { - "preset": "ts-jest", - "testEnvironment": "node", - "testPathIgnorePatterns": ["node_modules/", "build/"] + "eslintConfig": { + "extends": "@pkmn" } -} +} \ No newline at end of file diff --git a/predictor/src/test/index.test.ts b/predictor/src/index.test.ts similarity index 100% rename from predictor/src/test/index.test.ts rename to predictor/src/index.test.ts diff --git a/predictor/src/random.ts b/predictor/src/random.ts deleted file mode 100644 index e0001ff..0000000 --- a/predictor/src/random.ts +++ /dev/null @@ -1,55 +0,0 @@ -export class Random { - seed: number; - - static seed(n = 4 /* https://xkcd.com/221/ */) { - // Hash: https://burtleburtle.net/bob/hash/integer.html - n = n ^ 61 ^ (n >>> 16); - n = n + (n << 3); - n = n ^ (n >>> 4); - n = Math.imul(n, 0x27d4eb2d); - n = n ^ (n >>> 15); - return n >>> 0; - } - - constructor(seed = Random.seed()) { - this.seed = seed; - } - - // Mulberry32: https://gist.github.com/tommyettinger/46a874533244883189143505d203312c - next(min?: number, max?: number) { - if (min) min = Math.floor(min); - if (max) max = Math.floor(max); - - let z = (this.seed += 0x6d2b79f5 | 0); - z = Math.imul(z ^ (z >>> 15), z | 1); - z = z ^ (z + Math.imul(z ^ (z >>> 7), z | 61)); - z = (z ^ (z >>> 14)) >>> 0; - const n = z / 2 ** 32; - - if (min === undefined) return n; - if (!max) return Math.floor(n * min); - return Math.floor(n * (max - min)) + min; - } - - shuffle(arr: T[]) { - for (let i = arr.length - 1; i > 0; i--) { - const j = Math.floor(this.next() * (i + 1)); - [arr[i], arr[j]] = [arr[j], arr[i]]; - } - return arr; - } - - sample(arr: T[], remove = false) { - if (arr.length === 0) throw new RangeError('Cannot sample an empty array'); - const index = this.next(arr.length); - const val = arr[index]; - if (remove) { - arr[index] = arr[arr.length - 1]; - arr.pop(); - } - if (val === undefined && !Object.prototype.hasOwnProperty.call(arr, index)) { - throw new RangeError('Cannot sample a sparse array'); - } - return val; - } -} diff --git a/predictor/src/test/random.test.ts b/predictor/src/test/random.test.ts deleted file mode 100644 index 11451e5..0000000 --- a/predictor/src/test/random.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import {Random} from '../random'; - -describe('Random', () => { - test('next', () => { - let r = new Random(42); - const q = new Random(42); - let n = 0; - for (let i = 0; i <= 100; i++) { - n = r.next(); - expect(n).toBeGreaterThanOrEqual(0); - expect(n).toBeLessThan(1); - expect(n).toEqual(q.next()); - } - - r = new Random(); - n = 0; - for (let i = 0; i <= 100; i++) { - if (r.next(2) < 1) n++; - } - expect(n).toBeGreaterThanOrEqual(45); - expect(n).toBeLessThanOrEqual(55); - - r = new Random(); - n = 0; - for (let i = 0; i <= 100; i++) { - if (r.next(10, 265) < 227) n++; - } - expect(n).toBeGreaterThanOrEqual(80); - expect(n).toBeLessThanOrEqual(90); - }); - - test('sample', () => { - let r = new Random(); - let a: string[] = []; - expect(() => r.sample(a)).toThrow(RangeError); - - r = new Random(); - a[30] = 'hello'; - expect(() => { - for (let i = 0; i < 100; i++) { - r.sample(a); - } - }).toThrow(RangeError); - - r = new Random(); - a = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']; - delete a[9]; - expect(() => { - for (let i = 0; i < 100; i++) { - r.sample(a); - } - }).toThrow(RangeError); - - r = new Random(); - for (let i = 0; i < 10; ++i) { - expect(r.sample(['a'])).toBe('a'); - } - - r = new Random(); - a = ['a', 'b', 'c', 'd', 'e']; - const o = {a: 0, b: 0, c: 0, d: 0, e: 0}; - for (let i = 0; i < 1000; ++i) { - o[r.sample(a) as keyof typeof o]++; - } - for (const k of a) { - expect(o[k as keyof typeof o]).toBeGreaterThanOrEqual(170); - expect(o[k as keyof typeof o]).toBeLessThanOrEqual(230); - } - - r = new Random(); - a = ['x', 'x', 'y']; - const p = {x: 0, y: 0}; - for (let i = 0; i < 1000; ++i) { - p[r.sample(a) as keyof typeof p]++; - } - expect(p.x).toBeGreaterThanOrEqual(630); - expect(p.x).toBeLessThanOrEqual(710); - expect(p.y).toBeGreaterThanOrEqual(290); - expect(p.y).toBeLessThanOrEqual(370); - - r = new Random(5); - const q = new Random(5); - const b = [{}, {}, {}, {}, {}, {}, {}, {}]; - for (let i = 0; i < 10; ++i) { - expect(r.sample(b)).toBe(b[q.next(b.length)]); - } - }); - - test('shuffle', () => { - let r = new Random(); - expect(r.shuffle([])).toEqual([]); - const a = [1, 2, 3, 4, 5]; - const b = a.slice(); - - r = new Random(45); - const q = new Random(45); - expect(r.shuffle(a)).toEqual(q.shuffle(b)); - expect(a).toEqual(b); - - expect(a).not.toEqual([1, 2, 3, 4, 5]); - }); -}); diff --git a/predictor/tsconfig.json b/predictor/tsconfig.json index 61b458c..36e101e 100644 --- a/predictor/tsconfig.json +++ b/predictor/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "outDir": "build", "tsBuildInfoFile": ".tsbuildinfo" }, "include": ["src/**/*"] diff --git a/predictor/vitest.config.ts b/predictor/vitest.config.ts new file mode 100644 index 0000000..dbfd933 --- /dev/null +++ b/predictor/vitest.config.ts @@ -0,0 +1,9 @@ +import {defineConfig} from 'vitest/config'; + +export default defineConfig({ + test: { + watch: false, + globals: true, + exclude: ['node_modules', 'build'], + }, +}); diff --git a/spreads/package.json b/spreads/package.json index ef4b67c..280a71a 100644 --- a/spreads/package.json +++ b/spreads/package.json @@ -15,15 +15,15 @@ "dependencies": { "@pkmn/types": "^3.0.0" }, + "devDependencies": { + "tsup": "^7.2.0" + }, "scripts": { "lint": "eslint --cache src --ext ts", "fix": "eslint --fix src --ext ts", "compile": "tsc -p .", "build": "npm run compile && tsup src/index.ts", - "test": "jest", - "prepare": "npm run build", - "pretest": "npm run build", - "posttest": "npm run lint" + "test": "vitest src/test" }, "tsup": { "outDir": "build", @@ -32,9 +32,7 @@ "dts": true, "clean": true }, - "jest": { - "preset": "ts-jest", - "testEnvironment": "node", - "testPathIgnorePatterns": ["node_modules/", "build/"] + "eslintConfig": { + "extends": "@pkmn" } -} +} \ No newline at end of file diff --git a/spreads/src/common.ts b/spreads/src/common.ts index ea688f5..68ba065 100644 --- a/spreads/src/common.ts +++ b/spreads/src/common.ts @@ -1,5 +1,6 @@ import {GenerationNum, StatID} from '@pkmn/types'; -import {STATS, NATURES, GEN, Generation} from './data'; + +import {GEN, Generation, NATURES, STATS} from './data'; const STAT_ORDER = ['hp', 'atk', 'def', 'spa', 'spd', 'spe'] as const; const RBY_STAT_ORDER = ['hp', 'atk', 'def', 'spa', 'spe'] as const; @@ -9,7 +10,7 @@ export interface Range { max: T; } -export function isRange(r: unknown | Range): r is Range { +export function isRange(r: unknown): r is Range { return 'min' in (r as Range); } diff --git a/spreads/src/data.ts b/spreads/src/data.ts index 3e1924b..ae2a671 100644 --- a/spreads/src/data.ts +++ b/spreads/src/data.ts @@ -1,5 +1,5 @@ // This code is cribbed from @pkmn/data and @pkmn/dex: https://github.com/pkmn/ps -import {StatsTable, NatureName, StatID, GenerationNum, ID} from '@pkmn/types'; +import {GenerationNum, ID, NatureName, StatID, StatsTable} from '@pkmn/types'; export function toID(text: any): ID { if (text?.id) text = text.id; diff --git a/spreads/src/spread-range.ts b/spreads/src/spread-range.ts index 46190a7..5e6a1a1 100644 --- a/spreads/src/spread-range.ts +++ b/spreads/src/spread-range.ts @@ -1,15 +1,15 @@ -import {StatsTable, GenerationNum, NatureName, StatID} from '@pkmn/types'; +import {GenerationNum, NatureName, StatID, StatsTable} from '@pkmn/types'; import { + Range, displayRange, getNatureFromPlusMinus, isRange, parseRange, - Range, statOrder, } from './common'; -import {GEN, STATS, NATURES, Generation} from './data'; -import {Spread, SpreadTable, SpreadDisplayOptions} from './spread'; +import {GEN, Generation, NATURES, STATS} from './data'; +import {Spread, SpreadDisplayOptions, SpreadTable} from './spread'; import {StatsRange} from './stats-range'; // eslint-disable-next-line max-len diff --git a/spreads/src/spread.ts b/spreads/src/spread.ts index 3462f7b..3dba81a 100644 --- a/spreads/src/spread.ts +++ b/spreads/src/spread.ts @@ -1,8 +1,8 @@ -import {StatsTable, NatureName} from '@pkmn/types'; +import {NatureName, StatsTable} from '@pkmn/types'; -import {STATS, NATURES, GEN, Generation} from './data'; -import {Stats} from './stats'; +import {GEN, Generation, NATURES, STATS} from './data'; import {SpreadRange} from './spread-range'; +import {Stats} from './stats'; export interface SpreadTable { nature?: NatureName; diff --git a/spreads/src/stats-range.ts b/spreads/src/stats-range.ts index 2cb7889..d280b40 100644 --- a/spreads/src/stats-range.ts +++ b/spreads/src/stats-range.ts @@ -1,18 +1,18 @@ import {StatsTable} from '@pkmn/types'; import { + Range, StatsDisplayOptions, displayRange, displayStats, getNatureFromPlusMinus, isRange, parseRange, - Range, statOrder, } from './common'; -import {STATS, Generation, GEN} from './data'; -import {Stats} from './stats'; +import {GEN, Generation, STATS} from './data'; import {SpreadRange} from './spread-range'; +import {Stats} from './stats'; export class StatsRange implements Range { min: Stats; diff --git a/spreads/src/stats.ts b/spreads/src/stats.ts index 8ced6ae..748d292 100644 --- a/spreads/src/stats.ts +++ b/spreads/src/stats.ts @@ -1,7 +1,7 @@ -import {StatsTable, StatID, GenerationNum} from '@pkmn/types'; +import {GenerationNum, StatID, StatsTable} from '@pkmn/types'; import {StatsDisplayOptions, displayStats, getNatureFromPlusMinus} from './common'; -import {NATURES, STATS, GEN, Generation, Nature} from './data'; +import {GEN, Generation, NATURES, Nature, STATS} from './data'; import {Spread} from './spread'; import {StatsRange} from './stats-range'; diff --git a/spreads/src/test/runner.ts b/spreads/src/test/runner.ts index fad553e..a10128c 100644 --- a/spreads/src/test/runner.ts +++ b/spreads/src/test/runner.ts @@ -2,9 +2,9 @@ import assert from 'assert'; import {GenerationNum, StatsTable} from '@pkmn/types'; -import {Stats} from '../stats'; -import {Spread} from '../spread'; import * as data from '../data'; +import {Spread} from '../spread'; +import {Stats} from '../stats'; class Random { seed: number; diff --git a/spreads/src/test/spread-range.test.ts b/spreads/src/test/spread-range.test.ts index ee6d50d..ebbd5c8 100644 --- a/spreads/src/test/spread-range.test.ts +++ b/spreads/src/test/spread-range.test.ts @@ -1,4 +1,5 @@ import {NatureName} from '@pkmn/types'; + import {SpreadRange} from '../spread-range'; const RANGE = new SpreadRange({ diff --git a/spreads/src/test/stats-range.test.ts b/spreads/src/test/stats-range.test.ts index 95f55b7..a4e2972 100644 --- a/spreads/src/test/stats-range.test.ts +++ b/spreads/src/test/stats-range.test.ts @@ -1,7 +1,7 @@ /* eslint-disable jest/prefer-to-contain */ +import {Spread} from '../spread'; import {Stats} from '../stats'; import {StatsRange} from '../stats-range'; -import {Spread} from '../spread'; const RANGE = new StatsRange({ min: {hp: 360, atk: 367, def: 250, spa: 203, spd: 235, spe: 180}, diff --git a/spreads/src/test/stats.test.ts b/spreads/src/test/stats.test.ts index 03cd7cb..8eb9ab8 100644 --- a/spreads/src/test/stats.test.ts +++ b/spreads/src/test/stats.test.ts @@ -1,6 +1,6 @@ /* eslint-disable jest/no-conditional-expect */ -import {Stats, statToEV, statToIV} from '../stats'; import * as data from '../data'; +import {Stats, statToEV, statToIV} from '../stats'; import {run} from './runner'; diff --git a/spreads/tsconfig.json b/spreads/tsconfig.json index 61b458c..36e101e 100644 --- a/spreads/tsconfig.json +++ b/spreads/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "outDir": "build", "tsBuildInfoFile": ".tsbuildinfo" }, "include": ["src/**/*"] diff --git a/spreads/vitest.config.ts b/spreads/vitest.config.ts new file mode 100644 index 0000000..dbfd933 --- /dev/null +++ b/spreads/vitest.config.ts @@ -0,0 +1,9 @@ +import {defineConfig} from 'vitest/config'; + +export default defineConfig({ + test: { + watch: false, + globals: true, + exclude: ['node_modules', 'build'], + }, +}); diff --git a/tsconfig.json b/tsconfig.json index b5ae039..524bccf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "incremental": true, "lib": ["es2016", "dom"], "module": "commonjs", - "noEmit": true, + "noEmitOnError": true, "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, "sourceMap": true,