2024-06-29 19:43:46 -05:00
import { chunk , times } from 'lodash' ;
2024-05-19 17:50:01 -05:00
import { argv } from 'process' ;
2024-05-18 21:12:11 -05:00
import { Region , REGIONS } from '../src/constants/regions' ;
2024-06-28 14:12:58 -05:00
import { getAccountInfo } from '../src/core/blitz/getAccountInfo' ;
import getTankStats from '../src/core/blitz/getTankStats' ;
2024-06-26 21:22:03 -05:00
import { idToRegion } from '../src/core/blitz/idToRegion' ;
2024-05-19 17:50:01 -05:00
import {
AverageDefinitions ,
AverageDefinitionsAllStats ,
2024-06-26 21:07:54 -05:00
AverageDefinitionsEntry ,
AverageDefinitionsEntrySubPartial ,
2024-05-19 17:50:01 -05:00
} from '../src/core/blitzkit/averageDefinitions' ;
import { averageDefinitionsAllStatsKeys } from '../src/core/blitzkit/averageDefinitions/constants' ;
2024-06-28 20:08:50 -05:00
import { commitAssets } from '../src/core/blitzkit/commitAssets' ;
2024-06-26 21:22:03 -05:00
import { fetchPreDiscoveredIds } from '../src/core/blitzkit/fetchPreDiscoveredIds' ;
2024-06-28 20:08:50 -05:00
import { encodeToBase64 } from '../src/core/protobuf/encodeToBase64' ;
2024-05-18 21:12:11 -05:00
2024-06-26 14:07:27 -05:00
interface DataPoint {
x : number ;
y : number ;
w : number ;
}
2024-06-28 14:12:58 -05:00
const MAX_ACTIVITY_TIME = 1000 * 60 * 60 * 24 * 120 ;
const MIN_BATTLES = 5000 ;
2024-06-28 20:08:50 -05:00
const RUN_TIME = 1000 * 60 * 60 * 5.9 ;
2024-06-28 14:12:58 -05:00
const THREADS = 10 ;
const PLAYER_IDS_PER_CALL = 100 ;
2024-05-18 21:12:11 -05:00
2024-06-28 14:12:58 -05:00
const startTime = Date . now ();
2024-05-19 17:50:01 -05:00
const production = argv . includes ( '--production' );
2024-06-26 21:22:03 -05:00
const preDiscoveredIds = await fetchPreDiscoveredIds ( ! production );
2024-06-29 19:43:46 -05:00
const playerIds : Record < Region , number [][] > = {
asia : chunk (
preDiscoveredIds . filter (( id ) => idToRegion ( id ) === 'asia' ),
PLAYER_IDS_PER_CALL ,
),
com : chunk (
preDiscoveredIds . filter (( id ) => idToRegion ( id ) === 'com' ),
PLAYER_IDS_PER_CALL ,
),
eu : chunk (
preDiscoveredIds . filter (( id ) => idToRegion ( id ) === 'eu' ),
PLAYER_IDS_PER_CALL ,
),
2024-06-26 21:22:03 -05:00
};
let regionIndex = 0 ;
2024-06-28 20:08:50 -05:00
let checkedPlayers = 0 ;
let includedPlayers = 0 ;
2024-06-26 21:22:03 -05:00
const availableRegions = [... REGIONS ];
2024-06-28 14:12:58 -05:00
const tankIds : number [] = [];
const tanksSorted : Record < number , AverageDefinitionsAllStats [] > = {};
let postWorkRequested = false ;
2024-05-19 17:50:01 -05:00
2024-06-28 14:12:58 -05:00
times ( THREADS , async () => {
while ( availableRegions . length > 0 && startTime + RUN_TIME > Date . now ()) {
const region = availableRegions [ regionIndex ];
2024-06-29 10:49:32 -05:00
const regionIds = playerIds [ region ];
2024-06-29 19:43:46 -05:00
const [ ids ] = regionIds . splice (
Math . floor ( Math . random () * regionIds . length ),
1 ,
2024-06-29 10:49:32 -05:00
);
2024-06-28 14:12:58 -05:00
const accountInfo = await getAccountInfo ( region , ids , undefined , {
fields : 'last_battle_time,statistics.all.battles' ,
});
2024-06-29 19:43:46 -05:00
checkedPlayers += ids . length ;
2024-05-18 21:12:11 -05:00
2024-06-28 14:12:58 -05:00
/**
* Pass rates
* none: 1000 / 1000
* activity: 55 / 1000
* battles: 30 / 1000
* both: 23 / 1000
*/
2024-05-18 21:12:11 -05:00
2024-06-28 14:12:58 -05:00
const filteredIds = ids . filter (( _ , index ) => {
const info = accountInfo [ index ];
return (
info !== null &&
Date . now () - info . last_battle_time * 1000 <= MAX_ACTIVITY_TIME &&
info . statistics . all . battles > MIN_BATTLES
);
});
2024-06-28 20:08:50 -05:00
includedPlayers += filteredIds . length ;
2024-06-28 14:12:58 -05:00
const players = await Promise . all (
filteredIds . map (( id ) => getTankStats ( region , id )),
);
2024-05-19 17:50:01 -05:00
2024-06-28 14:12:58 -05:00
players . forEach (( tanks ) => {
tanks ? . forEach (( tank ) => {
if ( tank . all . battles === 0 ) return ;
2024-06-26 14:07:27 -05:00
2024-06-28 14:12:58 -05:00
if ( ! tankIds . includes ( tank . tank_id )) {
tankIds . push ( tank . tank_id );
tanksSorted [ tank . tank_id ] = [];
}
2024-05-19 17:50:01 -05:00
2024-06-28 14:12:58 -05:00
tanksSorted [ tank . tank_id ]. push ({
... tank . all ,
battle_life_time : tank.battle_life_time ,
});
2024-06-26 14:07:27 -05:00
});
2024-05-19 17:50:01 -05:00
});
2024-06-29 10:49:32 -05:00
if ( regionIds . length === 0 && availableRegions . includes ( region )) {
2024-06-28 14:12:58 -05:00
availableRegions . splice ( availableRegions . indexOf ( region ), 1 );
}
regionIndex = ( regionIndex + 1 ) % availableRegions . length ;
}
if ( ! postWorkRequested ) {
postWorkRequested = true ;
postWork ();
}
});
async function postWork() {
2024-05-19 17:50:01 -05:00
console . log (
2024-06-28 20:08:50 -05:00
`Generating statistics based off ${ includedPlayers . toLocaleString () } players ( ${ checkedPlayers . toLocaleString () } checked in total) and ${ tankIds . length } tanks...` ,
2024-05-19 17:50:01 -05:00
);
const averages : AverageDefinitions = {};
tankIds . forEach (( id ) => {
2024-06-28 14:12:58 -05:00
const tanks = tanksSorted [ id ];
2024-06-26 21:07:54 -05:00
const samples = tanks . length ;
const entry : AverageDefinitionsEntrySubPartial = {
samples ,
mu : {},
sigma : {},
r : {},
};
2024-05-19 17:50:01 -05:00
2024-06-26 14:07:27 -05:00
averageDefinitionsAllStatsKeys . forEach (( key ) => {
const data : DataPoint [] = tanks . map (( tank ) => ({
w : tank.battles ,
x : tank [ key ] / tank . battles ,
y : tank.wins / tank . battles ,
}));
2024-06-28 14:12:58 -05:00
if ( data . length === 0 ) return ;
2024-06-26 14:07:27 -05:00
function sum ( slicer : ( data : DataPoint ) => number ) {
return data . reduce (
( accumulator , data ) => accumulator + slicer ( data ),
0 ,
2024-05-19 17:50:01 -05:00
);
2024-06-26 14:07:27 -05:00
}
2024-05-19 17:50:01 -05:00
2024-06-26 14:07:27 -05:00
const sum_w = sum (({ w }) => w );
const sum_wx = sum (({ w , x }) => w * x );
const sum_wy = sum (({ w , y }) => w * y );
2024-06-28 14:12:58 -05:00
const x_bar = sum_wx / sum_w ;
const y_bar = sum_wy / sum_w ;
const mu = x_bar ;
2024-05-19 17:50:01 -05:00
2024-06-28 14:12:58 -05:00
const sigma_numerator = sum (({ w , x }) => w * ( x - x_bar ) ** 2 );
2024-06-26 21:07:54 -05:00
const sigma = Math . sqrt ( sigma_numerator / sum_w );
2024-06-28 14:12:58 -05:00
const r_numerator = sum (({ w , x , y }) => w * ( x - x_bar ) * ( y - y_bar ));
const r_denominator_x = sum (({ w , x }) => w * ( x - x_bar ) ** 2 );
const r_denominator_y = sum (({ w , y }) => w * ( y - y_bar ) ** 2 );
2024-06-26 14:07:27 -05:00
const r = r_numerator / Math . sqrt ( r_denominator_x * r_denominator_y );
2024-06-26 21:07:54 -05:00
entry . mu [ key ] = mu ;
entry . sigma [ key ] = sigma ;
2024-06-28 20:08:50 -05:00
entry . r [ key ] = isNaN ( r ) ? 0 : r ;
2024-06-26 14:07:27 -05:00
});
2024-06-26 21:07:54 -05:00
averages [ id ] = entry as AverageDefinitionsEntry ;
2024-05-19 17:50:01 -05:00
});
2024-06-28 20:08:50 -05:00
commitAssets (
'averages' ,
[
{
path : 'definitions/averages.pb' ,
content : await encodeToBase64 ( 'blitzkit.AverageDefinitions' , {
averages ,
}),
encoding : 'base64' ,
},
],
production ,
);
2024-05-19 17:50:01 -05:00
}