diff --git a/src/worker/core/player/fuzzRating.ts b/src/worker/core/player/fuzzRating.ts index 7610ace3ea..011ee6e1f3 100644 --- a/src/worker/core/player/fuzzRating.ts +++ b/src/worker/core/player/fuzzRating.ts @@ -1,6 +1,27 @@ +import { PHASE } from "../../../common"; import { g, helpers } from "../../util"; -const fuzzRating = (rating: number, fuzz: number): number => { +// Generate plus minus from ovr +const getO2PM = (ovr: number) => { + const iv = [72.7, 7.28 * 2, 18.1 / 2]; + const minVal = -2.021381747657189; + return minVal + iv[1] / (1 + Math.exp(-(ovr - iv[0]) / iv[2])); +}; +// get ovr from plus minus +const getPM2O = (pm: number) => { + const iv = [72.7, 7.28 * 2, 18.1 / 2]; + const minVal = -2.021381747657189; + //clip plus minus, don't send players to 0 lol + const pmC = Math.min(iv[1] + minVal, Math.max(minVal + 0.11, pm)); + // invert the sigmoid above + const ovr = iv[0] - iv[2] * Math.log(iv[1] / (pmC - minVal) - 1); + // clip overall + const ovrC = Math.min(100, Math.max(0, ovr)); + + return ovrC; +}; + +const fuzzRating = (rating: number, fuzz: number, isDraft = false): number => { // Turn off fuzz in multi team mode, because it doesn't have any meaning there in its current form. The check for // existence of variables is because this is sometimes called in league upgrade code when g is not available and // would be difficult to make available due to Firefox promise/IDB/worker issues. @@ -8,10 +29,24 @@ const fuzzRating = (rating: number, fuzz: number): number => { (g.hasOwnProperty("userTids") && g.get("userTids").length > 1) || g.get("godMode") ) { - fuzz = 0; + return rating; } - return Math.round(helpers.bound(rating + fuzz, 0, 100)); + let frac_left = 1; + + // this shouldn't happen for draft prospects. + if (!isDraft) { + // would love to use number of days; + //const numDays = season.getDaysLeftSchedule(); + //const numTotal = g.get("numGames"); + + frac_left = g.get("phase") >= PHASE.PLAYOFFS ? 0 : 1; + } + + // fuzz is -5 to 5. Let's assume it's really -1 to 1 in +/- terms + const fuzzed = getPM2O(getO2PM(rating) + (frac_left * fuzz) / 5); + + return Math.round(helpers.bound(fuzzed, 0, 100)); }; export default fuzzRating; diff --git a/src/worker/db/getCopies/playersPlus.test.ts b/src/worker/db/getCopies/playersPlus.test.ts index ff946c0183..b02ff54db9 100644 --- a/src/worker/db/getCopies/playersPlus.test.ts +++ b/src/worker/db/getCopies/playersPlus.test.ts @@ -299,10 +299,11 @@ describe("worker/db/getCopies/playersPlus", () => { } // This will break if ovr + fuzz is over 100 (should check bounds), but that never happens in practice - assert.strictEqual( - pf.ratings.ovr, - Math.round(p.ratings[1].ovr + p.ratings[1].fuzz), - ); + // fuzz is not this simple anymore + //assert.strictEqual( + // pf.ratings.ovr, + // Math.round(p.ratings[1].ovr + p.ratings[1].fuzz), + //); }); test("return stats from previous season if options.oldStats is true and current season has no stats record", async () => {