File size: 3,640 Bytes
69c9e26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import altair as alt
import pandas as pd
import streamlit as st


def compute_cumulative(players, scores):
    cumsum = {p: 0 for p in players}
    history = []
    for round_data in scores:
        for p in players:
            cumsum[p] += round_data.get(p, 0)
        history.append(dict(cumsum))
    return cumsum, history


def render_scoreboard_transposed(players, scores, winning_score):
    header_cols = ["Player"] + [f"R{i + 1}" for i in range(len(scores))]
    md = "| " + " | ".join(header_cols) + " |\n"
    md += "| " + " | ".join(["---"] * len(header_cols)) + " |\n"

    for p in players:
        row = [f"**{p}**"]
        running = 0
        for round_data in scores:
            pts = round_data.get(p, 0)
            prev = running
            running += pts
            change = running - prev
            if change > 0:
                indicator = f' <span style="color:green">&#9650; +{change}</span>'
            elif change < 0:
                indicator = f' <span style="color:red">&#9660; {change}</span>'
            else:
                indicator = ""
            winner_flag = " β˜…" if running >= winning_score else ""
            row.append(f"**{running}**{indicator}{winner_flag}")
        md += "| " + " | ".join(row) + " |\n"

    st.markdown(md, unsafe_allow_html=True)


def render_leaderboard(players, cumsum):
    st.subheader("πŸ† Leaderboard")
    if not players:
        return

    medals = ["πŸ₯‡", "πŸ₯ˆ", "πŸ₯‰"]
    cols = st.columns(len(players))
    for i, p in enumerate(players):
        label = f"{medals[i]} {p}" if i < len(medals) else f"#{i + 1} {p}"
        with cols[i]:
            st.metric(label, cumsum[p])


def render_chart(players, scores):
    if not scores:
        return

    cumsum = {p: 0 for p in players}
    rows = [{"Round": 0, **{p: 0 for p in players}}]
    for i, round_data in enumerate(scores):
        for p in players:
            cumsum[p] += round_data.get(p, 0)
        rows.append({"Round": i + 1, **dict(cumsum)})

    df = pd.DataFrame(rows)
    df_long = df.melt(id_vars="Round", var_name="Player", value_name="Points")

    chart = (
        alt.Chart(df_long)
        .mark_line(point=True)
        .encode(
            x=alt.X("Round:Q", axis=alt.Axis(tickMinStep=1)),
            y=alt.Y("Points:Q"),
            color=alt.Color("Player:N"),
            tooltip=["Round", "Player", "Points"],
        )
        .properties(height=300)
        .interactive()
    )

    st.altair_chart(chart, use_container_width=True)


def rows_to_players_and_scores(rows: list[dict]) -> tuple[list[str], list[dict]]:
    """Converts raw games_played rows (one row per player per round) into the
    same (players, scores) shape the live game view uses, so historical
    sessions can reuse the exact same rendering functions.

    Uses player_name (not player_id) since that's guaranteed present on both
    pre- and post-migration rows -- id lookups are for future robustness, not
    a requirement for basic display.
    """
    players_seen: list[str] = []
    by_round: dict[int, dict[str, int]] = {}
    for row in rows:
        name = row.get("player_name") or row.get("player_id") or "Unknown"
        if name not in players_seen:
            players_seen.append(name)
        try:
            rnd = int(row.get("round_number") or 0)
        except (TypeError, ValueError):
            rnd = 0
        try:
            score = int(row.get("score") or 0)
        except (TypeError, ValueError):
            score = 0
        by_round.setdefault(rnd, {})[name] = score

    scores = [by_round[r] for r in sorted(by_round)]
    return players_seen, scores