mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-28 14:18:04 -05:00
Handle 1 map only played in tournament without NaN
This commit is contained in:
@@ -18,7 +18,7 @@ import { modesShort } from "~/modules/in-game-lists/modes";
|
||||
import { stageIds } from "~/modules/in-game-lists/stage-ids";
|
||||
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { cutToNDecimalPlaces } from "~/utils/number";
|
||||
import { cutToNDecimalPlaces, winPercentage } from "~/utils/number";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
import {
|
||||
loader,
|
||||
@@ -277,12 +277,10 @@ function Players({
|
||||
return (
|
||||
<div className="stack md horizontal justify-center flex-wrap">
|
||||
{players.map((player) => {
|
||||
const setWinRate = Math.round(
|
||||
(player.setWins / (player.setWins + player.setLosses)) * 100,
|
||||
);
|
||||
const mapWinRate = Math.round(
|
||||
(player.mapWins / (player.mapWins + player.mapLosses)) * 100,
|
||||
);
|
||||
// a player only met on maps of a set someone else's team was fielded
|
||||
// for has no set record to show a win rate of
|
||||
const setWinRate = winPercentage(player.setWins, player.setLosses);
|
||||
const mapWinRate = winPercentage(player.mapWins, player.mapLosses);
|
||||
return (
|
||||
<div key={player.user.id} className="stack">
|
||||
<Link
|
||||
@@ -294,11 +292,18 @@ function Players({
|
||||
</Link>
|
||||
<div
|
||||
className={clsx("text-xs font-bold", {
|
||||
"text-success": setWinRate >= 50,
|
||||
"text-warning": setWinRate < 50,
|
||||
"text-success":
|
||||
typeof setWinRate === "number" && setWinRate >= 50,
|
||||
"text-warning":
|
||||
typeof setWinRate === "number" && setWinRate < 50,
|
||||
})}
|
||||
>
|
||||
{setWinRate}% ({mapWinRate}%)
|
||||
{typeof setWinRate === "number"
|
||||
? `${Math.round(setWinRate)}% `
|
||||
: null}
|
||||
{typeof mapWinRate === "number"
|
||||
? `(${Math.round(mapWinRate)}%)`
|
||||
: null}
|
||||
</div>
|
||||
<div className="text-xs">
|
||||
{player.setWins} ({player.mapWins}) {t("user:seasons.win.short")}
|
||||
|
||||
@@ -78,3 +78,20 @@ export function safeNumberParse(value: string | null) {
|
||||
const result = Number(trimmed);
|
||||
return Number.isNaN(result) ? null : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Share of games won as a percentage, or `null` when no games were played.
|
||||
* Callers decide how to round it and what to show in place of a missing rate.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* winPercentage(3, 1); // returns 75
|
||||
* winPercentage(0, 0); // returns null
|
||||
* ```
|
||||
*/
|
||||
export function winPercentage(wins: number, losses: number) {
|
||||
const played = wins + losses;
|
||||
if (played === 0) return null;
|
||||
|
||||
return (wins / played) * 100;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
cutToNDecimalPlaces,
|
||||
roundToNDecimalPlaces,
|
||||
safeNumberParse,
|
||||
winPercentage,
|
||||
} from "./number";
|
||||
|
||||
describe("roundToNDecimalPlaces()", () => {
|
||||
@@ -75,3 +76,18 @@ describe("safeNumberParse()", () => {
|
||||
expect(safeNumberParse(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("winPercentage()", () => {
|
||||
test.each([
|
||||
[3, 1, 75],
|
||||
[1, 1, 50],
|
||||
[0, 4, 0],
|
||||
[4, 0, 100],
|
||||
])("%d wins and %d losses is %d%%", (wins, losses, expected) => {
|
||||
expect(winPercentage(wins, losses)).toBe(expected);
|
||||
});
|
||||
|
||||
test("returns null when no games were played", () => {
|
||||
expect(winPercentage(0, 0)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user