diff --git a/app/core/mmr/utils.ts b/app/core/mmr/utils.ts index 36671de8b..6b0d4ec17 100644 --- a/app/core/mmr/utils.ts +++ b/app/core/mmr/utils.ts @@ -1,5 +1,5 @@ import clone from "just-clone"; -import { expose, rate, Rating } from "ts-trueskill"; +import { rating, ordinal, rate } from "openskill"; import { LFG_GROUP_FULL_SIZE, MMR_TOPX_VISIBILITY_CUTOFF } from "~/constants"; import { PlayFrontPageLoader } from "~/routes/play/index"; @@ -17,7 +17,7 @@ export function skillArrayToMMR( } export function muSigmaToSP(skill: { mu: number; sigma: number }) { - return toTwoDecimals(expose(new Rating(skill.mu, skill.sigma)) * 10 + 1000); + return toTwoDecimals(ordinal(rating(skill)) * 10 + 1000); } interface TeamSkill { @@ -37,7 +37,7 @@ export function teamSkillToExactMMR(teamSkills: TeamSkill[]) { teamSkillsClone.push({ user: { skill: [] } }); } - const defaultRating = new Rating(); + const defaultRating = rating(); const skillsWithDefaults = teamSkillsClone.reduce((acc: TeamSkill[], cur) => { if (cur.user.skill.length === 0) { return [ @@ -84,9 +84,9 @@ export function adjustSkills({ }): AdjustSkill[] { const mapToRatings = (id: string) => { const skill = skills.find((s) => s.userId === id); - if (!skill) return new Rating(); + if (!skill) return rating(); - return new Rating(skill.mu, skill.sigma); + return rating(skill); }; const winningTeam = playerIds.winning.map(mapToRatings); const losingTeam = playerIds.losing.map(mapToRatings); diff --git a/openskill.d.ts b/openskill.d.ts new file mode 100644 index 000000000..2276acd48 --- /dev/null +++ b/openskill.d.ts @@ -0,0 +1,10 @@ +type Rating = { + mu: number; + sigma: number; +}; + +declare module "openskill" { + export function rating(rating?: Rating): Rating; + export function ordinal(rating: Rating): number; + export function rate(ratings: [Rating[], Rating[]]): [Rating[], Rating[]]; +} diff --git a/package-lock.json b/package-lock.json index 668734aef..d79acb03d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "just-clone": "^5.0.1", "just-shuffle": "^4.0.1", "morgan": "^1.10.0", + "openskill": "^2.1.0", "passport": "^0.5.2", "passport-discord": "^0.1.4", "react": "^17.0.2", @@ -36,7 +37,6 @@ "socket.io": "^4.4.1", "socket.io-client": "^4.4.1", "tiny-invariant": "^1.2.0", - "ts-trueskill": "^3.3.0", "uuid": "^8.3.2", "zod": "^3.14.2" }, @@ -2787,18 +2787,6 @@ "node": ">=4.0.0" } }, - "node_modules/complex.js": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.15.tgz", - "integrity": "sha512-gDBvQU8IG139ZBQTSo2qvDFP+lANMGluM779csXOr6ny1NUtA3wkUnCFjlDNH/moAVfXtvClYt6G0zarFbtz5w==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -3348,11 +3336,6 @@ "node": ">=0.10.0" } }, - "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, "node_modules/decode-named-character-reference": { "version": "1.0.0", "dev": true, @@ -4214,11 +4197,6 @@ "version": "1.0.3", "license": "MIT" }, - "node_modules/escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" - }, "node_modules/escape-string-regexp": { "version": "1.0.5", "dev": true, @@ -5119,18 +5097,6 @@ "node": ">= 0.6" } }, - "node_modules/fraction.js": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", - "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, "node_modules/fresh": { "version": "0.5.2", "license": "MIT", @@ -5203,6 +5169,14 @@ "node": ">=10" } }, + "node_modules/gaussian": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gaussian/-/gaussian-1.2.0.tgz", + "integrity": "sha512-Pz/InFmDZ6VqCbXF+2+O6USIbPUPWhJ7DnqrZKfo9mMHszCd4pBad3WCVa9ruzzrBizYonsv0ZBmuv4rR8NRQg==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -6393,11 +6367,6 @@ "dev": true, "license": "MIT" }, - "node_modules/javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" - }, "node_modules/js-tokens": { "version": "4.0.0", "license": "MIT" @@ -6966,28 +6935,6 @@ "node": ">=0.10.0" } }, - "node_modules/mathjs": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.1.0.tgz", - "integrity": "sha512-TrpZAR3H9jR0Cv6cnzT+TZhE40Xs2SCaLf/qm2WcWm2tui69Gas/bC/ct5ZLZNWnWvNvJ7H2uHvuRDDl151PHA==", - "dependencies": { - "@babel/runtime": "^7.16.5", - "complex.js": "^2.0.15", - "decimal.js": "^10.3.1", - "escape-latex": "^1.2.0", - "fraction.js": "^4.1.2", - "javascript-natural-sort": "^0.7.1", - "seedrandom": "^3.0.5", - "tiny-emitter": "^2.1.0", - "typed-function": "^2.0.0" - }, - "bin": { - "mathjs": "bin/cli.js" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/mathml-tag-names": { "version": "2.1.3", "dev": true, @@ -8516,6 +8463,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openskill": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/openskill/-/openskill-2.1.0.tgz", + "integrity": "sha512-ViZXEFF6agXt4I50zJY51iRUpSdcTFCA/Dl8SMznnzvZ+IlnFWjttDA+lK+/C6cRfp9PwuoqfVstGJAMuubE8A==", + "dependencies": { + "gaussian": "1.2.0", + "ramda": "0.28.0", + "sort-unwind": "1.0.0" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -9400,6 +9357,15 @@ "node": ">=8" } }, + "node_modules/ramda": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz", + "integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, "node_modules/range-parser": { "version": "1.2.1", "license": "MIT", @@ -9933,11 +9899,6 @@ "object-assign": "^4.1.1" } }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, "node_modules/semver": { "version": "7.3.5", "dev": true, @@ -10294,6 +10255,19 @@ "node": ">=8" } }, + "node_modules/sort-unwind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sort-unwind/-/sort-unwind-1.0.0.tgz", + "integrity": "sha512-gmiZzA2UMO16/fV+oiGI3TV++pWBtlyrTmqOsgOSxEAFmud6gzS2SWtxjDHcI/eAxaKFNHj9NwSKnLOBgr/UGw==", + "dependencies": { + "ramda": "0.27.1" + } + }, + "node_modules/sort-unwind/node_modules/ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" + }, "node_modules/source-map": { "version": "0.7.3", "license": "BSD-3-Clause", @@ -11069,11 +11043,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "node_modules/tiny-invariant": { "version": "1.2.0", "license": "MIT" @@ -11186,14 +11155,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/ts-gaussian": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ts-gaussian/-/ts-gaussian-2.0.2.tgz", - "integrity": "sha512-DdMwQc1bgzRwbKQ3VoiV3aK2isz7CJhOmtO2oA+mp9soGpbN/p2pkBaIrZnJBW3jB293CUARLFmNktcTSHjypQ==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/ts-node": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", @@ -11245,19 +11206,6 @@ "node": ">=0.3.1" } }, - "node_modules/ts-trueskill": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ts-trueskill/-/ts-trueskill-3.3.0.tgz", - "integrity": "sha512-yGZQ9d4xIZg7HBKOnO70Qz//M/roPmw1p6cFFGcCtmWQlU6KMAoVzeScX60N70dD2ZvBxDTXXQFhaWr66zdiYQ==", - "dependencies": { - "mathjs": "^10.1.0", - "ts-gaussian": "^2.0.2", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -11374,14 +11322,6 @@ "node": ">= 0.6" } }, - "node_modules/typed-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.0.0.tgz", - "integrity": "sha512-Hhy1Iwo/e4AtLZNK10ewVVcP2UEs408DS35ubP825w/YgSBK1KVLwALvvIG4yX75QJrxjCpcWkzkVRB0BwwYlA==", - "engines": { - "node": ">= 8" - } - }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -14151,11 +14091,6 @@ "version": "1.8.2", "dev": true }, - "complex.js": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.15.tgz", - "integrity": "sha512-gDBvQU8IG139ZBQTSo2qvDFP+lANMGluM779csXOr6ny1NUtA3wkUnCFjlDNH/moAVfXtvClYt6G0zarFbtz5w==" - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -14534,11 +14469,6 @@ } } }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, "decode-named-character-reference": { "version": "1.0.0", "dev": true, @@ -15084,11 +15014,6 @@ "escape-html": { "version": "1.0.3" }, - "escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" - }, "escape-string-regexp": { "version": "1.0.5", "dev": true @@ -15723,11 +15648,6 @@ "forwarded": { "version": "0.2.0" }, - "fraction.js": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", - "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==" - }, "fresh": { "version": "0.5.2" }, @@ -15776,6 +15696,11 @@ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.5.3.tgz", "integrity": "sha512-sA5etGE7yD/pOqivZRBvUBd/NaL2sjAu6QuSaFoe1H2BrJSkH/T/UXAJ8CdXdw7DvY3Hs8CXKYkDWX7RiP5KOg==" }, + "gaussian": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gaussian/-/gaussian-1.2.0.tgz", + "integrity": "sha512-Pz/InFmDZ6VqCbXF+2+O6USIbPUPWhJ7DnqrZKfo9mMHszCd4pBad3WCVa9ruzzrBizYonsv0ZBmuv4rR8NRQg==" + }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -16526,11 +16451,6 @@ "version": "0.1.2", "dev": true }, - "javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" - }, "js-tokens": { "version": "4.0.0" }, @@ -16918,22 +16838,6 @@ "version": "1.1.1", "dev": true }, - "mathjs": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.1.0.tgz", - "integrity": "sha512-TrpZAR3H9jR0Cv6cnzT+TZhE40Xs2SCaLf/qm2WcWm2tui69Gas/bC/ct5ZLZNWnWvNvJ7H2uHvuRDDl151PHA==", - "requires": { - "@babel/runtime": "^7.16.5", - "complex.js": "^2.0.15", - "decimal.js": "^10.3.1", - "escape-latex": "^1.2.0", - "fraction.js": "^4.1.2", - "javascript-natural-sort": "^0.7.1", - "seedrandom": "^3.0.5", - "tiny-emitter": "^2.1.0", - "typed-function": "^2.0.0" - } - }, "mathml-tag-names": { "version": "2.1.3", "dev": true @@ -17859,6 +17763,16 @@ "mimic-fn": "^2.1.0" } }, + "openskill": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/openskill/-/openskill-2.1.0.tgz", + "integrity": "sha512-ViZXEFF6agXt4I50zJY51iRUpSdcTFCA/Dl8SMznnzvZ+IlnFWjttDA+lK+/C6cRfp9PwuoqfVstGJAMuubE8A==", + "requires": { + "gaussian": "1.2.0", + "ramda": "0.28.0", + "sort-unwind": "1.0.0" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -18461,6 +18375,11 @@ "version": "4.0.1", "dev": true }, + "ramda": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz", + "integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==" + }, "range-parser": { "version": "1.2.1" }, @@ -18850,11 +18769,6 @@ "object-assign": "^4.1.1" } }, - "seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, "semver": { "version": "7.3.5", "dev": true, @@ -19124,6 +19038,21 @@ } } }, + "sort-unwind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sort-unwind/-/sort-unwind-1.0.0.tgz", + "integrity": "sha512-gmiZzA2UMO16/fV+oiGI3TV++pWBtlyrTmqOsgOSxEAFmud6gzS2SWtxjDHcI/eAxaKFNHj9NwSKnLOBgr/UGw==", + "requires": { + "ramda": "0.27.1" + }, + "dependencies": { + "ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" + } + } + }, "source-map": { "version": "0.7.3" }, @@ -19687,11 +19616,6 @@ } } }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "tiny-invariant": { "version": "1.2.0" }, @@ -19768,11 +19692,6 @@ "version": "2.0.2", "dev": true }, - "ts-gaussian": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ts-gaussian/-/ts-gaussian-2.0.2.tgz", - "integrity": "sha512-DdMwQc1bgzRwbKQ3VoiV3aK2isz7CJhOmtO2oA+mp9soGpbN/p2pkBaIrZnJBW3jB293CUARLFmNktcTSHjypQ==" - }, "ts-node": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", @@ -19800,16 +19719,6 @@ } } }, - "ts-trueskill": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ts-trueskill/-/ts-trueskill-3.3.0.tgz", - "integrity": "sha512-yGZQ9d4xIZg7HBKOnO70Qz//M/roPmw1p6cFFGcCtmWQlU6KMAoVzeScX60N70dD2ZvBxDTXXQFhaWr66zdiYQ==", - "requires": { - "mathjs": "^10.1.0", - "ts-gaussian": "^2.0.2", - "uuid": "^8.3.2" - } - }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -19889,11 +19798,6 @@ "mime-types": "~2.1.24" } }, - "typed-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.0.0.tgz", - "integrity": "sha512-Hhy1Iwo/e4AtLZNK10ewVVcP2UEs408DS35ubP825w/YgSBK1KVLwALvvIG4yX75QJrxjCpcWkzkVRB0BwwYlA==" - }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", diff --git a/package.json b/package.json index dce55fd1c..7d53b21aa 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "just-clone": "^5.0.1", "just-shuffle": "^4.0.1", "morgan": "^1.10.0", + "openskill": "^2.1.0", "passport": "^0.5.2", "passport-discord": "^0.1.4", "react": "^17.0.2", @@ -56,7 +57,6 @@ "socket.io": "^4.4.1", "socket.io-client": "^4.4.1", "tiny-invariant": "^1.2.0", - "ts-trueskill": "^3.3.0", "uuid": "^8.3.2", "zod": "^3.14.2" },