Spaces:
Running
Running
Update app.py (#81)
Browse files- Update app.py (2053197d6b7302d519c720ed331b189dcabf545d)
app.py
CHANGED
|
@@ -687,8 +687,200 @@ def get_top3_and_player_count(game_id):
|
|
| 687 |
# Home page
|
| 688 |
|
| 689 |
HEARTBEAT_THRESHOLD_SECONDS = 60 # adjust if needed
|
| 690 |
-
|
| 691 |
def home_page():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
st.header("Home")
|
| 693 |
st.write("Create games, join games, and view leaderboard.")
|
| 694 |
|
|
@@ -766,7 +958,7 @@ def home_page():
|
|
| 766 |
f"(Game {r['game_id']}) โ {r['score']} pts")
|
| 767 |
|
| 768 |
# ---------- PLAY PAGE ----------
|
| 769 |
-
def
|
| 770 |
gid = st.session_state.get("current_game_id")
|
| 771 |
uname = st.session_state.get("username")
|
| 772 |
|
|
|
|
| 687 |
# Home page
|
| 688 |
|
| 689 |
HEARTBEAT_THRESHOLD_SECONDS = 60 # adjust if needed
|
|
|
|
| 690 |
def home_page():
|
| 691 |
+
st.header("Home")
|
| 692 |
+
st.write("Create games, invite friends, play and climb the leaderboard.")
|
| 693 |
+
|
| 694 |
+
# Show last score
|
| 695 |
+
if st.session_state.get('last_score') is not None:
|
| 696 |
+
st.success(f"Your last score: {st.session_state['last_score']} (Game {st.session_state.get('last_game')})")
|
| 697 |
+
|
| 698 |
+
# Load games and players
|
| 699 |
+
games = unified_get("games") or {}
|
| 700 |
+
players_map = unified_get("players") or {}
|
| 701 |
+
|
| 702 |
+
st.subheader("Recent games")
|
| 703 |
+
for g in sorted(games.values(), key=lambda x: x.get("created_at", ""), reverse=True)[:10]:
|
| 704 |
+
gid = g.get("game_id")
|
| 705 |
+
st.markdown(f"### ๐ฎ Game: **{gid}** {'(Closed)' if g.get('closed') else ''}")
|
| 706 |
+
st.write(f"Host: {g.get('host')} โ Topics: {', '.join(g.get('topics', []))}")
|
| 707 |
+
st.write(f"Created: {g.get('created_at')}")
|
| 708 |
+
|
| 709 |
+
# Players for this game
|
| 710 |
+
players_here = players_map.get(gid, {}) or {}
|
| 711 |
+
st.write(f"Players joined: **{len(players_here)}**")
|
| 712 |
+
|
| 713 |
+
# Submitted count
|
| 714 |
+
submitted_count = sum(1 for p in players_here.values() if p.get("submitted"))
|
| 715 |
+
st.write(f"Submitted: **{submitted_count} / {len(players_here)}**")
|
| 716 |
+
|
| 717 |
+
# Final scores
|
| 718 |
+
if players_here:
|
| 719 |
+
st.markdown("**๐ Final Scores**")
|
| 720 |
+
for uname_p, info in players_here.items():
|
| 721 |
+
if info.get("submitted"):
|
| 722 |
+
st.write(
|
| 723 |
+
f"{info.get('avatar','๐ฎ')} **{uname_p}** โ "
|
| 724 |
+
f"Score: **{info.get('score', 0)}**, "
|
| 725 |
+
f"Percentage: **{info.get('percentage', 0)}%**"
|
| 726 |
+
)
|
| 727 |
+
|
| 728 |
+
# Player status
|
| 729 |
+
if players_here:
|
| 730 |
+
st.markdown("**๐ฅ Player Status**")
|
| 731 |
+
for uname_p, info in players_here.items():
|
| 732 |
+
status = "โ
Submitted" if info.get('submitted') else "โณ Playing"
|
| 733 |
+
st.write(f"{info.get('avatar','๐ฎ')} **{uname_p}** โ {status}")
|
| 734 |
+
|
| 735 |
+
st.markdown("---")
|
| 736 |
+
|
| 737 |
+
# Invite & challenge buttons
|
| 738 |
+
if not g.get('closed'):
|
| 739 |
+
st.info(f"Share this Game ID: {gid}")
|
| 740 |
+
render_copy_button(gid, gid)
|
| 741 |
+
|
| 742 |
+
if st.session_state.get('username'):
|
| 743 |
+
if st.button(f"Invite your friends to {gid}", key=f"invite_{gid}"):
|
| 744 |
+
friends = get_friends_map().get(st.session_state['username'], [])
|
| 745 |
+
if not friends:
|
| 746 |
+
st.warning("No friends to invite.")
|
| 747 |
+
else:
|
| 748 |
+
for f in friends:
|
| 749 |
+
send_game_invite(st.session_state['username'], f, gid)
|
| 750 |
+
st.success("Invites sent to friends.")
|
| 751 |
+
|
| 752 |
+
if st.button(f"Challenge friends with a new game like {gid}", key=f"challenge_{gid}"):
|
| 753 |
+
new_id = create_game(
|
| 754 |
+
st.session_state.get('username','Host'),
|
| 755 |
+
g.get('topics', []),
|
| 756 |
+
num_questions=len(g.get('questions', []))
|
| 757 |
+
)
|
| 758 |
+
# Update session for new game
|
| 759 |
+
st.session_state["current_game_id"] = new_id
|
| 760 |
+
st.session_state["game_questions"] = games.get(new_id, {}).get("questions", [])
|
| 761 |
+
st.success(f"Challenge created: {new_id}")
|
| 762 |
+
st.experimental_rerun()
|
| 763 |
+
|
| 764 |
+
# Weekly leaderboard
|
| 765 |
+
st.subheader("๐ Weekly Leaderboard (Top 10)")
|
| 766 |
+
weekly = get_weekly_leaderboard()
|
| 767 |
+
if not weekly:
|
| 768 |
+
st.info("No scores yet this week.")
|
| 769 |
+
else:
|
| 770 |
+
for i, r in enumerate(weekly, 1):
|
| 771 |
+
st.write(
|
| 772 |
+
f"{i}. {r.get('avatar','๐ฎ')} **{r['name']}** "
|
| 773 |
+
f"(Game {r['game_id']}) โ {r['score']} pts"
|
| 774 |
+
)
|
| 775 |
+
|
| 776 |
+
# ----------------------------
|
| 777 |
+
# Play Page
|
| 778 |
+
# ----------------------------
|
| 779 |
+
def play_page():
|
| 780 |
+
uname = st.session_state.get("username")
|
| 781 |
+
gid = st.session_state.get("current_game_id")
|
| 782 |
+
if not uname or not gid:
|
| 783 |
+
st.warning("No active game or username found. Please join or create a game first.")
|
| 784 |
+
return
|
| 785 |
+
|
| 786 |
+
games = unified_get("games") or {}
|
| 787 |
+
game = games.get(gid)
|
| 788 |
+
if not game:
|
| 789 |
+
st.error("Game not found.")
|
| 790 |
+
return
|
| 791 |
+
|
| 792 |
+
questions = st.session_state.get("game_questions") or game.get("questions", [])
|
| 793 |
+
if not questions:
|
| 794 |
+
st.error("No questions loaded.")
|
| 795 |
+
return
|
| 796 |
+
|
| 797 |
+
# Timer
|
| 798 |
+
if 'question_started_at' not in st.session_state or st.session_state['question_started_at'] is None:
|
| 799 |
+
st.session_state['question_started_at'] = time.time()
|
| 800 |
+
|
| 801 |
+
idx = st.session_state.get('current_index', 0)
|
| 802 |
+
if idx >= len(questions):
|
| 803 |
+
st.success("All done โ submit!")
|
| 804 |
+
return
|
| 805 |
+
|
| 806 |
+
q = questions[idx]
|
| 807 |
+
st.subheader(f"Question {idx+1}/{len(questions)}")
|
| 808 |
+
st.write(q["question"])
|
| 809 |
+
elapsed = int(time.time() - st.session_state['question_started_at'])
|
| 810 |
+
time_limit = 15
|
| 811 |
+
st.markdown(f"**Time left:** {max(0, time_limit - elapsed)} seconds")
|
| 812 |
+
|
| 813 |
+
choice = st.radio("Choose an answer:", q["options"], key=f"choice_{idx}")
|
| 814 |
+
|
| 815 |
+
col1, col2 = st.columns(2)
|
| 816 |
+
with col1:
|
| 817 |
+
if st.button("Next", key=f"next_{idx}"):
|
| 818 |
+
taken = time.time() - st.session_state['question_started_at']
|
| 819 |
+
answers = st.session_state.get('answers', [""]*len(questions))
|
| 820 |
+
times = st.session_state.get('answer_times', [None]*len(questions))
|
| 821 |
+
answers[idx] = choice
|
| 822 |
+
times[idx] = taken
|
| 823 |
+
st.session_state['answers'] = answers
|
| 824 |
+
st.session_state['answer_times'] = times
|
| 825 |
+
st.session_state['current_index'] = idx + 1
|
| 826 |
+
st.session_state['question_started_at'] = time.time()
|
| 827 |
+
|
| 828 |
+
# Update heartbeat
|
| 829 |
+
players = unified_get("players") or {}
|
| 830 |
+
if players.get(gid, {}).get(uname):
|
| 831 |
+
players[gid][uname]['last_heartbeat'] = now_iso()
|
| 832 |
+
unified_set("players", players)
|
| 833 |
+
heartbeat_unified(gid, uname)
|
| 834 |
+
st.experimental_rerun()
|
| 835 |
+
|
| 836 |
+
with col2:
|
| 837 |
+
if idx == len(questions) - 1:
|
| 838 |
+
if st.button("Submit All Answers", key=f"submit_{idx}"):
|
| 839 |
+
answers = st.session_state.get('answers', [""]*len(questions))
|
| 840 |
+
times = st.session_state.get('answer_times', [None]*len(questions))
|
| 841 |
+
answers[idx] = choice
|
| 842 |
+
times[idx] = time.time() - st.session_state.get('question_started_at', time.time())
|
| 843 |
+
st.session_state['answers'] = answers
|
| 844 |
+
st.session_state['answer_times'] = times
|
| 845 |
+
|
| 846 |
+
score, flags = compute_score(questions, answers, times)
|
| 847 |
+
now = now_iso()
|
| 848 |
+
avatar = "๐ฎ"
|
| 849 |
+
row = {
|
| 850 |
+
"name": uname,
|
| 851 |
+
"score": score,
|
| 852 |
+
"game_id": gid,
|
| 853 |
+
"topics": ",".join(game.get('topics', [])),
|
| 854 |
+
"timestamp": now,
|
| 855 |
+
"avatar": avatar,
|
| 856 |
+
"questions": " || ".join([q["question"] for q in questions]),
|
| 857 |
+
"answers": " || ".join([str(a) for a in answers]),
|
| 858 |
+
"correct_flags": " || ".join(flags),
|
| 859 |
+
"percentage": round(score / len(questions) * 100, 2)
|
| 860 |
+
}
|
| 861 |
+
unified_push_leaderboard(row)
|
| 862 |
+
|
| 863 |
+
# Mark player submitted
|
| 864 |
+
players = unified_get("players") or {}
|
| 865 |
+
if players.get(gid) and players[gid].get(uname):
|
| 866 |
+
players[gid][uname]['submitted'] = True
|
| 867 |
+
players[gid][uname]['submitted_at'] = now
|
| 868 |
+
players[gid][uname]['score'] = score
|
| 869 |
+
players[gid][uname]['percentage'] = row['percentage']
|
| 870 |
+
unified_set("players", players)
|
| 871 |
+
|
| 872 |
+
# Auto close game
|
| 873 |
+
if game.get('auto_close', True):
|
| 874 |
+
games[gid]['closed'] = True
|
| 875 |
+
games[gid]['closed_at'] = now
|
| 876 |
+
unified_set("games", games)
|
| 877 |
+
|
| 878 |
+
st.success(f"Submitted! Score: {score}")
|
| 879 |
+
st.balloons()
|
| 880 |
+
st.session_state['last_score'] = score
|
| 881 |
+
st.session_state['last_game'] = gid
|
| 882 |
+
st.session_state['current_index'] = 0
|
| 883 |
+
def home_pe():
|
| 884 |
st.header("Home")
|
| 885 |
st.write("Create games, join games, and view leaderboard.")
|
| 886 |
|
|
|
|
| 958 |
f"(Game {r['game_id']}) โ {r['score']} pts")
|
| 959 |
|
| 960 |
# ---------- PLAY PAGE ----------
|
| 961 |
+
def y_page():
|
| 962 |
gid = st.session_state.get("current_game_id")
|
| 963 |
uname = st.session_state.get("username")
|
| 964 |
|