feat: technical dogs

This commit is contained in:
TrèsAbhi committed 2024-05-15 19:44:51 -05:00
1 parent ca418b28b5
commit b5b87fbe17
4 files changed
+92

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
<div style="display: flex; flex-direction: column; align-items: center">
<img src="https://i.imgur.com/ytgXskL.png">
<h1>BlitzKit Performance Metric</h1>
<span>Revision 1</span>
</div>
## Preface
> [!CAUTION]
> This is a working draft and is incomplete. Do not implement.
The goal of this paper is to deliver a numerical metric for judging player performance by converging all statistics down to a single, easy-to-consume, human-readable value. This must be a mathematically elegant process that yields a reasonable number that asymptotes under extreme situations. More specifically, this value should equal 1, on average across all players and tanks. Extreme cases should be mapped between 0 and 2. This value will be multiplied by 100 and rounded to the nearest integer for readability purposes.
Wargaming provides cumulative statistics for players and tanks. The big idea is to average all provided statistics together to get one unified value. We will also incorporate other composite values based on these default statistics into the average. However, averaging this way poses two problems:
1. All statistics do not scale equally. For example, good players performing with 3,000 damage per battle could also attain a kill-to-death ratio of 3. But averaging them as-is would imply damage per battle has weight 1000 times that of the kill ratio. In other words, damage per battle would totally diminish the impact of kill ratios on the average. Hence, there’s a need for normalization. Normalization, in most cases, simply means dividing by the corresponding averages.
2. All statistics are not equally important. For example, dealing damage is arguably far more important than base capture; hence, it does not make sense to average them together. But this does not mean we totally discard this value as it does make an impact albeit very small. Instead, we will be incorporating weighted averages where the weights will be calculated through correlational analysis against win rate. Specifically, the weights of the statistics will be the corresponding correlation coefficient, r. One caveat to look out for is that r can be negative for statistics like damage received which must be minimized. Fortunately, negative weights are okay and make intuitive sense; after all, bleeding health should punish the metric.
## Constants
A few constants that the rest of the paper depends on. Available under the “constants” object.
```ts
players_per_team = 7;
```
## Wargaming-Provided Statistics
Wargaming provides the following pieces of statistics for each player and tank. These are cumulative; in other words, they simply add up over time and it is up to the recorder to contrast two snapshots of said statistics in time to get periodical statistics. Available under the “all” object.
```ts
battles;
capture_points;
damage_dealt;
damage_received;
dropped_capture_points;
frags; // kills
frags8p; // frags against tanks at or above tier 8
hits;
losses;
max_frags;
max_frags_tank_id;
max_xp;
max_xp_tank_id;
shots;
spotted;
survived_battles;
win_and_survived;
wins;
xp;
```
## Composite Statistics
Raw statistics from Wargaming can be combined into more meaningful numeric snippets. Most of these values are divided by all.battles indicating they are the average across all battles in the given period. Assigned under the “composite” object.
```ts
win_rate = all.wins / all.battles;
capture_points = all.capture_points / all.battles;
damage_dealt = all.damage_dealt / all.battles;
damage_received = all.damage_received / all.battles;
dropped_capture_points = all.dropped_capture_points / all.battles;
frags = all.frags / all.battles;
hits = all.hits / all.battles;
shots = all.shots / all.battles;
spotted = all.spotted / all.battles;
survival_rate = all.survived_battles / all.battles;
xp = all.xp / all.battles;
damage_ratio = all.damage_dealt / all.damage_received;
accuracy = all.hits / all.shots;
damage_per_hit = composite.damage_dealt / composite.hits;
```
## Normalized Statistics
The goal is to adjust all values such that each value individually averages out to 1 across all players. Most normalizations are simple. The more complex ones will be represented mathematically. Assigned under the “normalized” object.
```
win_rate = composite.win_rate / average.win_rate
damage_dealt = composite.damage_dealt / tier_average.health
damage_received = composite.damage_received / tier_average.health
```
Now the complex normalizations.
### `frags`
On average, a player kills 1 player on the enemy team. However, the max is 7. We need to map 0 frags to 0, 1 to 1, and 7 to 2 to maintain, for the lack of a better term, an asymptotic behavior in extreme cases. $\sqrt{x}$ serves as an excellent choice as a parent function as it already features points $\left( 0, 0 \right)$ and $\left( 1, 1 \right)$. However, at $x = 7 \Leftrightarrow y = \sqrt{7}$ but we desire 2. We can achieve this by applying a coefficient of $\frac{2}{\sqrt{7}}$. This however shifts the point $\left( 1, 1 \right)$; hence, we have to linearly interpolate the coefficient between 1 and $\frac{2}{\sqrt{7}}$ on the interval $\left[ 1, 6 \right]$. Proof for the coefficient is left as an exercise for the reader.
$$
normalized.frags = \left[ \frac{composite.frags - 1}{constants.players\_per\_team - 1} \left( \frac{2}{\sqrt{constants.players\_per\_team}} -1 \right) +1 \right] \sqrt{composite.frags}
$$
+45
View File
@@ -0,0 +1,45 @@
# Blitzkit Rating Leaderboard
A straightforward way of storing the entire World of Tanks Blitz rating leaderboard to disk.
```cpp
primary BlitzkitRatingLeaderboard {
utf8"BKRL" magic;
Header header;
primary Body<header> body;
}
Header {
uint16 version; // always 1
Format format;
uint32 count;
}
Format enum<uint8> {
Base,
Superset1,
}
Body<Header header> match<header.format> {
Base {
primary {
uint32 id;
uint16 score;
}[header.count] entries;
};
Superset1 {
primary {
uint32 id;
uint16 score;
uint32 battles;
uint32 wins;
uint32 survived;
uint32 damage;
uint32 kills;
}[header.count] entries;
};
}
```
+99
View File
@@ -0,0 +1,99 @@
# Cedilla Object Notation
1. JSON compressed as CDON, written directly to disk (a direct 50% reduction).
2. CDON further compressed with LZ4 (a clean 90% reduction).
3. The raw JSON file as fetched from a random data source.
4. CDON decompressed once again to JSON. Note that this file is a little larger than the original JSON file as it contains a lot more decimal places due to float precision (this shouldn't affect the size in memory).
![an image of file sizes](https://i.imgur.com/AHJiFpq.png)
FS stands for "Fast String".
```cpp
primary CedillaObjectNotation {
utf8"CDON" magic;
Header header;
primary Body<header.fsFormat> body;
}
Header {
uint16 version; // always 1
FSFormat fsFormat;
StringTable<fsFormat> stringTable;
}
FSFormat enum<uint8> {
Format8,
Format16,
Format32,
}
FSPrimitive<FSFormat fsFormat> match(fsFormat) {
Format8: uint8;
Format16: uint16;
Format32: uint32;
}
StringTable<FSFormat fsFormat> {
FSPrimitive<fsFormat> count;
String[count] strings;
}
Body<FSFormat fsFormat> {
primary Value<fsFormat> object;
}
ValueType enum<uint8> {
Null,
Boolean,
Uint8,
Uint16,
Uint32,
Uint64,
Int8,
Int16,
Int32,
Int64,
Float32,
Float64,
String,
FS,
Array,
Object,
}
String {
uint32 length;
primary utf8<length> value;
}
StringValue Value & { (ValueType.String | ValueType.FS) type }
Value<FSFormat fsFormat> {
ValueType type;
match<type> {
Boolean: { uint8 value; }
Uint8: { uint8 value; }
Uint16: { uint16 value; }
Uint32: { uint32 value; }
Uint64: { uint64 value; }
Int8: { int8 value; }
Int16: { int16 value; }
Int32: { int32 value; }
Int64: { int64 value; }
Float32: { float32 value; }
Float64: { float64 value; }
String: { String value; }
FS: { FSPrimitive<fsFormat> index; }
Array: {
uint32 count;
Value[count] values;
}
Object: {
uint32 count;
StringValue[count] keys;
Value[count] values;
}
}
}
```
+71
View File
@@ -0,0 +1,71 @@
# BlitzKit Regular Tank Stats
A straightforward way of storing the entire World of Tanks Blitz tank statistics for players to disk.
## Manifest `manifest.rtsm`
```cpp
primary RegularTankStatisticsManifest {
utf8"RTSM" magic;
Header header;
primary Body body;
}
Header {
uint16 version; // always 1
}
Body {
uint32 time;
uint8 base;
}
```
## Chunk `*.rtsc`
```cpp
primary RegularTankStatisticsChunk {
utf8"RTSC" magic;
Header header;
primary Body body<header.playerCount>;
}
Header {
uint16 version; // always 1
uint32 playerCount;
}
Body<uint32 playerCount> {
Player[playerCount] players;
}
Player {
uint32 id;
uint16 tankCount;
Tank[tankCount] tanks;
}
Tank {
uint32 id;
uint32 battleLifeTime;
uint32 lastBattleTime;
uint8 markOfMastery;
uint32 battles;
uint32 capturePoints;
uint32 damageDealt;
uint32 damageReceived;
uint32 droppedCapturePoints;
uint32 frags;
uint32 frags8p;
uint32 hits;
uint32 losses;
uint8 maxFrags;
uint16 maxXp;
uint32 shots;
uint32 spotted;
uint32 survivedBattles;
uint32 winAndSurvived;
uint32 wins;
uint32 xp;
}
```