summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorschneefux <schneefux+commit@schneefux.xyz>2018-07-26 21:53:13 +0200
committerschneefux <schneefux+commit@schneefux.xyz>2018-12-13 17:09:35 +0100
commit1c3196ae3436a03668df8559709b50343d68878a (patch)
treed96bb8c489549efaeef1e843dddfbc0719890af1 /src
parentd61fa871d66d6237d7bca28bbd5a4535827abc01 (diff)
downloadbrokentalents-1c3196ae3436a03668df8559709b50343d68878a.tar.gz
brokentalents-1c3196ae3436a03668df8559709b50343d68878a.zip
Summaries: Init aggregate statistics
Diffstat (limited to 'src')
-rw-r--r--src/ModeTab.vue2
-rw-r--r--src/ReportService.js63
-rw-r--r--src/Summaries.vue113
3 files changed, 142 insertions, 36 deletions
diff --git a/src/ModeTab.vue b/src/ModeTab.vue
index 14bb960..0b57e35 100644
--- a/src/ModeTab.vue
+++ b/src/ModeTab.vue
@@ -39,7 +39,7 @@
<win-pick-scatter></win-pick-scatter>
</div>
- <div class="box" v-show="hasTalents">
+ <div class="box">
<h2 class="title is-2">Summaries</h2>
<summaries></summaries>
</div>
diff --git a/src/ReportService.js b/src/ReportService.js
index f11240c..b8a23fd 100644
--- a/src/ReportService.js
+++ b/src/ReportService.js
@@ -1,5 +1,6 @@
import * as maps from './maps/maps';
import * as metadata from '../data/5f7773da/metadata.json';
+import * as baseStats from '../data/vainglory.json'; // Thanks to https://github.com/oberocks/vainglory-base-stats
import * as R from 'ramda';
const POPULAR_THRESHOLD = 1.0; // percent
@@ -23,7 +24,7 @@ const topUnpopularWins = new Map();
const topKillDeathPoints = new Map();
const topObjectivePoints = new Map();
const topBlitzPointsDelta = new Map();
-const summaryTalents = new Map();
+const summaries = new Map();
let totalMatches = 0;
let actors = [];
const modes = metadata.config.api.modes;
@@ -76,8 +77,28 @@ for(let mode of modes) {
// r^2 is also correlation coefficient ^ 2
const rsquare = 1 - ssres / sstot;
+ const hero = maps.getHero(entry.Actor);
+ const baseStat = baseStats.heroes[hero.toLowerCase()];
+ const rateClass = (r) => {
+ let heroClass = '';
+ if (r.team_utility > r.offense) {
+ if (r.defense > r.team_utility) heroClass += 'Tank';
+ else heroClass += 'Healer';
+ } else {
+ if (r.offense > r.defense) heroClass += 'Squishy';
+ else heroClass += 'Warrior';
+ }
+ return heroClass;
+ };
+
return Object.assign({}, entry, {
_Count: 1, // TODO find a cleaner solution to convert the sum of relatives to absolutes in summaries
+ Hero: hero,
+ AttackType: baseStat.attack_type,
+ Difficulty: baseStat.difficulty,
+ HeroClass: rateClass(baseStat.ratings),
+ HeroSpeed: baseStat.ratings.mobility < 3 ? 'Slow' : baseStat.ratings.mobility < 6 ? 'Average' : 'Fast',
+ PrimaryRole: baseStat.primary_role,
Rarity: maps.getTalentRarity(entry.Talent),
TalentWinrateBase: intercept || entry.Winner,
TalentWinrateScaling: slope || 0, // to 1 = max level
@@ -135,11 +156,35 @@ for(let mode of modes) {
.filter((entry) => !entry.SampleTooSmall && !entry.VarianceTooLarge && !!entry.BlitzPointsDelta)
.sort((entry1, entry2) => entry2.BlitzPointsDelta - entry1.BlitzPointsDelta)[0]);
- summaryTalents.set(mode,
- new Map(Object.entries(
- R.reduceBy(R.mergeWith((a, b) => R.is(String, a) ? a : R.add(a, b) ), 0, R.prop('Rarity'), reports.get(mode))
- ))
- );
+ const addOrIdentity = (a, b) => R.is(String, a) ? a : R.add(a, b);
+ const summarizeByProp = (propName) => new Map(Object.entries(
+ R.reduceBy(R.mergeWith(addOrIdentity), 0, R.prop(propName), reports.get(mode))
+ ));
+ summaries.set(mode, [ {
+ 'key': 'Rarity',
+ 'name': 'Talent Rarity',
+ 'data': [...summarizeByProp('Rarity').values()],
+ }, {
+ 'key': 'Difficulty',
+ 'name': 'Difficulty',
+ 'data': [...summarizeByProp('Difficulty').values()],
+ }, {
+ 'key': 'PrimaryRole',
+ 'name': 'Primary Role',
+ 'data': [...summarizeByProp('PrimaryRole').values()],
+ }, {
+ 'key': 'AttackType',
+ 'name': 'Attack Type',
+ 'data': [...summarizeByProp('AttackType').values()],
+ }, {
+ 'key': 'HeroSpeed',
+ 'name': 'Speed',
+ 'data': [...summarizeByProp('HeroSpeed').values()],
+ }, {
+ 'key': 'HeroClass',
+ 'name': 'Class',
+ 'data': [...summarizeByProp('HeroClass').values()],
+ } ]);
if (actors.length == 0) {
actors = [...new Set(reports.get(mode).map((entry) => entry.Actor))];
@@ -157,7 +202,7 @@ export default {
'\n' +
reports.get(mode)
.map((entry) => [
- maps.getHero(entry.Actor),
+ entry.Hero,
maps.getTalentName(entry.Talent),
maps.getTalentRarity(entry.Talent),
entry.Kills,
@@ -233,8 +278,8 @@ export default {
return topBlitzPointsDelta.get(mode);
},
- getSummaryTalents(mode) {
- return summaryTalents.get(mode);
+ getSummaries(mode) {
+ return summaries.get(mode);
},
getActors() {
diff --git a/src/Summaries.vue b/src/Summaries.vue
index 51bd277..009a36b 100644
--- a/src/Summaries.vue
+++ b/src/Summaries.vue
@@ -1,30 +1,83 @@
<template>
- <b-table :data="talentSummaries"
- :mobile-cards="false">
- <template slot-scope="props" slot="header">
- <b-tooltip :active="!!props.column.meta" :label="props.column.meta || ''" position="is-bottom" dashed>
- {{ props.column.label }}
- </b-tooltip>
- </template>
+ <div>
+ <div class="block">
+ <b-field grouped group-multiline>
+ <label class="control label">Metrics</label>
+ <p class="control">
+ <b-checkbox v-model="talentsVisible" v-show="hasTalents">Talent Levels</b-checkbox>
+ </p>
+ <p class="control">
+ <b-checkbox v-model="winrateVisible">Win Rate</b-checkbox>
+ </p>
+ <p class="control">
+ <b-checkbox v-model="killsDeathsVisible">Kills and Deaths</b-checkbox>
+ </p>
+ <p class="control">
+ <b-checkbox v-model="objectivesVisible">Objectives</b-checkbox>
+ </p>
+ </b-field>
+ </div>
- <template slot-scope="props">
- <b-table-column field="Rarity" label="Rarity">
- {{ props.row.Rarity }}
- </b-table-column>
+ <div class="block">
+ <b-field grouped group-multiline>
+ <label class="control label">Category</label>
+ <p class="control"
+ v-for="trait in summaries">
+ <b-radio v-model="selectedTrait"
+ :native-value="trait.key">
+ {{ trait.name }}
+ </b-radio>
+ </p>
+ </b-field>
+ </div>
- <b-table-column field="TalentWinrateBase" label="Level 1 Win Rate" meta="Estimated." sortable numeric>
- {{ (100 * props.row.TalentWinrateBase / props.row._Count).toFixed(2) }}%
- </b-table-column>
+ <div class="block">
+ <label class="label">Data</label>
+ <b-table :data="summary.data"
+ :mobile-cards="false"
+ narrowed>
+ <template slot-scope="props" slot="header">
+ <b-tooltip :active="!!props.column.meta" :label="props.column.meta || ''" position="is-bottom" dashed>
+ {{ props.column.label }}
+ </b-tooltip>
+ </template>
- <b-table-column field="TalentWinrateMax" label="Max Level Win Rate" meta="Estimated." sortable numeric>
- {{ (100 * props.row.TalentWinrateMax / props.row._Count).toFixed(2) }}%
- </b-table-column>
+ <template slot-scope="props">
+ <b-table-column :field="selectedTrait" label="">
+ {{ props.row[selectedTrait] }}
+ </b-table-column>
- <b-table-column field="TotalPicks" label="Pick Rate" sortable numeric>
- {{ (100 * props.row.TotalPicks / totalPicks).toFixed(2) }}%
- </b-table-column>
- </template>
- </b-table>
+ <b-table-column field="TotalWinner" label="Win Rate" :visible="winrateVisible" sortable numeric>
+ {{ (100 * props.row.TotalWinner / props.row._Count).toFixed(2) }}%
+ </b-table-column>
+
+ <b-table-column field="TalentWinrateBase" label="Level 1 Win Rate" :visible="talentsVisible && hasTalents" meta="Estimated." sortable numeric>
+ {{ (100 * props.row.TalentWinrateBase / props.row._Count).toFixed(2) }}%
+ </b-table-column>
+
+ <b-table-column field="TalentWinrateMax" label="Max Level Win Rate" :visible="talentsVisible && hasTalents" meta="Estimated." sortable numeric>
+ {{ (100 * props.row.TalentWinrateMax / props.row._Count).toFixed(2) }}%
+ </b-table-column>
+
+ <b-table-column field="Kills" label="Kills" :visible="killsDeathsVisible" sortable numeric>
+ {{ (props.row.Kills / props.row._Count).toFixed(2) }}
+ </b-table-column>
+
+ <b-table-column field="Deaths" label="Deaths" :visible="killsDeathsVisible" sortable numeric>
+ {{ (props.row.Deaths / props.row._Count).toFixed(2) }}
+ </b-table-column>
+
+ <b-table-column field="TurretKills" label="Turret Kills" :visible="objectivesVisible" sortable numeric>
+ {{ (props.row.TurretKills / props.row._Count).toFixed(2) }}
+ </b-table-column>
+
+ <b-table-column field="TotalPicks" label="Pick Rate" sortable numeric>
+ {{ (100 * props.row.TotalPicks / totalPicks).toFixed(2) }}%
+ </b-table-column>
+ </template>
+ </b-table>
+ </div>
+ </div>
</template>
<script>
@@ -37,15 +90,23 @@ export default Vue.component('summaries', {
mixins: [ RouterParamMixin ],
data: function() {
return {
+ talentsVisible: false,
+ winrateVisible: true,
+ killsDeathsVisible: false,
+ objectivesVisible: false,
+ selectedTrait: this.hasTalents? 'Rarity' : 'Difficulty',
};
},
computed: {
- talentSummaries: function() {
- return [...ReportService.getSummaryTalents(this.selectedMode).values()]
- .sort((entry1, entry2) => maps.getTalentRarityIndex(entry1.Talent) - maps.getTalentRarityIndex(entry2.Talent));
+ summaries: function() {
+ return ReportService.getSummaries(this.selectedMode)
+ .filter((summary) => this.hasTalents? true : summary.key != 'Rarity');
+ },
+ summary: function() {
+ return this.summaries.find((summary) => summary.key == [this.selectedTrait]);
},
totalPicks: function() {
- return this.talentSummaries.map((entry) => entry.TotalPicks).reduce((agg, cur) => agg + cur, 0);
+ return this.summary.data.map((entry) => entry.TotalPicks).reduce((agg, cur) => agg + cur, 0);
},
},
components: {