From bdbc0073a86da30a5a8145933585384e692f5dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Wed, 26 Jun 2024 14:07:27 -0500 Subject: [PATCH] feat: start of math --- scripts/averages.ts | 120 +++++++++++++--------------------------- src/types/tanksStats.ts | 21 +------ 2 files changed, 40 insertions(+), 101 deletions(-) diff --git a/scripts/averages.ts b/scripts/averages.ts index 82b240768..592d2ebf0 100644 --- a/scripts/averages.ts +++ b/scripts/averages.ts @@ -16,6 +16,12 @@ import { superCompress } from '../src/core/blitzkit/superCompress'; import { DidsReadStream, DidsWriteStream } from '../src/core/streams/dids'; import { IndividualTankStats } from '../src/types/tanksStats'; +interface DataPoint { + x: number; + y: number; + w: number; +} + const RUN_TIME = 1000 * 60 * 60; const MAX_REQUESTS = 10; @@ -138,17 +144,23 @@ function discover() { function postWork() { const tankIds: number[] = []; - const sorted: Record = {}; + const sorted: Record = {}; + + console.log('Sorting tanks...'); players.forEach((tanks) => { tanks.forEach((tank) => { if (tank.all.battles === 0) return; - if (!sorted[tank.tank_id]) { + if (!tankIds.includes(tank.tank_id)) { tankIds.push(tank.tank_id); sorted[tank.tank_id] = []; } - sorted[tank.tank_id].push(tank); + + sorted[tank.tank_id].push({ + ...tank.all, + battle_life_time: tank.battle_life_time, + }); }); }); @@ -160,89 +172,33 @@ function postWork() { tankIds.forEach((id) => { const tanks = sorted[id]; - const samples = tanks.length; - function sum(slice: (tank: AverageDefinitionsAllStats) => number) { - return tanks.reduce((acc, tank) => { - return ( - acc + slice({ battle_life_time: tank.battle_life_time, ...tank.all }) + averageDefinitionsAllStatsKeys.forEach((key) => { + const data: DataPoint[] = tanks.map((tank) => ({ + w: tank.battles, + x: tank[key] / tank.battles, + y: tank.wins / tank.battles, + })); + + function sum(slicer: (data: DataPoint) => number) { + return data.reduce( + (accumulator, data) => accumulator + slicer(data), + 0, ); - }, 0); - } + } - const denominator = sum((tank) => tank.battles); + const sum_w = sum(({ w }) => w); + const sum_wx = sum(({ w, x }) => w * x); + const sum_wy = sum(({ w, y }) => w * y); + const x_w = sum_wx / sum_w; + const y_w = sum_wy / sum_w; + const r_numerator = sum(({ w, x, y }) => w * (x - x_w) * (y - y_w)); + const r_denominator_x = sum(({ w, x }) => w * (x - x_w) ** 2); + const r_denominator_y = sum(({ w, y }) => w * (y - y_w) ** 2); - function moment(slice: (tank: AverageDefinitionsAllStats) => number) { - return tanks.reduce((acc, tank) => { - return ( - acc + - tank.all.battles * - slice({ battle_life_time: tank.battle_life_time, ...tank.all }) - ); - }, 0); - } - - function weightedAverage( - slice: (tank: AverageDefinitionsAllStats) => number, - ) { - return moment(slice) / denominator; - } - - const mu = averageDefinitionsAllStatsKeys.reduce< - Partial - >((acc, key) => { - acc[key] = weightedAverage((tank) => tank[key]); - return acc; - }, {}) as AverageDefinitionsAllStats; - const sigma = averageDefinitionsAllStatsKeys.reduce< - Partial - >((acc, key) => { - acc[key] = Math.sqrt(sum((tank) => (tank[key] - mu[key]) ** 2) / samples); - return acc; - }, {}) as AverageDefinitionsAllStats; - const r = averageDefinitionsAllStatsKeys.reduce< - Partial - >((acc, key) => { - acc[key] = - (samples * - sum( - (tank) => (tank[key] / tank.battles) * (tank.wins / tank.battles), - ) - - sum((tank) => tank[key] / tank.battles) * - sum((tank) => tank.wins / tank.battles)) / - Math.sqrt( - (samples * sum((tank) => (tank[key] / tank.battles) ** 2) - - sum((tank) => tank[key] / tank.battles) ** 2) * - (samples * sum((tank) => (tank.wins / tank.battles) ** 2) - - sum((tank) => tank.wins / tank.battles) ** 2), - ); - return acc; - }, {}) as AverageDefinitionsAllStats; - const m = averageDefinitionsAllStatsKeys.reduce< - Partial - >((acc, key) => { - acc[key] = - (samples * - sum( - (tank) => (tank[key] / tank.battles) * (tank.wins / tank.battles), - ) - - sum((tank) => tank[key] / tank.battles) * - sum((tank) => tank.wins / tank.battles)) / - (samples * sum((tank) => (tank[key] / tank.battles) ** 2) - - sum((tank) => tank[key] / tank.battles) ** 2); - return acc; - }, {}) as AverageDefinitionsAllStats; - const b = averageDefinitionsAllStatsKeys.reduce< - Partial - >((acc, key) => { - acc[key] = - (sum((tank) => tank.wins / tank.battles) - - m[key] * sum((tank) => tank[key] / tank.battles)) / - samples; - return acc; - }, {}) as AverageDefinitionsAllStats; - - averages[id] = { samples, mu, sigma, r, m, b }; + const r = r_numerator / Math.sqrt(r_denominator_x * r_denominator_y); + const mu = x_w; + }); }); commitAssets( diff --git a/src/types/tanksStats.ts b/src/types/tanksStats.ts index 73c73fda9..dcff4a22e 100644 --- a/src/types/tanksStats.ts +++ b/src/types/tanksStats.ts @@ -1,4 +1,5 @@ import { AllStats } from '../core/blitz/getAccountInfo'; +import { emptyAllStats } from '../core/blitzstars/getStatsInPeriod/constants'; export interface IndividualTankStats { all: AllStats; @@ -23,25 +24,7 @@ export interface NormalizedTankStats { } export const emptyIndividualTankStats: IndividualTankStats = { - all: { - battles: 0, - capture_points: 0, - damage_dealt: 0, - damage_received: 0, - dropped_capture_points: 0, - frags: 0, - frags8p: 0, - hits: 0, - losses: 0, - max_frags: 0, - max_xp: 0, - shots: 0, - spotted: 0, - survived_battles: 0, - xp: 0, - win_and_survived: 0, - wins: 0, - }, + all: emptyAllStats, last_battle_time: 0, account_id: 0, max_xp: 0,