mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: beggening of player stats
This commit is contained in:
1 parent
521b0e8906
commit
dc5e4fc2de
7 files changed
+249
-5
No files matched your search
Vendored
+3
@@ -19,6 +19,7 @@
|
||||
"betakrieg",
|
||||
"BINORMAL",
|
||||
"Binormals",
|
||||
"bkni",
|
||||
"BKRL",
|
||||
"BKRTS",
|
||||
"BKRTSMANIFEST",
|
||||
@@ -87,6 +88,7 @@
|
||||
"loddist",
|
||||
"lods",
|
||||
"lossrate",
|
||||
"malum",
|
||||
"mantlets",
|
||||
"metallicness",
|
||||
"minitelrose",
|
||||
@@ -136,6 +138,7 @@
|
||||
"Specularity",
|
||||
"SSAO",
|
||||
"startcase",
|
||||
"statkit",
|
||||
"statsfull",
|
||||
"steamapps",
|
||||
"Stringifying",
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
> [!CAUTION]
|
||||
> This is a working draft and is incomplete. Do not implement.
|
||||
|
||||
## Why BkPM over WN8?
|
||||
## Why BkNI over WN8?
|
||||
|
||||
BkPM addresses a lot of issues with WN8:
|
||||
BkNI addresses a lot of issues with WN8:
|
||||
|
||||
- WN8 incorporates win rate into the metric; a metric like this must be a correlate of win rate, not a dependent
|
||||
- The weights and biases are not dynamic and their formulation is not transparent
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Flex, Heading, Text } from '@radix-ui/themes';
|
||||
import PageWrapper from '../../../../components/PageWrapper';
|
||||
import { getAccountInfo } from '../../../../core/blitz/getAccountInfo';
|
||||
import { getClanAccountInfo } from '../../../../core/blitz/getClanAccountInfo';
|
||||
import { idToRegion } from '../../../../core/blitz/idToRegion';
|
||||
import getBkniPercentile from '../../../../core/statistics/getBkniPercentile';
|
||||
import strings from '../../../../lang/en-US.json';
|
||||
|
||||
export default async function Page({ params }: { params: { id: string } }) {
|
||||
const id = parseInt(params.id);
|
||||
const region = idToRegion(id);
|
||||
const accountInfo = await getAccountInfo(region, id);
|
||||
const clanAccountInfo = await getClanAccountInfo(region, id, ['clan']);
|
||||
const bkniRaw = Math.random() * 2 - 1;
|
||||
const bkniFraction = Math.random();
|
||||
const bkniMetric = Math.round(bkniFraction * 200);
|
||||
const bkniPercentile = getBkniPercentile(bkniMetric);
|
||||
|
||||
return (
|
||||
<PageWrapper>
|
||||
<Flex gap="2">
|
||||
<Heading>{accountInfo.nickname}</Heading>
|
||||
{clanAccountInfo?.clan && (
|
||||
<Heading color="gray">[{clanAccountInfo.clan.tag}]</Heading>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" align="center">
|
||||
<svg
|
||||
width={180}
|
||||
height={100}
|
||||
style={{
|
||||
transform: 'scaleX(-1)',
|
||||
}}
|
||||
>
|
||||
<circle
|
||||
cx={90}
|
||||
cy={90}
|
||||
r={80}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
stroke="var(--color-panel-solid)"
|
||||
strokeWidth={20}
|
||||
strokeDasharray={80 * Math.PI}
|
||||
strokeDashoffset={80 * Math.PI}
|
||||
/>
|
||||
<circle
|
||||
cx={90}
|
||||
cy={90}
|
||||
r={80}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
stroke="purple"
|
||||
strokeWidth={20}
|
||||
strokeDasharray={(160 - 80 * bkniFraction) * Math.PI}
|
||||
strokeDashoffset={(160 - 80 * bkniFraction) * Math.PI}
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<Flex direction="column" align="center" mt="-7">
|
||||
<Text weight="bold" size="8">
|
||||
{bkniMetric}
|
||||
</Text>
|
||||
<Text color="gray" size="4">
|
||||
{strings.common.bkni_percentile[bkniPercentile]}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</PageWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
'use client';
|
||||
|
||||
import { ArrowDownIcon, MagnifyingGlassIcon } from '@radix-ui/react-icons';
|
||||
import { Button, Card, Flex, Text, TextField } from '@radix-ui/themes';
|
||||
import { useRef, useState } from 'react';
|
||||
import { Link } from '../../../components/Link';
|
||||
import PageWrapper from '../../../components/PageWrapper';
|
||||
import { UNLOCALIZED_REGION_NAMES_SHORT } from '../../../constants/regions';
|
||||
import searchPlayersAcrossRegions, {
|
||||
AccountListWithServer,
|
||||
} from '../../../core/blitz/searchPlayersAcrossRegions';
|
||||
|
||||
export default function Page() {
|
||||
const input = useRef<HTMLInputElement>(null);
|
||||
const [showSearch, setShowSearch] = useState(false);
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [searchResults, setSearchResults] = useState<AccountListWithServer>([]);
|
||||
|
||||
async function search() {
|
||||
if (!input.current) return;
|
||||
|
||||
setSearchResults(await searchPlayersAcrossRegions(input.current.value, 9));
|
||||
setSearching(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageWrapper>
|
||||
<Flex
|
||||
style={{ flex: 1 }}
|
||||
align="center"
|
||||
justify="center"
|
||||
direction="column"
|
||||
gap="3"
|
||||
>
|
||||
<Flex gap="2" align="center">
|
||||
<Text color="gray">Look up a player to get started</Text>
|
||||
<ArrowDownIcon />
|
||||
</Flex>
|
||||
|
||||
<TextField.Root
|
||||
size="3"
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '75vw',
|
||||
maxWidth: 480,
|
||||
}}
|
||||
ref={input}
|
||||
placeholder="Search players..."
|
||||
onChange={() => {
|
||||
if (!input.current) return;
|
||||
|
||||
const sanitized = input.current.value.trim();
|
||||
|
||||
if (sanitized.length > 0) {
|
||||
setShowSearch(true);
|
||||
setSearching(true);
|
||||
search();
|
||||
} else {
|
||||
setShowSearch(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TextField.Slot>
|
||||
<MagnifyingGlassIcon />
|
||||
</TextField.Slot>
|
||||
|
||||
{showSearch && (
|
||||
<Card
|
||||
mt="2"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '100%',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{searching && (
|
||||
<Flex justify="center">
|
||||
<Text color="gray">Searching...</Text>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{!searching && searchResults.length === 0 && (
|
||||
<Flex justify="center">
|
||||
<Text color="gray">No players found</Text>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{!searching && searchResults.length > 0 && (
|
||||
<Flex direction="column" gap="2">
|
||||
{searchResults.map((player) => (
|
||||
<Link
|
||||
key={player.account_id}
|
||||
href={`/tools/player-stats/${player.account_id}`}
|
||||
>
|
||||
<Button key={player.account_id} variant="ghost">
|
||||
{player.nickname} (
|
||||
{UNLOCALIZED_REGION_NAMES_SHORT[player.region]})
|
||||
</Button>
|
||||
</Link>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</TextField.Root>
|
||||
</Flex>
|
||||
</PageWrapper>
|
||||
);
|
||||
}
|
||||
@@ -31,9 +31,9 @@ export const TOOLS: Tool[] = [
|
||||
significant: true,
|
||||
},
|
||||
{
|
||||
id: 'statistics',
|
||||
title: 'Statistics',
|
||||
description: 'Player statistics',
|
||||
id: 'player-stats',
|
||||
title: 'Player stats',
|
||||
description: 'Periodical player statistics',
|
||||
pageDescription:
|
||||
'Periodical player statistics and tanks played in World of Tanks Blitz',
|
||||
image: '8gJGkKP',
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* (10) 0 - 10: super malum
|
||||
* (10) 10 - 20: malum
|
||||
* (20) 20 - 40: very bad
|
||||
* (20) 40 - 60: bad
|
||||
* (20) 60 - 80: below average
|
||||
* (40) 80 - 120: average
|
||||
* (20) 120 - 140: above average
|
||||
* (20) 140 - 160: good
|
||||
* (20) 160 - 180: very good
|
||||
* (10) 180 - 190: unicum
|
||||
* (10) 190 - 200: super unicum
|
||||
*/
|
||||
|
||||
export enum BkniPercentile {
|
||||
SuperMalum,
|
||||
Malum,
|
||||
VeryBad,
|
||||
Bad,
|
||||
BelowAverage,
|
||||
Average,
|
||||
AboveAverage,
|
||||
Good,
|
||||
VeryGood,
|
||||
Unicum,
|
||||
SuperUnicum,
|
||||
}
|
||||
|
||||
const BKNI_PERCENTILES = [
|
||||
{ min: 0, percentile: BkniPercentile.SuperMalum },
|
||||
{ min: 10, percentile: BkniPercentile.Malum },
|
||||
{ min: 20, percentile: BkniPercentile.VeryBad },
|
||||
{ min: 40, percentile: BkniPercentile.Bad },
|
||||
{ min: 60, percentile: BkniPercentile.BelowAverage },
|
||||
{ min: 80, percentile: BkniPercentile.Average },
|
||||
{ min: 120, percentile: BkniPercentile.AboveAverage },
|
||||
{ min: 140, percentile: BkniPercentile.Good },
|
||||
{ min: 160, percentile: BkniPercentile.VeryGood },
|
||||
{ min: 180, percentile: BkniPercentile.Unicum },
|
||||
{ min: 190, percentile: BkniPercentile.SuperUnicum },
|
||||
];
|
||||
|
||||
export default function getBkniPercentile(wn8: number) {
|
||||
const lastIndex = BKNI_PERCENTILES.findLastIndex(({ min }) => min <= wn8);
|
||||
return BKNI_PERCENTILES[lastIndex === -1 ? 0 : lastIndex].percentile;
|
||||
}
|
||||
@@ -13,6 +13,20 @@
|
||||
"9": "Super unicum"
|
||||
},
|
||||
|
||||
"bkni_percentile": {
|
||||
"0": "Super malum",
|
||||
"1": "Malum",
|
||||
"2": "Very bad",
|
||||
"3": "Bad",
|
||||
"4": "Below average",
|
||||
"5": "Average",
|
||||
"6": "Above average",
|
||||
"7": "Good",
|
||||
"8": "Very good",
|
||||
"9": "Unicum",
|
||||
"10": "Super unicum"
|
||||
},
|
||||
|
||||
"nations": {
|
||||
"ussr": "U.S.S.R.",
|
||||
"germany": "Germany",
|
||||
|
||||
Reference in new issue
Block a user