diff --git a/src/routes/tools/+page.svelte b/src/routes/tools/+page.svelte index 860cd81..688508e 100644 --- a/src/routes/tools/+page.svelte +++ b/src/routes/tools/+page.svelte @@ -5,7 +5,8 @@
{#each [ { name: 'BeatLeader Compare Players', href: '/tools/beatleader-compare', desc: 'Find songs A played that B has not' }, - { name: 'BeatLeader Playlist Gap', href: '/tools/beatleader-playlist-gap', desc: 'Upload a playlist and find songs a player has not played' } + { name: 'BeatLeader Playlist Gap', href: '/tools/beatleader-playlist-gap', desc: 'Upload a playlist and find songs a player has not played' }, + { name: 'BeatLeader Head-to-Head', href: '/tools/beatleader-headtohead', desc: 'Compare two players on the same map/difficulty' } ] as tool}
{tool.name}
diff --git a/src/routes/tools/beatleader-headtohead/+page.svelte b/src/routes/tools/beatleader-headtohead/+page.svelte new file mode 100644 index 0000000..941dbce --- /dev/null +++ b/src/routes/tools/beatleader-headtohead/+page.svelte @@ -0,0 +1,749 @@ + + +
+

BeatLeader: Head-to-Head

+

Paginated head-to-head results on the same map and difficulty.

+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + {#if errorMsg} +
{errorMsg}
+ {/if} + + {#if items.length > 0} + +
+
+
Shared Maps
+
{totalComparable}
+
+
+
Wins {idShortA}
+
{winsA}
+
+
+
Wins {idShortB}
+
{winsB}
+
+
+
Ties
+
{ties}
+
+
+
+
+
Avg Acc {idShortA}
+
{avgAccA.toFixed(2)}%
+
+
+
Avg Acc {idShortB}
+
{avgAccB.toFixed(2)}%
+
+
+
Median {idShortA}
+
{medAccA.toFixed(2)}%
+
+
+
Median {idShortB}
+
{medAccB.toFixed(2)}%
+
+
+
+
+
Avg Win Margin
+
{avgMargin.toFixed(2)}%
+
+
+
95th %ile Margin
+
{p95Margin.toFixed(2)}%
+
+
+
Longest Streak {idShortA}
+
{longestA}
+
+
+
Longest Streak {idShortB}
+
{longestB}
+
+
+ + +
+
+
Win Share
+
+
+
+
+
+
+ {idShortA} {(shareA * 100).toFixed(1)}% + Ties {(shareT * 100).toFixed(1)}% + {idShortB} {(shareB * 100).toFixed(1)}% +
+
+
+ + + + {#if totalComparable > 0} + + + + + + + {/if} + {totalComparable} maps + +
+
+ + +
+
Win Margin Histogram (A − B, %)
+
+ {#each histogram as count, i} +
= 0 ? 'rgba(0,255,204,0.8)' : 'rgba(255,0,170,0.8)'};`} + >
+ {/each} +
+
+
Left (magenta) favors {idShortB}, Right (cyan) favors {idShortA}
+
+ + +
+
Cumulative Wins Over Time
+ + + + {#if cumSeries.length > 1} + {#key cumSeries.length} + + `${mapX(p.t, 600)},${mapY(p.a, 120)}`).join(' ')} /> + + `${mapX(p.t, 600)},${mapY(p.b, 120)}`).join(' ')} /> + {/key} + {/if} + +
+ {/if} + + {#if items.length > 0} +
+
+ {items.length} songs + · + + +
+
+ + {#if loadingMeta} +
Loading covers…
+ {/if} + +
+ + {#if totalPages > 1} +
+ + Page {page} / {totalPages} + +
+ {/if} + {/if} +
+ + + +