Crowd Pick 비율 합이 항상 100% 되도록 반올림 보정

develop
jwkim 2026-06-19 11:05:13 +09:00
parent 0a64a220ca
commit 8580bbb713
2 changed files with 27 additions and 6 deletions

View File

@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { outcomeLabel, pct, kickoffDisplay, winProb } from "@/lib/format"; import { outcomeLabel, pctParts, kickoffDisplay, winProb } from "@/lib/format";
import { type Lang, dict, teamShort } from "@/lib/i18n"; import { type Lang, dict, teamShort } from "@/lib/i18n";
import type { import type {
Outcome, Outcome,
@ -542,15 +542,15 @@ export default function Arena({
Crowd Pick{" "} Crowd Pick{" "}
<span className="text-[14px] text-[var(--ink-muted)]">{t.crowdSub}</span> <span className="text-[14px] text-[var(--ink-muted)]">{t.crowdSub}</span>
</div> </div>
{/* 비율은 분포 합 기준(=total 과 동일해야 정상) — 구 데이터 불일치 시에도 100% 이내로 표시 */} {/* 비율은 분포 합 기준 + 합이 항상 100% 되도록 보정(최대 잔여 방식) */}
<div className="flex h-14 overflow-hidden rounded-2xl border border-[var(--line-d)]"> <div className="flex h-14 overflow-hidden rounded-2xl border border-[var(--line-d)]">
{(() => { {(() => {
const sum = crowd.teamAWin + crowd.draw + crowd.teamBWin; const [a, d, b] = pctParts([crowd.teamAWin, crowd.draw, crowd.teamBWin]);
return ( return (
<> <>
<CrowdSeg team={match.teamA} value={pct(crowd.teamAWin, sum)} tone="a" /> <CrowdSeg team={match.teamA} value={a} tone="a" />
<CrowdSeg value={pct(crowd.draw, sum)} tone="draw" /> <CrowdSeg value={d} tone="draw" />
<CrowdSeg team={match.teamB} value={pct(crowd.teamBWin, sum)} tone="b" /> <CrowdSeg team={match.teamB} value={b} tone="b" />
</> </>
); );
})()} })()}

View File

@ -55,6 +55,27 @@ export function pct(part: number, total: number): number {
return Math.round((part / total) * 100); return Math.round((part / total) * 100);
} }
// 여러 비율을 정수 %로 — 합이 항상 정확히 100 (최대 잔여 방식).
// 각자 따로 반올림하면 99/101 이 나오는 문제를 방지.
export function pctParts(parts: number[]): number[] {
const total = parts.reduce((s, p) => s + p, 0);
if (!total) return parts.map(() => 0);
const raw = parts.map((p) => (p / total) * 100);
const floor = raw.map(Math.floor);
let remainder = 100 - floor.reduce((s, v) => s + v, 0);
// 소수부가 큰 순서로 남은 %를 1씩 분배
const order = raw
.map((v, i) => ({ i, frac: v - Math.floor(v) }))
.sort((a, b) => b.frac - a.frac);
const out = [...floor];
for (const { i } of order) {
if (remainder <= 0) break;
out[i] += 1;
remainder -= 1;
}
return out;
}
// "방금 전 / 3분 전 / 2시간 전 / 5일 전" — 댓글 상대시간 // "방금 전 / 3분 전 / 2시간 전 / 5일 전" — 댓글 상대시간
export function relativeTime(iso: string, lang: Lang = "ko"): string { export function relativeTime(iso: string, lang: Lang = "ko"): string {
const then = new Date(iso).getTime(); const then = new Date(iso).getTime();