@firestone-hs/bgs-global-stats 1.0.39 → 1.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/internal-model.d.ts +1 -0
- package/dist/internal-model.js.map +1 -1
- package/dist/quests-v2/quests-stats-buikder.js +6 -4
- package/dist/quests-v2/quests-stats-buikder.js.map +1 -1
- package/dist/stats-v2/bgs-hero-stat.d.ts +31 -0
- package/dist/stats-v2/bgs-hero-stat.js.map +1 -1
- package/dist/stats-v2/stats-buikder.js +80 -20
- package/dist/stats-v2/stats-buikder.js.map +1 -1
- package/package.json +3 -3
package/dist/internal-model.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface InternalBgsRow {
|
|
|
17
17
|
readonly bgsQuestsCompletedTimings: string;
|
|
18
18
|
readonly bgsQuestsDifficulties: string;
|
|
19
19
|
readonly bgsHeroQuestRewards: string;
|
|
20
|
+
readonly bgsAnomalies: string;
|
|
20
21
|
}
|
|
21
22
|
export interface Slice {
|
|
22
23
|
readonly lastUpdateDate: Date;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-model.js","sourceRoot":"","sources":["../src/internal-model.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\nimport { BgsGlobalStats2 } from './bgs-global-stats';\r\n\r\nexport interface InternalBgsRow {\r\n\treadonly id: number;\r\n\treadonly reviewId: string;\r\n\treadonly creationDate: Date;\r\n\treadonly buildNumber: number;\r\n\treadonly rating: number;\r\n\treadonly heroCardId: string;\r\n\treadonly rank: number;\r\n\treadonly tribes: string;\r\n\treadonly combatWinrate: string;\r\n\treadonly warbandStats: string;\r\n\treadonly darkmoonPrizes: boolean;\r\n\treadonly quests: boolean;\r\n\treadonly bgsHeroQuests: string;\r\n\treadonly bgsQuestsCompletedTimings: string;\r\n\treadonly bgsQuestsDifficulties: string;\r\n\treadonly bgsHeroQuestRewards: string;\r\n}\r\n\r\nexport interface Slice {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly dataPoints: number;\r\n\treadonly dataForTribes: readonly DataForTribes[];\r\n\treadonly highestMmr: number;\r\n\treadonly mmrGroups: readonly RankGroup[];\r\n}\r\n\r\nexport interface DataForTribes {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly tribes: readonly Race[];\r\n\treadonly dataPoints: number;\r\n\treadonly dataForRank: readonly DataForMmr[];\r\n}\r\n\r\nexport interface DataForMmr {\r\n\treadonly lastUpdateDate: Date;\r\n\t// works in 500 MMR increments. All data in that group is for players between mmr and mmr + 500\r\n\treadonly mmrThreshold: number;\r\n\treadonly mmrCeiling: number;\r\n\treadonly dataPoints: number;\r\n\treadonly heroStats: readonly HeroStat[];\r\n}\r\n\r\nexport interface HeroStat {\r\n\treadonly heroCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly placementDistribution: readonly { rank: number; totalMatches: number }[];\r\n\t// To get the actual winrate, you will have to divide the totalWinrate by the dataPoints\r\n\treadonly combatWinrate: readonly { turn: number; dataPoints: number; totalWinrate: number }[];\r\n\t// Same\r\n\treadonly warbandStats: readonly { turn: number; dataPoints: number; totalStats: number }[];\r\n}\r\n\r\nexport interface FinalBgsDataForTimePeriod {\r\n\treadonly statsForTribes: readonly InternalBgsGlobalStats[];\r\n}\r\n\r\nexport interface InternalBgsGlobalStats extends BgsGlobalStats2 {\r\n\treadonly timePeriod: 'all-time' | 'past-three' | 'past-seven' | 'last-patch';\r\n\treadonly tribes: readonly Race[];\r\n\treadonly dataPoints: number;\r\n}\r\n\r\nexport interface RankGroup {\r\n\treadonly mmrThreshold: number;\r\n\treadonly mmrRangeUp: number;\r\n\treadonly quantity?: number;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"internal-model.js","sourceRoot":"","sources":["../src/internal-model.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\nimport { BgsGlobalStats2 } from './bgs-global-stats';\r\n\r\nexport interface InternalBgsRow {\r\n\treadonly id: number;\r\n\treadonly reviewId: string;\r\n\treadonly creationDate: Date;\r\n\treadonly buildNumber: number;\r\n\treadonly rating: number;\r\n\treadonly heroCardId: string;\r\n\treadonly rank: number;\r\n\treadonly tribes: string;\r\n\treadonly combatWinrate: string;\r\n\treadonly warbandStats: string;\r\n\treadonly darkmoonPrizes: boolean;\r\n\treadonly quests: boolean;\r\n\treadonly bgsHeroQuests: string;\r\n\treadonly bgsQuestsCompletedTimings: string;\r\n\treadonly bgsQuestsDifficulties: string;\r\n\treadonly bgsHeroQuestRewards: string;\r\n\treadonly bgsAnomalies: string;\r\n}\r\n\r\nexport interface Slice {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly dataPoints: number;\r\n\treadonly dataForTribes: readonly DataForTribes[];\r\n\treadonly highestMmr: number;\r\n\treadonly mmrGroups: readonly RankGroup[];\r\n}\r\n\r\nexport interface DataForTribes {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly tribes: readonly Race[];\r\n\treadonly dataPoints: number;\r\n\treadonly dataForRank: readonly DataForMmr[];\r\n}\r\n\r\nexport interface DataForMmr {\r\n\treadonly lastUpdateDate: Date;\r\n\t// works in 500 MMR increments. All data in that group is for players between mmr and mmr + 500\r\n\treadonly mmrThreshold: number;\r\n\treadonly mmrCeiling: number;\r\n\treadonly dataPoints: number;\r\n\treadonly heroStats: readonly HeroStat[];\r\n}\r\n\r\nexport interface HeroStat {\r\n\treadonly heroCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly placementDistribution: readonly { rank: number; totalMatches: number }[];\r\n\t// To get the actual winrate, you will have to divide the totalWinrate by the dataPoints\r\n\treadonly combatWinrate: readonly { turn: number; dataPoints: number; totalWinrate: number }[];\r\n\t// Same\r\n\treadonly warbandStats: readonly { turn: number; dataPoints: number; totalStats: number }[];\r\n}\r\n\r\nexport interface FinalBgsDataForTimePeriod {\r\n\treadonly statsForTribes: readonly InternalBgsGlobalStats[];\r\n}\r\n\r\nexport interface InternalBgsGlobalStats extends BgsGlobalStats2 {\r\n\treadonly timePeriod: 'all-time' | 'past-three' | 'past-seven' | 'last-patch';\r\n\treadonly tribes: readonly Race[];\r\n\treadonly dataPoints: number;\r\n}\r\n\r\nexport interface RankGroup {\r\n\treadonly mmrThreshold: number;\r\n\treadonly mmrRangeUp: number;\r\n\treadonly quantity?: number;\r\n}\r\n"]}
|
|
@@ -3,7 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.buildQuestStats = void 0;
|
|
4
4
|
const aws_lambda_utils_1 = require("@firestone-hs/aws-lambda-utils");
|
|
5
5
|
const buildQuestStats = (rows) => {
|
|
6
|
-
const
|
|
6
|
+
const rowsWithDifficulty = rows.filter((row) => {
|
|
7
|
+
const diff = row.bgsQuestsDifficulties;
|
|
8
|
+
return diff != null && !!(diff === null || diff === void 0 ? void 0 : diff.length) && !!parseInt(diff) && !isNaN(parseInt(diff));
|
|
9
|
+
});
|
|
10
|
+
const groupedByQuest = (0, aws_lambda_utils_1.groupByFunction)((row) => row.bgsHeroQuests.trim())(rowsWithDifficulty);
|
|
7
11
|
return Object.values(groupedByQuest).flatMap((data) => buildStatsForSingleQuest(data));
|
|
8
12
|
};
|
|
9
13
|
exports.buildQuestStats = buildQuestStats;
|
|
@@ -59,9 +63,7 @@ const buildHeroStats = (rows, refAverageTurnToComplete, refCompletionRate) => {
|
|
|
59
63
|
};
|
|
60
64
|
const buildDifficultyStats = (rows, refAverageTurnToComplete, refCompletionRate) => {
|
|
61
65
|
const groupedByDifficulty = (0, aws_lambda_utils_1.groupByFunction)((r) => parseInt(r.bgsQuestsDifficulties))(rows);
|
|
62
|
-
return Object.keys(groupedByDifficulty)
|
|
63
|
-
.filter((diff) => diff != null && !!(diff === null || diff === void 0 ? void 0 : diff.length) && !!parseInt(diff) && !isNaN(parseInt(diff)))
|
|
64
|
-
.map((diff) => {
|
|
66
|
+
return Object.keys(groupedByDifficulty).map((diff) => {
|
|
65
67
|
const rowsForDifficulty = groupedByDifficulty[diff];
|
|
66
68
|
const completed = rowsForDifficulty.filter((r) => { var _a; return !!((_a = r === null || r === void 0 ? void 0 : r.bgsQuestsCompletedTimings) === null || _a === void 0 ? void 0 : _a.length); });
|
|
67
69
|
const completionRate = completed.length / rowsForDifficulty.length;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quests-stats-buikder.js","sourceRoot":"","sources":["../../src/quests-v2/quests-stats-buikder.ts"],"names":[],"mappings":";;;AAAA,qEAAiE;AAK1D,MAAM,eAAe,GAAG,CAAC,IAA+B,EAAiC,EAAE;
|
|
1
|
+
{"version":3,"file":"quests-stats-buikder.js","sourceRoot":"","sources":["../../src/quests-v2/quests-stats-buikder.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { Race } from '@firestone-hs/reference-data';\r\nimport { InternalBgsRow } from '../internal-model';\r\nimport { BgsGlobalQuestStat, BgsQuestDifficultyStat, BgsQuestHeroStat, BgsQuestTribeStat } from './bgs-quest-stat';\r\n\r\nexport const buildQuestStats = (rows: readonly InternalBgsRow[]): readonly BgsGlobalQuestStat[] => {\r\n\t// Limit to the quests that have a valid difficulty, as it makes debugging easier and the data more reliable\r\n\tconst rowsWithDifficulty = rows.filter((row) => {\r\n\t\tconst diff = row.bgsQuestsDifficulties;\r\n\t\treturn diff != null && !!diff?.length && !!parseInt(diff) && !isNaN(parseInt(diff));\r\n\t});\r\n\tconst groupedByQuest: {\r\n\t\t[questCardId: string]: readonly InternalBgsRow[];\r\n\t} = groupByFunction((row: InternalBgsRow) => row.bgsHeroQuests.trim())(rowsWithDifficulty);\r\n\treturn Object.values(groupedByQuest).flatMap((data) => buildStatsForSingleQuest(data));\r\n};\r\n\r\n// All rows here belong to a single quest\r\nconst buildStatsForSingleQuest = (rows: readonly InternalBgsRow[]): BgsGlobalQuestStat => {\r\n\tconst ref = rows[0];\r\n\tconst completed = rows.filter((r) => !!r.bgsQuestsCompletedTimings?.length);\r\n\tconst averageTurnToComplete = average(completed.map((r) => parseInt(r.bgsQuestsCompletedTimings)));\r\n\tconst completionRate = completed.length / rows.length;\r\n\treturn {\r\n\t\tquestCardId: ref.bgsHeroQuests.trim(),\r\n\t\tdataPoints: rows.length,\r\n\t\taverageTurnToComplete: averageTurnToComplete,\r\n\t\tcompletionRate: completionRate,\r\n\t\tdifficultyStats: buildDifficultyStats(rows, averageTurnToComplete, completionRate),\r\n\t\theroStats: buildHeroStats(rows, averageTurnToComplete, completionRate),\r\n\t\ttribeStats: buildTribeStats(rows, averageTurnToComplete, completionRate),\r\n\t};\r\n};\r\n\r\nconst buildTribeStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\trefAverageTurnToComplete: number,\r\n\trefCompletionRate: number,\r\n): readonly BgsQuestTribeStat[] => {\r\n\tconst rowsWithTribes = rows.filter((r) => !!r.tribes?.length);\r\n\tconst uniqueTribes: readonly Race[] = [\r\n\t\t...new Set(rowsWithTribes.flatMap((r) => r.tribes.split(',')).map((r) => parseInt(r))),\r\n\t];\r\n\treturn uniqueTribes.map((tribe) => {\r\n\t\tconst rowsForTribe = rowsWithTribes.filter((r) => r.tribes.split(',').includes('' + tribe));\r\n\t\tconst completed = rowsForTribe.filter((r) => !!r?.bgsQuestsCompletedTimings?.length);\r\n\t\tconst completionRate = completed.length / rowsForTribe.length;\r\n\t\tconst averageTurnToComplete = average(completed.map((r) => parseInt(r.bgsQuestsCompletedTimings)));\r\n\t\treturn {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: rowsForTribe.length,\r\n\t\t\tcompletionRate: completionRate,\r\n\t\t\taverageTurnToComplete: averageTurnToComplete,\r\n\t\t\timpactCompletionRate: completionRate - refCompletionRate,\r\n\t\t\timpactTurnToComplete: averageTurnToComplete - refAverageTurnToComplete,\r\n\t\t};\r\n\t});\r\n};\r\n\r\nconst buildHeroStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\trefAverageTurnToComplete: number,\r\n\trefCompletionRate: number,\r\n): readonly BgsQuestHeroStat[] => {\r\n\tconst groupedByHero = groupByFunction((r: InternalBgsRow) => r.heroCardId)(rows);\r\n\treturn Object.keys(groupedByHero).map((heroCardId) => {\r\n\t\tconst rowsForHero = groupedByHero[heroCardId];\r\n\t\tconst completed = rowsForHero.filter((r) => !!r?.bgsQuestsCompletedTimings?.length);\r\n\t\tconst completionRate = completed.length / rowsForHero.length;\r\n\t\tconst averageTurnToComplete = average(completed.map((r) => parseInt(r.bgsQuestsCompletedTimings)));\r\n\t\treturn {\r\n\t\t\theroCardId: heroCardId,\r\n\t\t\tdataPoints: rowsForHero.length,\r\n\t\t\taverageTurnToComplete: averageTurnToComplete,\r\n\t\t\tcompletionRate: completionRate,\r\n\t\t};\r\n\t});\r\n};\r\n\r\nconst buildDifficultyStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\trefAverageTurnToComplete: number,\r\n\trefCompletionRate: number,\r\n): readonly BgsQuestDifficultyStat[] => {\r\n\tconst groupedByDifficulty = groupByFunction((r: InternalBgsRow) => parseInt(r.bgsQuestsDifficulties))(rows);\r\n\treturn Object.keys(groupedByDifficulty).map((diff) => {\r\n\t\tconst rowsForDifficulty = groupedByDifficulty[diff];\r\n\t\tconst completed = rowsForDifficulty.filter((r) => !!r?.bgsQuestsCompletedTimings?.length);\r\n\t\tconst completionRate = completed.length / rowsForDifficulty.length;\r\n\t\tconst averageTurnToComplete = average(completed.map((r) => parseInt(r.bgsQuestsCompletedTimings)));\r\n\t\treturn {\r\n\t\t\tdifficulty: parseInt(diff),\r\n\t\t\tdataPoints: rowsForDifficulty.length,\r\n\t\t\tcompletionRate: completionRate,\r\n\t\t\taverageTurnToComplete: averageTurnToComplete,\r\n\t\t\timpactCompletionRate: completionRate - refCompletionRate,\r\n\t\t\timpactTurnToComplete: averageTurnToComplete - refAverageTurnToComplete,\r\n\t\t};\r\n\t});\r\n};\r\n\r\nconst average = (data: readonly number[]): number => {\r\n\treturn data.reduce((a, b) => a + b, 0) / data.length;\r\n};\r\n"]}
|
|
@@ -27,6 +27,7 @@ export interface BgsGlobalHeroStat {
|
|
|
27
27
|
averageStats: number;
|
|
28
28
|
}[];
|
|
29
29
|
readonly tribeStats: readonly BgsHeroTribeStat[];
|
|
30
|
+
readonly anomalyStats: readonly BgsHeroAnomalyStat[];
|
|
30
31
|
}
|
|
31
32
|
export interface BgsHeroTribeStat {
|
|
32
33
|
readonly tribe: Race;
|
|
@@ -59,3 +60,33 @@ export interface BgsHeroTribeStat {
|
|
|
59
60
|
impact: number;
|
|
60
61
|
}[];
|
|
61
62
|
}
|
|
63
|
+
export interface BgsHeroAnomalyStat {
|
|
64
|
+
readonly anomaly: string;
|
|
65
|
+
readonly dataPoints: number;
|
|
66
|
+
readonly averagePosition: number;
|
|
67
|
+
readonly impactAveragePosition: number;
|
|
68
|
+
readonly placementDistribution: readonly {
|
|
69
|
+
rank: number;
|
|
70
|
+
percentage: number;
|
|
71
|
+
}[];
|
|
72
|
+
readonly impactPlacementDistribution: readonly {
|
|
73
|
+
rank: number;
|
|
74
|
+
impact: number;
|
|
75
|
+
}[];
|
|
76
|
+
readonly combatWinrate: readonly {
|
|
77
|
+
turn: number;
|
|
78
|
+
winrate: number;
|
|
79
|
+
}[];
|
|
80
|
+
readonly impactCombatWinrate: readonly {
|
|
81
|
+
turn: number;
|
|
82
|
+
impact: number;
|
|
83
|
+
}[];
|
|
84
|
+
readonly warbandStats: readonly {
|
|
85
|
+
turn: number;
|
|
86
|
+
averageStats: number;
|
|
87
|
+
}[];
|
|
88
|
+
readonly impactWarbandStats: readonly {
|
|
89
|
+
turn: number;
|
|
90
|
+
impact: number;
|
|
91
|
+
}[];
|
|
92
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bgs-hero-stat.js","sourceRoot":"","sources":["../../src/stats-v2/bgs-hero-stat.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\nimport { MmrPercentile } from '../bgs-global-stats';\r\nimport { WithMmrAndTimePeriod } from '../quests-v2/charged-stat';\r\n\r\nexport interface BgsHeroStatsV2 {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly mmrPercentiles: readonly MmrPercentile[];\r\n\treadonly dataPoints: number;\r\n\treadonly heroStats: readonly WithMmrAndTimePeriod<BgsGlobalHeroStat>[];\r\n}\r\n\r\nexport interface BgsGlobalHeroStat {\r\n\treadonly heroCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly averagePosition: number;\r\n\treadonly standardDeviation: number;\r\n\treadonly standardDeviationOfTheMean: number;\r\n\treadonly conservativePositionEstimate: number;\r\n\treadonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\treadonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\treadonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\treadonly tribeStats: readonly BgsHeroTribeStat[];\r\n}\r\n\r\nexport interface BgsHeroTribeStat {\r\n\treadonly tribe: Race;\r\n\treadonly dataPoints: number;\r\n\treadonly dataPointsOnMissingTribe: number;\r\n\treadonly averagePosition: number;\r\n\treadonly impactAveragePosition: number;\r\n\treadonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\treadonly impactPlacementDistribution: readonly { rank: number; impact: number }[];\r\n\treadonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\treadonly impactCombatWinrate: readonly { turn: number; impact: number }[];\r\n\treadonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\treadonly impactWarbandStats: readonly { turn: number; impact: number }[];\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"bgs-hero-stat.js","sourceRoot":"","sources":["../../src/stats-v2/bgs-hero-stat.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\nimport { MmrPercentile } from '../bgs-global-stats';\r\nimport { WithMmrAndTimePeriod } from '../quests-v2/charged-stat';\r\n\r\nexport interface BgsHeroStatsV2 {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly mmrPercentiles: readonly MmrPercentile[];\r\n\treadonly dataPoints: number;\r\n\treadonly heroStats: readonly WithMmrAndTimePeriod<BgsGlobalHeroStat>[];\r\n}\r\n\r\nexport interface BgsGlobalHeroStat {\r\n\treadonly heroCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly averagePosition: number;\r\n\treadonly standardDeviation: number;\r\n\treadonly standardDeviationOfTheMean: number;\r\n\treadonly conservativePositionEstimate: number;\r\n\treadonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\treadonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\treadonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\treadonly tribeStats: readonly BgsHeroTribeStat[];\r\n\treadonly anomalyStats: readonly BgsHeroAnomalyStat[];\r\n}\r\n\r\nexport interface BgsHeroTribeStat {\r\n\treadonly tribe: Race;\r\n\treadonly dataPoints: number;\r\n\treadonly dataPointsOnMissingTribe: number;\r\n\treadonly averagePosition: number;\r\n\treadonly impactAveragePosition: number;\r\n\treadonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\treadonly impactPlacementDistribution: readonly { rank: number; impact: number }[];\r\n\treadonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\treadonly impactCombatWinrate: readonly { turn: number; impact: number }[];\r\n\treadonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\treadonly impactWarbandStats: readonly { turn: number; impact: number }[];\r\n}\r\n\r\nexport interface BgsHeroAnomalyStat {\r\n\treadonly anomaly: string;\r\n\treadonly dataPoints: number;\r\n\treadonly averagePosition: number;\r\n\treadonly impactAveragePosition: number;\r\n\treadonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\treadonly impactPlacementDistribution: readonly { rank: number; impact: number }[];\r\n\treadonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\treadonly impactCombatWinrate: readonly { turn: number; impact: number }[];\r\n\treadonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\treadonly impactWarbandStats: readonly { turn: number; impact: number }[];\r\n}\r\n"]}
|
|
@@ -7,26 +7,26 @@ const common_1 = require("../common");
|
|
|
7
7
|
const util_functions_1 = require("../utils/util-functions");
|
|
8
8
|
const buildStats = (rows, allCards) => {
|
|
9
9
|
const groupedByHero = (0, aws_lambda_utils_1.groupByFunction)((row) => (0, util_functions_1.normalizeHeroCardId)(row.heroCardId, allCards))(rows);
|
|
10
|
-
return Object.values(groupedByHero).flatMap(data => buildStatsForSingleHero(data));
|
|
10
|
+
return Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data));
|
|
11
11
|
};
|
|
12
12
|
exports.buildStats = buildStats;
|
|
13
13
|
const buildStatsForSingleHero = (rows) => {
|
|
14
14
|
const ref = rows[0];
|
|
15
|
-
const averagePosition = average(rows.map(r => r.rank));
|
|
15
|
+
const averagePosition = average(rows.map((r) => r.rank));
|
|
16
16
|
const placementDistribution = (0, common_1.buildPlacementDistributionWithPercentages)(rows);
|
|
17
17
|
const rawCombatWinrates = (0, build_battlegrounds_hero_stats_new_1.buildCombatWinrate)(rows);
|
|
18
|
-
const combatWinrate = rawCombatWinrates.map(info => ({
|
|
18
|
+
const combatWinrate = rawCombatWinrates.map((info) => ({
|
|
19
19
|
turn: info.turn,
|
|
20
20
|
winrate: info.totalWinrate / info.dataPoints,
|
|
21
21
|
}));
|
|
22
22
|
const rawWarbandStats = (0, build_battlegrounds_hero_stats_new_1.buildWarbandStats)(rows);
|
|
23
|
-
const warbandStats = rawWarbandStats.map(info => ({
|
|
23
|
+
const warbandStats = rawWarbandStats.map((info) => ({
|
|
24
24
|
turn: info.turn,
|
|
25
25
|
averageStats: info.totalStats / info.dataPoints,
|
|
26
26
|
}));
|
|
27
|
-
const allRanks = rows.map(r => r.rank);
|
|
28
|
-
const allDeviations = allRanks.map(r => averagePosition - r);
|
|
29
|
-
const squareDeviations = allDeviations.map(d => Math.pow(d, 2));
|
|
27
|
+
const allRanks = rows.map((r) => r.rank);
|
|
28
|
+
const allDeviations = allRanks.map((r) => averagePosition - r);
|
|
29
|
+
const squareDeviations = allDeviations.map((d) => Math.pow(d, 2));
|
|
30
30
|
const sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);
|
|
31
31
|
const variance = sumOfSquares / rows.length;
|
|
32
32
|
const standardDeviation = Math.sqrt(variance);
|
|
@@ -42,23 +42,26 @@ const buildStatsForSingleHero = (rows) => {
|
|
|
42
42
|
combatWinrate: combatWinrate,
|
|
43
43
|
warbandStats: warbandStats,
|
|
44
44
|
tribeStats: buildTribeStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats),
|
|
45
|
+
anomalyStats: buildAnomalyStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats),
|
|
45
46
|
};
|
|
46
47
|
return result;
|
|
47
48
|
};
|
|
48
49
|
const buildTribeStats = (rows, refAveragePosition, refPlacementDistribution, refCombatWinrate, refWarbandStats) => {
|
|
49
|
-
const uniqueTribes = [
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
50
|
+
const uniqueTribes = [
|
|
51
|
+
...new Set(rows.flatMap((r) => r.tribes.split(',')).map((r) => parseInt(r))),
|
|
52
|
+
];
|
|
53
|
+
return uniqueTribes.map((tribe) => {
|
|
54
|
+
const rowsForTribe = rows.filter((r) => r.tribes.split(',').includes('' + tribe));
|
|
55
|
+
const rowsWithoutTribe = rows.filter((r) => !r.tribes.split(',').includes('' + tribe));
|
|
56
|
+
const averagePosition = average(rowsForTribe.map((r) => r.rank));
|
|
54
57
|
const placementDistribution = (0, common_1.buildPlacementDistributionWithPercentages)(rowsForTribe);
|
|
55
58
|
const rawCombatWinrates = (0, build_battlegrounds_hero_stats_new_1.buildCombatWinrate)(rowsForTribe);
|
|
56
|
-
const combatWinrate = rawCombatWinrates.map(info => ({
|
|
59
|
+
const combatWinrate = rawCombatWinrates.map((info) => ({
|
|
57
60
|
turn: info.turn,
|
|
58
61
|
winrate: info.totalWinrate / info.dataPoints,
|
|
59
62
|
}));
|
|
60
63
|
const rawWarbandStats = (0, build_battlegrounds_hero_stats_new_1.buildWarbandStats)(rowsForTribe);
|
|
61
|
-
const warbandStats = rawWarbandStats.map(info => ({
|
|
64
|
+
const warbandStats = rawWarbandStats.map((info) => ({
|
|
62
65
|
turn: info.turn,
|
|
63
66
|
averageStats: info.totalStats / info.dataPoints,
|
|
64
67
|
}));
|
|
@@ -69,9 +72,9 @@ const buildTribeStats = (rows, refAveragePosition, refPlacementDistribution, ref
|
|
|
69
72
|
averagePosition: averagePosition,
|
|
70
73
|
impactAveragePosition: averagePosition - refAveragePosition,
|
|
71
74
|
placementDistribution: placementDistribution,
|
|
72
|
-
impactPlacementDistribution: refPlacementDistribution.map(p => {
|
|
75
|
+
impactPlacementDistribution: refPlacementDistribution.map((p) => {
|
|
73
76
|
var _a;
|
|
74
|
-
const newPlacementInfo = placementDistribution.find(p2 => p2.rank === p.rank);
|
|
77
|
+
const newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);
|
|
75
78
|
if (!newPlacementInfo) {
|
|
76
79
|
}
|
|
77
80
|
return {
|
|
@@ -80,9 +83,9 @@ const buildTribeStats = (rows, refAveragePosition, refPlacementDistribution, ref
|
|
|
80
83
|
};
|
|
81
84
|
}),
|
|
82
85
|
combatWinrate: combatWinrate,
|
|
83
|
-
impactCombatWinrate: refCombatWinrate.map(c => {
|
|
86
|
+
impactCombatWinrate: refCombatWinrate.map((c) => {
|
|
84
87
|
var _a;
|
|
85
|
-
const newCombatWinrate = combatWinrate.find(c2 => c2.turn === c.turn);
|
|
88
|
+
const newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);
|
|
86
89
|
if (!newCombatWinrate) {
|
|
87
90
|
}
|
|
88
91
|
return {
|
|
@@ -91,9 +94,66 @@ const buildTribeStats = (rows, refAveragePosition, refPlacementDistribution, ref
|
|
|
91
94
|
};
|
|
92
95
|
}),
|
|
93
96
|
warbandStats: warbandStats,
|
|
94
|
-
impactWarbandStats: refWarbandStats.map(c => {
|
|
97
|
+
impactWarbandStats: refWarbandStats.map((c) => {
|
|
95
98
|
var _a;
|
|
96
|
-
const newWarbandStats = warbandStats.find(c2 => c2.turn === c.turn);
|
|
99
|
+
const newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);
|
|
100
|
+
if (!newWarbandStats) {
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
turn: c.turn,
|
|
104
|
+
impact: ((_a = newWarbandStats === null || newWarbandStats === void 0 ? void 0 : newWarbandStats.averageStats) !== null && _a !== void 0 ? _a : 0) - c.averageStats,
|
|
105
|
+
};
|
|
106
|
+
}),
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
const buildAnomalyStats = (rows, refAveragePosition, refPlacementDistribution, refCombatWinrate, refWarbandStats) => {
|
|
111
|
+
const uniqueAnomalies = [...new Set(rows.flatMap((r) => r.bgsAnomalies))];
|
|
112
|
+
return uniqueAnomalies.map((anomaly) => {
|
|
113
|
+
const rowsForAnomaly = rows.filter((r) => r.bgsAnomalies.includes(anomaly));
|
|
114
|
+
const averagePosition = average(rowsForAnomaly.map((r) => r.rank));
|
|
115
|
+
const placementDistribution = (0, common_1.buildPlacementDistributionWithPercentages)(rowsForAnomaly);
|
|
116
|
+
const rawCombatWinrates = (0, build_battlegrounds_hero_stats_new_1.buildCombatWinrate)(rowsForAnomaly);
|
|
117
|
+
const combatWinrate = rawCombatWinrates.map((info) => ({
|
|
118
|
+
turn: info.turn,
|
|
119
|
+
winrate: info.totalWinrate / info.dataPoints,
|
|
120
|
+
}));
|
|
121
|
+
const rawWarbandStats = (0, build_battlegrounds_hero_stats_new_1.buildWarbandStats)(rowsForAnomaly);
|
|
122
|
+
const warbandStats = rawWarbandStats.map((info) => ({
|
|
123
|
+
turn: info.turn,
|
|
124
|
+
averageStats: info.totalStats / info.dataPoints,
|
|
125
|
+
}));
|
|
126
|
+
return {
|
|
127
|
+
anomaly: anomaly,
|
|
128
|
+
dataPoints: rowsForAnomaly.length,
|
|
129
|
+
averagePosition: averagePosition,
|
|
130
|
+
impactAveragePosition: averagePosition - refAveragePosition,
|
|
131
|
+
placementDistribution: placementDistribution,
|
|
132
|
+
impactPlacementDistribution: refPlacementDistribution.map((p) => {
|
|
133
|
+
var _a;
|
|
134
|
+
const newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);
|
|
135
|
+
if (!newPlacementInfo) {
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
rank: p.rank,
|
|
139
|
+
impact: ((_a = newPlacementInfo === null || newPlacementInfo === void 0 ? void 0 : newPlacementInfo.percentage) !== null && _a !== void 0 ? _a : 0) - p.percentage,
|
|
140
|
+
};
|
|
141
|
+
}),
|
|
142
|
+
combatWinrate: combatWinrate,
|
|
143
|
+
impactCombatWinrate: refCombatWinrate.map((c) => {
|
|
144
|
+
var _a;
|
|
145
|
+
const newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);
|
|
146
|
+
if (!newCombatWinrate) {
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
turn: c.turn,
|
|
150
|
+
impact: ((_a = newCombatWinrate === null || newCombatWinrate === void 0 ? void 0 : newCombatWinrate.winrate) !== null && _a !== void 0 ? _a : 0) - c.winrate,
|
|
151
|
+
};
|
|
152
|
+
}),
|
|
153
|
+
warbandStats: warbandStats,
|
|
154
|
+
impactWarbandStats: refWarbandStats.map((c) => {
|
|
155
|
+
var _a;
|
|
156
|
+
const newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);
|
|
97
157
|
if (!newWarbandStats) {
|
|
98
158
|
}
|
|
99
159
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats-buikder.js","sourceRoot":"","sources":["../../src/stats-v2/stats-buikder.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, Race } from '@firestone-hs/reference-data';\r\nimport { buildCombatWinrate, buildWarbandStats } from '../build-battlegrounds-hero-stats-new';\r\nimport { buildPlacementDistributionWithPercentages } from '../common';\r\nimport { InternalBgsRow } from '../internal-model';\r\nimport { normalizeHeroCardId } from '../utils/util-functions';\r\nimport { BgsGlobalHeroStat, BgsHeroTribeStat } from './bgs-hero-stat';\r\n\r\nexport const buildStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\tconst groupedByHero: {\r\n\t\t[questCardId: string]: readonly InternalBgsRow[];\r\n\t} = groupByFunction((row: InternalBgsRow) => normalizeHeroCardId(row.heroCardId, allCards))(rows);\r\n\treturn Object.values(groupedByHero).flatMap(data => buildStatsForSingleHero(data));\r\n};\r\n\r\n// All rows here belong to a single hero\r\nconst buildStatsForSingleHero = (rows: readonly InternalBgsRow[]): BgsGlobalHeroStat => {\r\n\tconst ref = rows[0];\r\n\tconst averagePosition = average(rows.map(r => r.rank));\r\n\tconst placementDistribution = buildPlacementDistributionWithPercentages(rows);\r\n\tconst rawCombatWinrates = buildCombatWinrate(rows);\r\n\tconst combatWinrate: readonly { turn: number; winrate: number }[] = rawCombatWinrates.map(info => ({\r\n\t\tturn: info.turn,\r\n\t\twinrate: info.totalWinrate / info.dataPoints,\r\n\t}));\r\n\tconst rawWarbandStats = buildWarbandStats(rows);\r\n\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map(info => ({\r\n\t\tturn: info.turn,\r\n\t\taverageStats: info.totalStats / info.dataPoints,\r\n\t}));\r\n\r\n\tconst allRanks = rows.map(r => r.rank);\r\n\tconst allDeviations = allRanks.map(r => averagePosition - r);\r\n\tconst squareDeviations = allDeviations.map(d => Math.pow(d, 2));\r\n\tconst sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);\r\n\tconst variance = sumOfSquares / rows.length;\r\n\tconst standardDeviation = Math.sqrt(variance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(rows.length);\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: rows.length,\r\n\t\taveragePosition: averagePosition,\r\n\t\tstandardDeviation: standardDeviation,\r\n\t\tstandardDeviationOfTheMean: standardDeviationOfTheMean,\r\n\t\tconservativePositionEstimate: averagePosition + 3 * standardDeviationOfTheMean,\r\n\t\tplacementDistribution: placementDistribution,\r\n\t\tcombatWinrate: combatWinrate,\r\n\t\twarbandStats: warbandStats,\r\n\t\ttribeStats: buildTribeStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst buildTribeStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\trefAveragePosition: number,\r\n\trefPlacementDistribution: readonly { rank: number; percentage: number }[],\r\n\trefCombatWinrate: readonly { turn: number; winrate: number }[],\r\n\trefWarbandStats: readonly { turn: number; averageStats: number }[],\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst uniqueTribes: readonly Race[] = [...new Set(rows.flatMap(r => r.tribes.split(',')).map(r => parseInt(r)))];\r\n\treturn uniqueTribes.map(tribe => {\r\n\t\tconst rowsForTribe = rows.filter(r => r.tribes.split(',').includes('' + tribe));\r\n\t\tconst rowsWithoutTribe = rows.filter(r => !r.tribes.split(',').includes('' + tribe));\r\n\t\tconst averagePosition = average(rowsForTribe.map(r => r.rank));\r\n\t\tconst placementDistribution = buildPlacementDistributionWithPercentages(rowsForTribe);\r\n\t\tconst rawCombatWinrates = buildCombatWinrate(rowsForTribe);\r\n\t\tconst combatWinrate = rawCombatWinrates.map(info => ({\r\n\t\t\tturn: info.turn,\r\n\t\t\twinrate: info.totalWinrate / info.dataPoints,\r\n\t\t}));\r\n\t\tconst rawWarbandStats = buildWarbandStats(rowsForTribe);\r\n\t\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map(info => ({\r\n\t\t\tturn: info.turn,\r\n\t\t\taverageStats: info.totalStats / info.dataPoints,\r\n\t\t}));\r\n\t\treturn {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: rowsForTribe.length,\r\n\t\t\tdataPointsOnMissingTribe: rowsWithoutTribe.length,\r\n\t\t\taveragePosition: averagePosition,\r\n\t\t\timpactAveragePosition: averagePosition - refAveragePosition,\r\n\t\t\tplacementDistribution: placementDistribution,\r\n\t\t\timpactPlacementDistribution: refPlacementDistribution.map(p => {\r\n\t\t\t\tconst newPlacementInfo = placementDistribution.find(p2 => p2.rank === p.rank);\r\n\t\t\t\t// Cna happen when there isn't a lot of data points, typically for high MMR\r\n\t\t\t\tif (!newPlacementInfo) {\r\n\t\t\t\t\t// console.log('missing placement info', placementDistribution, p);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\trank: p.rank,\r\n\t\t\t\t\timpact: (newPlacementInfo?.percentage ?? 0) - p.percentage,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t\tcombatWinrate: combatWinrate,\r\n\t\t\timpactCombatWinrate: refCombatWinrate.map(c => {\r\n\t\t\t\tconst newCombatWinrate = combatWinrate.find(c2 => c2.turn === c.turn);\r\n\t\t\t\tif (!newCombatWinrate) {\r\n\t\t\t\t\t// console.debug('missing winrate info', combatWinrate);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\tturn: c.turn,\r\n\t\t\t\t\timpact: (newCombatWinrate?.winrate ?? 0) - c.winrate,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t\twarbandStats: warbandStats,\r\n\t\t\timpactWarbandStats: refWarbandStats.map(c => {\r\n\t\t\t\tconst newWarbandStats = warbandStats.find(c2 => c2.turn === c.turn);\r\n\t\t\t\tif (!newWarbandStats) {\r\n\t\t\t\t\t// console.debug('missing warband info', warbandStats);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\tturn: c.turn,\r\n\t\t\t\t\timpact: (newWarbandStats?.averageStats ?? 0) - c.averageStats,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t};\r\n\t});\r\n};\r\n\r\nconst average = (data: readonly number[]): number => {\r\n\treturn data.reduce((a, b) => a + b, 0) / data.length;\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"file":"stats-buikder.js","sourceRoot":"","sources":["../../src/stats-v2/stats-buikder.ts"],"names":[],"mappings":";;;AAAA,qEAAiE;AAEjE,8FAA8F;AAC9F,sCAAsE;AAEtE,4DAA8D;AAGvD,MAAM,UAAU,GAAG,CACzB,IAA+B,EAC/B,QAAyB,EACM,EAAE;IACjC,MAAM,aAAa,GAEf,IAAA,kCAAe,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,IAAA,oCAAmB,EAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClG,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;AACtF,CAAC,CAAC;AARW,QAAA,UAAU,cAQrB;AAGF,MAAM,uBAAuB,GAAG,CAAC,IAA+B,EAAqB,EAAE;IACtF,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,MAAM,qBAAqB,GAAG,IAAA,kDAAyC,EAAC,IAAI,CAAC,CAAC;IAC9E,MAAM,iBAAiB,GAAG,IAAA,uDAAkB,EAAC,IAAI,CAAC,CAAC;IACnD,MAAM,aAAa,GAAiD,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACpG,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU;KAC5C,CAAC,CAAC,CAAC;IACJ,MAAM,eAAe,GAAG,IAAA,sDAAiB,EAAC,IAAI,CAAC,CAAC;IAChD,MAAM,YAAY,GAAsD,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtG,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,YAAY,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU;KAC/C,CAAC,CAAC,CAAC;IAEJ,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;IAC/D,MAAM,gBAAgB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClE,MAAM,YAAY,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5C,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,0BAA0B,GAAG,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAsB;QACjC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,UAAU,EAAE,IAAI,CAAC,MAAM;QACvB,eAAe,EAAE,eAAe;QAChC,iBAAiB,EAAE,iBAAiB;QACpC,0BAA0B,EAAE,0BAA0B;QACtD,4BAA4B,EAAE,eAAe,GAAG,CAAC,GAAG,0BAA0B;QAC9E,qBAAqB,EAAE,qBAAqB;QAC5C,aAAa,EAAE,aAAa;QAC5B,YAAY,EAAE,YAAY;QAC1B,UAAU,EAAE,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,qBAAqB,EAAE,aAAa,EAAE,YAAY,CAAC;QACtG,YAAY,EAAE,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE,qBAAqB,EAAE,aAAa,EAAE,YAAY,CAAC;KAC1G,CAAC;IACF,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CACvB,IAA+B,EAC/B,kBAA0B,EAC1B,wBAAyE,EACzE,gBAA8D,EAC9D,eAAkE,EACpC,EAAE;IAChC,MAAM,YAAY,GAAoB;QACrC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;KAC5E,CAAC;IACF,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACjC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;QAClF,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;QACvF,MAAM,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,MAAM,qBAAqB,GAAG,IAAA,kDAAyC,EAAC,YAAY,CAAC,CAAC;QACtF,MAAM,iBAAiB,GAAG,IAAA,uDAAkB,EAAC,YAAY,CAAC,CAAC;QAC3D,MAAM,aAAa,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACtD,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU;SAC5C,CAAC,CAAC,CAAC;QACJ,MAAM,eAAe,GAAG,IAAA,sDAAiB,EAAC,YAAY,CAAC,CAAC;QACxD,MAAM,YAAY,GAAsD,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACtG,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU;SAC/C,CAAC,CAAC,CAAC;QACJ,OAAO;YACN,KAAK,EAAE,KAAK;YACZ,UAAU,EAAE,YAAY,CAAC,MAAM;YAC/B,wBAAwB,EAAE,gBAAgB,CAAC,MAAM;YACjD,eAAe,EAAE,eAAe;YAChC,qBAAqB,EAAE,eAAe,GAAG,kBAAkB;YAC3D,qBAAqB,EAAE,qBAAqB;YAC5C,2BAA2B,EAAE,wBAAwB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;;gBAC/D,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;gBAEhF,IAAI,CAAC,gBAAgB,EAAE;iBAEtB;gBACD,OAAO;oBACN,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,MAAM,EAAE,CAAC,MAAA,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,CAAE,UAAU,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU;iBAC1D,CAAC;YACH,CAAC,CAAC;YACF,aAAa,EAAE,aAAa;YAC5B,mBAAmB,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;;gBAC/C,MAAM,gBAAgB,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;gBACxE,IAAI,CAAC,gBAAgB,EAAE;iBAEtB;gBACD,OAAO;oBACN,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,MAAM,EAAE,CAAC,MAAA,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,CAAE,OAAO,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO;iBACpD,CAAC;YACH,CAAC,CAAC;YACF,YAAY,EAAE,YAAY;YAC1B,kBAAkB,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;;gBAC7C,MAAM,eAAe,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;gBACtE,IAAI,CAAC,eAAe,EAAE;iBAErB;gBACD,OAAO;oBACN,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,MAAM,EAAE,CAAC,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,YAAY,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY;iBAC7D,CAAC;YACH,CAAC,CAAC;SACF,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACzB,IAA+B,EAC/B,kBAA0B,EAC1B,wBAAyE,EACzE,gBAA8D,EAC9D,eAAkE,EAClC,EAAE;IAClC,MAAM,eAAe,GAAsB,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC7F,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACtC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5E,MAAM,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACnE,MAAM,qBAAqB,GAAG,IAAA,kDAAyC,EAAC,cAAc,CAAC,CAAC;QACxF,MAAM,iBAAiB,GAAG,IAAA,uDAAkB,EAAC,cAAc,CAAC,CAAC;QAC7D,MAAM,aAAa,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACtD,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU;SAC5C,CAAC,CAAC,CAAC;QACJ,MAAM,eAAe,GAAG,IAAA,sDAAiB,EAAC,cAAc,CAAC,CAAC;QAC1D,MAAM,YAAY,GAAsD,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACtG,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU;SAC/C,CAAC,CAAC,CAAC;QACJ,OAAO;YACN,OAAO,EAAE,OAAO;YAChB,UAAU,EAAE,cAAc,CAAC,MAAM;YACjC,eAAe,EAAE,eAAe;YAChC,qBAAqB,EAAE,eAAe,GAAG,kBAAkB;YAC3D,qBAAqB,EAAE,qBAAqB;YAC5C,2BAA2B,EAAE,wBAAwB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;;gBAC/D,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;gBAEhF,IAAI,CAAC,gBAAgB,EAAE;iBAEtB;gBACD,OAAO;oBACN,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,MAAM,EAAE,CAAC,MAAA,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,CAAE,UAAU,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU;iBAC1D,CAAC;YACH,CAAC,CAAC;YACF,aAAa,EAAE,aAAa;YAC5B,mBAAmB,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;;gBAC/C,MAAM,gBAAgB,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;gBACxE,IAAI,CAAC,gBAAgB,EAAE;iBAEtB;gBACD,OAAO;oBACN,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,MAAM,EAAE,CAAC,MAAA,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,CAAE,OAAO,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO;iBACpD,CAAC;YACH,CAAC,CAAC;YACF,YAAY,EAAE,YAAY;YAC1B,kBAAkB,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;;gBAC7C,MAAM,eAAe,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;gBACtE,IAAI,CAAC,eAAe,EAAE;iBAErB;gBACD,OAAO;oBACN,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,MAAM,EAAE,CAAC,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,YAAY,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY;iBAC7D,CAAC;YACH,CAAC,CAAC;SACF,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,IAAuB,EAAU,EAAE;IACnD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;AACtD,CAAC,CAAC","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, Race } from '@firestone-hs/reference-data';\r\nimport { buildCombatWinrate, buildWarbandStats } from '../build-battlegrounds-hero-stats-new';\r\nimport { buildPlacementDistributionWithPercentages } from '../common';\r\nimport { InternalBgsRow } from '../internal-model';\r\nimport { normalizeHeroCardId } from '../utils/util-functions';\r\nimport { BgsGlobalHeroStat, BgsHeroAnomalyStat, BgsHeroTribeStat } from './bgs-hero-stat';\r\n\r\nexport const buildStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\tconst groupedByHero: {\r\n\t\t[questCardId: string]: readonly InternalBgsRow[];\r\n\t} = groupByFunction((row: InternalBgsRow) => normalizeHeroCardId(row.heroCardId, allCards))(rows);\r\n\treturn Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data));\r\n};\r\n\r\n// All rows here belong to a single hero\r\nconst buildStatsForSingleHero = (rows: readonly InternalBgsRow[]): BgsGlobalHeroStat => {\r\n\tconst ref = rows[0];\r\n\tconst averagePosition = average(rows.map((r) => r.rank));\r\n\tconst placementDistribution = buildPlacementDistributionWithPercentages(rows);\r\n\tconst rawCombatWinrates = buildCombatWinrate(rows);\r\n\tconst combatWinrate: readonly { turn: number; winrate: number }[] = rawCombatWinrates.map((info) => ({\r\n\t\tturn: info.turn,\r\n\t\twinrate: info.totalWinrate / info.dataPoints,\r\n\t}));\r\n\tconst rawWarbandStats = buildWarbandStats(rows);\r\n\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n\t\tturn: info.turn,\r\n\t\taverageStats: info.totalStats / info.dataPoints,\r\n\t}));\r\n\r\n\tconst allRanks = rows.map((r) => r.rank);\r\n\tconst allDeviations = allRanks.map((r) => averagePosition - r);\r\n\tconst squareDeviations = allDeviations.map((d) => Math.pow(d, 2));\r\n\tconst sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);\r\n\tconst variance = sumOfSquares / rows.length;\r\n\tconst standardDeviation = Math.sqrt(variance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(rows.length);\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: rows.length,\r\n\t\taveragePosition: averagePosition,\r\n\t\tstandardDeviation: standardDeviation,\r\n\t\tstandardDeviationOfTheMean: standardDeviationOfTheMean,\r\n\t\tconservativePositionEstimate: averagePosition + 3 * standardDeviationOfTheMean,\r\n\t\tplacementDistribution: placementDistribution,\r\n\t\tcombatWinrate: combatWinrate,\r\n\t\twarbandStats: warbandStats,\r\n\t\ttribeStats: buildTribeStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats),\r\n\t\tanomalyStats: buildAnomalyStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst buildTribeStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\trefAveragePosition: number,\r\n\trefPlacementDistribution: readonly { rank: number; percentage: number }[],\r\n\trefCombatWinrate: readonly { turn: number; winrate: number }[],\r\n\trefWarbandStats: readonly { turn: number; averageStats: number }[],\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst uniqueTribes: readonly Race[] = [\r\n\t\t...new Set(rows.flatMap((r) => r.tribes.split(',')).map((r) => parseInt(r))),\r\n\t];\r\n\treturn uniqueTribes.map((tribe) => {\r\n\t\tconst rowsForTribe = rows.filter((r) => r.tribes.split(',').includes('' + tribe));\r\n\t\tconst rowsWithoutTribe = rows.filter((r) => !r.tribes.split(',').includes('' + tribe));\r\n\t\tconst averagePosition = average(rowsForTribe.map((r) => r.rank));\r\n\t\tconst placementDistribution = buildPlacementDistributionWithPercentages(rowsForTribe);\r\n\t\tconst rawCombatWinrates = buildCombatWinrate(rowsForTribe);\r\n\t\tconst combatWinrate = rawCombatWinrates.map((info) => ({\r\n\t\t\tturn: info.turn,\r\n\t\t\twinrate: info.totalWinrate / info.dataPoints,\r\n\t\t}));\r\n\t\tconst rawWarbandStats = buildWarbandStats(rowsForTribe);\r\n\t\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n\t\t\tturn: info.turn,\r\n\t\t\taverageStats: info.totalStats / info.dataPoints,\r\n\t\t}));\r\n\t\treturn {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: rowsForTribe.length,\r\n\t\t\tdataPointsOnMissingTribe: rowsWithoutTribe.length,\r\n\t\t\taveragePosition: averagePosition,\r\n\t\t\timpactAveragePosition: averagePosition - refAveragePosition,\r\n\t\t\tplacementDistribution: placementDistribution,\r\n\t\t\timpactPlacementDistribution: refPlacementDistribution.map((p) => {\r\n\t\t\t\tconst newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);\r\n\t\t\t\t// Cna happen when there isn't a lot of data points, typically for high MMR\r\n\t\t\t\tif (!newPlacementInfo) {\r\n\t\t\t\t\t// console.log('missing placement info', placementDistribution, p);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\trank: p.rank,\r\n\t\t\t\t\timpact: (newPlacementInfo?.percentage ?? 0) - p.percentage,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t\tcombatWinrate: combatWinrate,\r\n\t\t\timpactCombatWinrate: refCombatWinrate.map((c) => {\r\n\t\t\t\tconst newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);\r\n\t\t\t\tif (!newCombatWinrate) {\r\n\t\t\t\t\t// console.debug('missing winrate info', combatWinrate);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\tturn: c.turn,\r\n\t\t\t\t\timpact: (newCombatWinrate?.winrate ?? 0) - c.winrate,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t\twarbandStats: warbandStats,\r\n\t\t\timpactWarbandStats: refWarbandStats.map((c) => {\r\n\t\t\t\tconst newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);\r\n\t\t\t\tif (!newWarbandStats) {\r\n\t\t\t\t\t// console.debug('missing warband info', warbandStats);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\tturn: c.turn,\r\n\t\t\t\t\timpact: (newWarbandStats?.averageStats ?? 0) - c.averageStats,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t};\r\n\t});\r\n};\r\n\r\nconst buildAnomalyStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\trefAveragePosition: number,\r\n\trefPlacementDistribution: readonly { rank: number; percentage: number }[],\r\n\trefCombatWinrate: readonly { turn: number; winrate: number }[],\r\n\trefWarbandStats: readonly { turn: number; averageStats: number }[],\r\n): readonly BgsHeroAnomalyStat[] => {\r\n\tconst uniqueAnomalies: readonly string[] = [...new Set(rows.flatMap((r) => r.bgsAnomalies))];\r\n\treturn uniqueAnomalies.map((anomaly) => {\r\n\t\tconst rowsForAnomaly = rows.filter((r) => r.bgsAnomalies.includes(anomaly));\r\n\t\tconst averagePosition = average(rowsForAnomaly.map((r) => r.rank));\r\n\t\tconst placementDistribution = buildPlacementDistributionWithPercentages(rowsForAnomaly);\r\n\t\tconst rawCombatWinrates = buildCombatWinrate(rowsForAnomaly);\r\n\t\tconst combatWinrate = rawCombatWinrates.map((info) => ({\r\n\t\t\tturn: info.turn,\r\n\t\t\twinrate: info.totalWinrate / info.dataPoints,\r\n\t\t}));\r\n\t\tconst rawWarbandStats = buildWarbandStats(rowsForAnomaly);\r\n\t\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n\t\t\tturn: info.turn,\r\n\t\t\taverageStats: info.totalStats / info.dataPoints,\r\n\t\t}));\r\n\t\treturn {\r\n\t\t\tanomaly: anomaly,\r\n\t\t\tdataPoints: rowsForAnomaly.length,\r\n\t\t\taveragePosition: averagePosition,\r\n\t\t\timpactAveragePosition: averagePosition - refAveragePosition,\r\n\t\t\tplacementDistribution: placementDistribution,\r\n\t\t\timpactPlacementDistribution: refPlacementDistribution.map((p) => {\r\n\t\t\t\tconst newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);\r\n\t\t\t\t// Cna happen when there isn't a lot of data points, typically for high MMR\r\n\t\t\t\tif (!newPlacementInfo) {\r\n\t\t\t\t\t// console.log('missing placement info', placementDistribution, p);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\trank: p.rank,\r\n\t\t\t\t\timpact: (newPlacementInfo?.percentage ?? 0) - p.percentage,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t\tcombatWinrate: combatWinrate,\r\n\t\t\timpactCombatWinrate: refCombatWinrate.map((c) => {\r\n\t\t\t\tconst newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);\r\n\t\t\t\tif (!newCombatWinrate) {\r\n\t\t\t\t\t// console.debug('missing winrate info', combatWinrate);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\tturn: c.turn,\r\n\t\t\t\t\timpact: (newCombatWinrate?.winrate ?? 0) - c.winrate,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t\twarbandStats: warbandStats,\r\n\t\t\timpactWarbandStats: refWarbandStats.map((c) => {\r\n\t\t\t\tconst newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);\r\n\t\t\t\tif (!newWarbandStats) {\r\n\t\t\t\t\t// console.debug('missing warband info', warbandStats);\r\n\t\t\t\t}\r\n\t\t\t\treturn {\r\n\t\t\t\t\tturn: c.turn,\r\n\t\t\t\t\timpact: (newWarbandStats?.averageStats ?? 0) - c.averageStats,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t};\r\n\t});\r\n};\r\n\r\nconst average = (data: readonly number[]): number => {\r\n\treturn data.reduce((a, b) => a + b, 0) / data.length;\r\n};\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firestone-hs/bgs-global-stats",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.40",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint --color --fix --ext .ts .",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@firestone-hs/aws-lambda-utils": "0.0.27",
|
|
24
|
-
"@firestone-hs/hs-replay-xml-parser": "0.0.
|
|
24
|
+
"@firestone-hs/hs-replay-xml-parser": "^0.0.117",
|
|
25
25
|
"@firestone-hs/reference-data": "^1.0.50",
|
|
26
26
|
"@types/elementtree": "^0.1.0",
|
|
27
27
|
"aws-sdk": "^2.1040.0",
|
|
@@ -57,4 +57,4 @@
|
|
|
57
57
|
"webpack-cli": "^5.0.2",
|
|
58
58
|
"yaml-cfn": "^0.2.2"
|
|
59
59
|
}
|
|
60
|
-
}
|
|
60
|
+
}
|