"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var _a, _Pool_config; Object.defineProperty(exports, "__esModule", { value: true }); exports.Pool = void 0; const defaultConfig = { DMax: 400, initialRating: 1500, kGenerator: ({ matchCount }) => { if (matchCount < 30) { return 32; } return 24; }, propsKey: "elo", }; const full = (config) => (Object.assign(Object.assign({}, defaultConfig), config)); const defaultElo = (initialRating) => ({ rating: initialRating, lastDelta: NaN, lastPlayedAt: NaN, matchCount: 0, }); const elo = (config = {}) => { const { DMax, initialRating, kGenerator, propsKey } = full(config); const dummy = { [propsKey]: defaultElo(initialRating) }; return (a) => { var _b; const eloA = (_b = a[propsKey]) !== null && _b !== void 0 ? _b : dummy[propsKey]; const oddsAgainst = (b) => { var _b; const eloB = (_b = b[propsKey]) !== null && _b !== void 0 ? _b : dummy[propsKey]; const clamp = (value) => ({ between: (lower, upper) => Math.max(Math.min(value, upper), lower), }); const D = clamp(eloA.rating - eloB.rating).between(-DMax, DMax); return 1 / (1 + Math.pow(10, (-D / DMax))); }; const resolveMatch = (didAWin) => (b) => { var _b; const eloB = (_b = b[propsKey]) !== null && _b !== void 0 ? _b : dummy[propsKey]; const metaA = { k: kGenerator(eloA), didWin: didAWin, p: oddsAgainst(b), }; const metaB = { k: kGenerator(eloB), didWin: 1 - didAWin, p: 1 - metaA.p, }; const from = ({ rating: oldRating, matchCount }, { k, didWin, p }, playedAt) => { const newRating = oldRating + k * (didWin - p); return { rating: newRating, matchCount: matchCount + 1, lastDelta: newRating - oldRating, lastPlayedAt: playedAt, }; }; const playedAt = Date.now(); return [ Object.assign(Object.assign({}, a), { [propsKey]: from(eloA, metaA, playedAt) }), Object.assign(Object.assign({}, b), { [propsKey]: from(eloB, metaB, playedAt) }), ]; }; const reset = () => { const _b = a, _c = propsKey, elo = _b[_c], cleanedA = __rest(_b, [typeof _c === "symbol" ? _c : _c + ""]); return cleanedA; }; return { wins: resolveMatch(1), ties: resolveMatch(0.5), loses: resolveMatch(0), oddsAgainst, reset, }; }; }; class Pool { static config(config) { __classPrivateFieldSet(_a, _a, config, "f", _Pool_config); return _a; } static from(iterable) { const { initialRating, propsKey } = full(__classPrivateFieldGet(this, _a, "f", _Pool_config)); const player = elo(__classPrivateFieldGet(this, _a, "f", _Pool_config)); const dummy = { [propsKey]: defaultElo(initialRating) }; iterable.player = (indexA) => { const playerA = player(iterable[indexA]); const resolveMatch = (indexB, resolver) => { const [newA, newB] = playerA[resolver](iterable[indexB]); iterable[indexA] = newA; iterable[indexB] = newB; return iterable; }; return { wins: (indexB) => resolveMatch(indexB, "wins"), ties: (indexB) => resolveMatch(indexB, "ties"), loses: (indexB) => resolveMatch(indexB, "loses"), oddsAgainst: (indexB) => { return playerA.oddsAgainst(iterable[indexB]); }, reset: () => { iterable[indexA] = playerA.reset(); return iterable; }, }; }; iterable.pick = (forcedMethod) => { var _b, _c; const methods = ["random", "matchCount", "lastPlayedAt"]; const pickRandom = (min, max) => min + Math.floor(Math.random() * (max - min + 1)); const method = forcedMethod !== null && forcedMethod !== void 0 ? forcedMethod : methods[pickRandom(0, methods.length - 1)]; if (iterable.length <= 1) { throw new Error("not enough players"); } if (iterable.length === 2) { return [0, 1, method]; // should be handled explicitly because of random option } switch (method) { case "random": { const i = pickRandom(0, iterable.length - 1); const j = (i + pickRandom(1, iterable.length - 1)) % iterable.length; return [i, j, method]; } default: { const fromProp = (key) => iterable.reduce(([first, second, third], challenger, index) => { var _b; const challengerStats = (_b = challenger[propsKey]) !== null && _b !== void 0 ? _b : dummy[propsKey]; if (challengerStats[key] < first[key]) { return [Object.assign({ index }, challengerStats), first, second]; } if (challengerStats[key] < second[key]) { return [first, Object.assign({ index }, challengerStats), second]; } if (challengerStats[key] < third[key]) { return [first, second, Object.assign({ index }, challengerStats)]; } return [first, second, third]; }, [{ [key]: Infinity }, { [key]: Infinity }, { [key]: Infinity }]); let [a, b, c] = fromProp(method); if (method === "matchCount" && a.lastPlayedAt === c.lastPlayedAt) { [c, b] = [b, c]; } return [(_b = a.index) !== null && _b !== void 0 ? _b : 0, (_c = c.index) !== null && _c !== void 0 ? _c : 1, method]; // i and j may be undefined if all lastPlayedAt are NaN } } }; return iterable; } } exports.Pool = Pool; _a = Pool; _Pool_config = { value: defaultConfig }; exports.default = elo;