boardgames_tracker / app_pages /points_tracker.py
RandomCatLover's picture
added good working stuff
69c9e26
Raw
History Blame Contribute Delete
9.36 kB
import uuid
import streamlit as st
import pandas as pd
import scoreboard
import sheets_backend as sheets
from workbook_access import get_workbook
def _init_state():
if "pt_players" not in st.session_state:
st.session_state.pt_players = []
if "pt_player_ids" not in st.session_state:
st.session_state.pt_player_ids = {}
if "pt_game_started" not in st.session_state:
st.session_state.pt_game_started = False
if "pt_scores" not in st.session_state:
st.session_state.pt_scores = []
if "pt_winning_score" not in st.session_state:
st.session_state.pt_winning_score = 100
if "pt_game_name" not in st.session_state:
st.session_state.pt_game_name = ""
if "pt_session_id" not in st.session_state:
st.session_state.pt_session_id = None
if "pt_game_id" not in st.session_state:
st.session_state.pt_game_id = None
_NEW_GAME_NAME_SENTINEL = "+ New game name"
def _game_name_picker(wb):
past_names = sheets.list_game_names(wb) if wb is not None else []
if not past_names:
st.session_state.pt_game_name = st.text_input(
"Game name (optional)",
value=st.session_state.pt_game_name,
placeholder="e.g. Catan, Ticket to Ride…",
)
return
options = past_names + [_NEW_GAME_NAME_SENTINEL]
current = st.session_state.pt_game_name
default_index = options.index(current) if current in options else len(options) - 1
choice = st.selectbox("Game name (optional)", options, index=default_index)
if choice == _NEW_GAME_NAME_SENTINEL:
st.session_state.pt_game_name = st.text_input(
"New game name",
value="" if current in past_names else current,
placeholder="e.g. Catan, Ticket to Ride…",
)
else:
st.session_state.pt_game_name = choice
def _render_new_game_tab(wb):
st.subheader("Game Setup")
_game_name_picker(wb)
st.session_state.pt_winning_score = st.number_input(
"Winning score (points to win)",
min_value=1,
value=st.session_state.pt_winning_score,
step=10,
)
st.subheader("Players")
def _add_player():
name = st.session_state.pt_new_player.strip()
if name and name not in st.session_state.pt_players:
st.session_state.pt_players.append(name)
if wb is not None:
st.session_state.pt_player_ids[name] = sheets.add_player(wb, name)
st.session_state.pt_new_player = ""
col1, col2 = st.columns([3, 1])
with col1:
st.text_input("Player name", key="pt_new_player")
with col2:
st.write("")
st.write("")
st.button("Add Player", key="pt_add_btn", on_click=_add_player)
if wb is not None:
saved_names = [p["name"] for p in sheets.list_players(wb) if p.get("name")]
remaining = [n for n in saved_names if n not in st.session_state.pt_players]
if remaining:
st.caption("Quick-add from your saved players:")
qcols = st.columns(min(len(remaining), 6))
for i, name in enumerate(remaining):
if qcols[i % len(qcols)].button(name, key=f"pt_quickadd_{name}"):
st.session_state.pt_players.append(name)
st.session_state.pt_player_ids[name] = sheets.add_player(wb, name)
st.rerun()
if st.session_state.pt_players:
st.markdown("**Players:**")
for i, p in enumerate(st.session_state.pt_players):
col_name, col_del = st.columns([4, 1])
col_name.write(f"{i + 1}. {p}")
if col_del.button("Remove", key=f"pt_rm_{i}"):
st.session_state.pt_players.pop(i)
st.rerun()
if len(st.session_state.pt_players) >= 2:
if st.button("Start Game", type="primary"):
st.session_state.pt_game_started = True
st.session_state.pt_scores = []
st.session_state.pt_session_id = uuid.uuid4().hex
if wb is not None:
game_name = st.session_state.pt_game_name or "Points Tracker"
st.session_state.pt_game_id = sheets.get_or_create_game(wb, game_name)
# Covers players already in the list before a workbook was
# available (e.g. added while logged out then logged in).
for name in st.session_state.pt_players:
if name not in st.session_state.pt_player_ids:
st.session_state.pt_player_ids[name] = sheets.add_player(wb, name)
st.rerun()
else:
st.info("Add at least 2 players to start.")
def _render_saved_players_tab(wb):
st.subheader("Saved Players")
if wb is None:
st.info("Log in with Google to see your saved players.")
return
players = sheets.list_players(wb)
if not players:
st.caption("No saved players yet -- add one in the New Game tab.")
return
df = pd.DataFrame(players)[["name", "created_at"]]
st.dataframe(df, hide_index=True, width="stretch")
def _render_game_history_tab(wb):
st.subheader("Game History")
if wb is None:
st.info("Log in with Google to see your game history.")
return
sessions = sheets.list_game_sessions(wb)
if not sessions:
st.caption("No games recorded yet.")
return
for session in sessions:
col_info, col_link = st.columns([4, 1])
with col_info:
st.write(
f"**{session['game_name']}** -- {session['timestamp']} -- "
f"{session['num_players']} players, {session['num_rounds']} rounds"
)
with col_link:
if st.button("View", key=f"pt_view_{session['session_id']}"):
st.switch_page(
"app_pages/game_detail.py",
query_params={"session_id": session["session_id"]},
)
def _setup_phase():
wb = get_workbook()
tab_new, tab_players, tab_history = st.tabs(["New Game", "Saved Players", "Game History"])
with tab_new:
_render_new_game_tab(wb)
with tab_players:
_render_saved_players_tab(wb)
with tab_history:
_render_game_history_tab(wb)
def _game_phase():
players = st.session_state.pt_players
scores = st.session_state.pt_scores
winning_score = st.session_state.pt_winning_score
game_name = st.session_state.pt_game_name
cumsum, _ = scoreboard.compute_cumulative(players, scores)
sorted_players = sorted(players, key=lambda p: cumsum[p], reverse=True)
scoreboard.render_leaderboard(sorted_players, cumsum)
if game_name:
st.subheader(f"Tracking: {game_name}")
col_reset, col_undo = st.columns([1, 1])
with col_reset:
if st.button("Reset Game"):
st.session_state.pt_game_started = False
st.session_state.pt_scores = []
st.rerun()
with col_undo:
if scores and st.button("Undo Last Round"):
st.session_state.pt_scores.pop()
st.rerun()
winners = [p for p in players if cumsum[p] >= winning_score]
if scores:
with st.expander("Scoreboard", expanded=True):
scoreboard.render_scoreboard_transposed(sorted_players, scores, winning_score)
with st.expander("Points progression chart", expanded=False):
scoreboard.render_chart(players, scores)
if winners:
st.success(f"Game over! Winner{'s' if len(winners) > 1 else ''}: **{', '.join(winners)}** reached {winning_score} points!")
st.subheader("Final Standings")
ranked = sorted(cumsum.items(), key=lambda x: x[1], reverse=True)
for i, (player, total) in enumerate(ranked):
medal = ["1st", "2nd", "3rd"][i] if i < 3 else f"{i + 1}th"
st.write(f"**{medal}** - {player}: **{total}** points")
return
next_round = len(scores) + 1
st.subheader(f"Round {next_round}")
st.caption(f"Target: {winning_score} points to win.")
round_scores = {}
cols = st.columns(len(players))
for i, player in enumerate(players):
with cols[i]:
current = cumsum[player]
round_scores[player] = st.number_input(
f"{player} ({current} pts)",
value=0,
step=1,
key=f"pt_input_r{next_round}_{player}",
)
if st.button("Save Round", type="primary", key=f"pt_save_r{next_round}"):
st.session_state.pt_scores.append(round_scores)
wb = get_workbook()
if wb is not None:
for player, score in round_scores.items():
sheets.record_round(
wb,
game_id=st.session_state.pt_game_id or "",
game_name=game_name or "Points Tracker",
session_id=st.session_state.pt_session_id,
round_number=next_round,
player_id=st.session_state.pt_player_ids.get(player, ""),
player_name=player,
score=score,
)
st.rerun()
# Page entry point
st.title("Points Tracker")
_init_state()
if not st.session_state.pt_game_started:
_setup_phase()
else:
_game_phase()