function ScoreBar({ label, value }) {
const safeValue = Math.max(0, Math.min(30, Number(value) || 0));
const pct = safeValue / 30 * 100;
return (
);
}
function RoundStatus({ results = [] }) {
const cells = [0,1,2].map((i) => results[i] ?? null);
return (
{t("rounds")}
{cells.map((result, i) => {
const mark = result === "player" ? "✓" : result === "ai" ? "×" : result === "parda" ? "=" : "·";
const cls = result === "player" ? "round-win" : result === "ai" ? "round-loss" : result === "parda" ? "round-draw" : "round-pending";
return
{i + 1}
{mark}
;
})}
);
}
function recommendedCardIdFor(match, playable) {
const hand = match.hands?.player || [];
if (!hand.length) return null;
const current = match.currentTrick || [];
const aiPlayed = current.find((p) => p.player === "ai")?.card;
if (aiPlayed) {
const winners = hand.filter((c) => playable.has(c.id) && trucoPower(c) > trucoPower(aiPlayed));
if (winners.length) return winners.slice().sort((a,b) => trucoPower(a) - trucoPower(b))[0].id;
}
const playableHand = hand.filter((c) => playable.has(c.id));
if (!playableHand.length) return null;
const best = playableHand.slice().sort((a,b) => trucoPower(b) - trucoPower(a))[0];
return best?.id || null;
}
function TableView({ match, rival, sound, onAction, onHelp, onExit, onToggleSound }) {
const wait = waitingOn(match);
const legal = wait === "player" ? legalActions(match, "player") : [];
const tip = coachTip(match);
const calls = legal.filter((a) => a.type === "CALL");
const responds = legal.filter((a) => a.type === "RESPOND");
const canMazo = legal.some((a) => a.type === "MAZO");
const canAck = legal.some((a) => a.type === "ACK");
const playable = new Set(legal.filter((a) => a.type === "PLAY").map((a) => a.cardId));
const recommendedCardId = recommendedCardIdFor(match, playable);
const playerTrick = match.currentTrick.find((p) => p.player === "player");
const aiTrick = match.currentTrick.find((p) => p.player === "ai");
return (
{match.hands.ai.map((card, i) => (
))}
{t("rival")}
{aiTrick ?
:
}
VS
{t("you")}
{playerTrick ?
:
}
{t("tricks")}: {match.bazas.player} – {match.bazas.ai}
{t("handValue")} {match.trucoAccepted ? [1,2,3,4][match.trucoLevel] : 1}
{match.mano === "player" ? t("youreHand") : t("rivalHand")}
{wait === "ai" ? {t("thinking")}
: null}
{tip.title}
{tip.body}
{tip.hint}
{match.hands.player.map((card) => (
onAction({ type: "PLAY", cardId: card.id }) : undefined}/>
))}
{canAck ? : null}
{responds.map((a) => (
))}
{calls.map((a) => )}
{canMazo ? : null}
{t("handEnvido")}: {envidoPoints(match.hands.player)}
{match.toast && match.phase === "toast" ? (
{trToast(match.toast.title)}
{trToastBody(match.toast.body)}
) : null}
);
}