Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -607,22 +607,26 @@ def join_game(game_id, username, avatar):
|
|
| 607 |
def compute_score(questions, answers, times):
|
| 608 |
total = 0
|
| 609 |
flags = []
|
|
|
|
| 610 |
for i, q in enumerate(questions):
|
| 611 |
ans = answers[i] if i < len(answers) else ""
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
|
|
|
| 616 |
if correct:
|
| 617 |
base = 10
|
| 618 |
t = times[i] if i < len(times) and times[i] else 999
|
| 619 |
-
bonus = max(0, int((max(0, 15 - min(t,15)) / 15) * 5))
|
| 620 |
total += base + bonus
|
| 621 |
flags.append("Yes")
|
| 622 |
else:
|
| 623 |
flags.append("No")
|
|
|
|
| 624 |
return total, flags
|
| 625 |
|
|
|
|
| 626 |
# ----------------- UI -----------------
|
| 627 |
st.sidebar.title("Mode & Profile")
|
| 628 |
mode_choice = st.sidebar.selectbox("Mode", ["Offline (local JSON)", "Online (Firebase)"], index=0)
|
|
@@ -788,26 +792,32 @@ if 'username' not in st.session_state:
|
|
| 788 |
|
| 789 |
def create_game(host=None, topics=[], num_questions=5, auto_close=True, ai_topic=None):
|
| 790 |
games = unified_get("games") or {}
|
| 791 |
-
gid = f"GAME{int(time.time())}"
|
| 792 |
host = host or st.session_state.get("username", "Host")
|
| 793 |
|
| 794 |
-
# 🔹 Get questions
|
| 795 |
questions = []
|
| 796 |
|
| 797 |
-
# 1️⃣ AI questions
|
| 798 |
if ai_topic:
|
| 799 |
ai_questions = generate_ai_questions(ai_topic, num_questions=num_questions)
|
| 800 |
if ai_questions:
|
| 801 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
|
| 803 |
-
# 2️⃣
|
| 804 |
if not questions:
|
| 805 |
for topic in topics:
|
| 806 |
-
qs = questions_db.get(topic
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
|
| 810 |
-
|
|
|
|
|
|
|
| 811 |
|
| 812 |
# 🔹 Store game
|
| 813 |
games[gid] = {
|
|
@@ -819,16 +829,31 @@ def create_game(host=None, topics=[], num_questions=5, auto_close=True, ai_topic
|
|
| 819 |
"closed": False,
|
| 820 |
"auto_close": auto_close
|
| 821 |
}
|
|
|
|
| 822 |
unified_set("games", games)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 823 |
st.success(f"Game created: {gid} with {len(questions)} questions.")
|
| 824 |
return gid
|
| 825 |
|
|
|
|
|
|
|
|
|
|
| 826 |
# -------------------------
|
| 827 |
# PLAY PAGE
|
| 828 |
# -------------------------
|
| 829 |
def play_page():
|
| 830 |
gid = st.session_state.get("active_game_id")
|
| 831 |
uname = st.session_state.get("username")
|
|
|
|
| 832 |
if not gid or not uname:
|
| 833 |
st.error("No active game or username found. Please join or create a game first.")
|
| 834 |
return
|
|
@@ -844,15 +869,26 @@ def play_page():
|
|
| 844 |
st.error("No questions loaded.")
|
| 845 |
return
|
| 846 |
|
| 847 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 848 |
if 'question_started_at' not in st.session_state or st.session_state['question_started_at'] is None:
|
| 849 |
st.session_state['question_started_at'] = time.time()
|
| 850 |
|
| 851 |
-
idx = st.session_state
|
|
|
|
| 852 |
if idx >= len(questions):
|
| 853 |
st.success("All done — submit your answers!")
|
| 854 |
return
|
| 855 |
|
|
|
|
| 856 |
q = questions[idx]
|
| 857 |
st.subheader(f"Question {idx+1}/{len(questions)}")
|
| 858 |
st.write(q["question"])
|
|
@@ -861,36 +897,49 @@ def play_page():
|
|
| 861 |
time_limit = 15
|
| 862 |
st.markdown(f"**Time left:** {max(0, time_limit - elapsed)} seconds")
|
| 863 |
|
| 864 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 865 |
|
| 866 |
col1, col2 = st.columns(2)
|
| 867 |
|
|
|
|
| 868 |
with col1:
|
| 869 |
-
if st.button("Next", key=f"next_{idx}"):
|
| 870 |
taken = time.time() - st.session_state['question_started_at']
|
| 871 |
-
|
| 872 |
-
|
|
|
|
|
|
|
| 873 |
answers[idx] = choice
|
| 874 |
times[idx] = taken
|
| 875 |
-
|
| 876 |
-
st.session_state['answer_times'] = times
|
| 877 |
st.session_state['current_index'] = idx + 1
|
| 878 |
st.session_state['question_started_at'] = time.time()
|
| 879 |
-
st.
|
| 880 |
|
|
|
|
| 881 |
with col2:
|
| 882 |
-
if idx == len(questions)-1:
|
| 883 |
-
if st.button("Submit All Answers", key=f"submit_{idx}"):
|
| 884 |
-
|
| 885 |
-
|
|
|
|
|
|
|
| 886 |
answers[idx] = choice
|
| 887 |
-
times[idx] = time.time() - st.session_state
|
| 888 |
-
|
| 889 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 890 |
|
| 891 |
-
|
| 892 |
-
score = sum([1 for i,q in enumerate(questions) if answers[i] == q["answer"]])
|
| 893 |
-
percentage = int(score / len(questions) * 100)
|
| 894 |
|
| 895 |
# Update players
|
| 896 |
players = unified_get("players") or {}
|
|
@@ -911,11 +960,16 @@ def play_page():
|
|
| 911 |
games[gid]['closed_at'] = now_iso()
|
| 912 |
unified_set("games", games)
|
| 913 |
|
| 914 |
-
st.success(
|
|
|
|
|
|
|
| 915 |
st.balloons()
|
|
|
|
| 916 |
st.session_state['last_score'] = score
|
| 917 |
st.session_state['last_game'] = gid
|
| 918 |
st.session_state['current_index'] = 0
|
|
|
|
|
|
|
| 919 |
# Join game
|
| 920 |
def join_game_page():
|
| 921 |
st.header("Join Game")
|
|
@@ -933,20 +987,32 @@ def join_game_page():
|
|
| 933 |
if ok:
|
| 934 |
st.success(f"Joined game {game_id} successfully!")
|
| 935 |
|
| 936 |
-
#
|
| 937 |
games = unified_get("games") or {}
|
| 938 |
g = games.get(game_id, {})
|
| 939 |
|
| 940 |
-
#
|
| 941 |
st.session_state['game_questions'] = g.get('questions', [])
|
| 942 |
|
|
|
|
| 943 |
st.session_state['game_id'] = game_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 944 |
st.session_state['username'] = username
|
| 945 |
st.session_state['avatar'] = avatar
|
|
|
|
|
|
|
| 946 |
else:
|
| 947 |
st.error(msg)
|
| 948 |
|
| 949 |
|
|
|
|
| 950 |
# Play page
|
| 951 |
# ----------------- Create Game -----------------
|
| 952 |
|
|
|
|
| 607 |
def compute_score(questions, answers, times):
|
| 608 |
total = 0
|
| 609 |
flags = []
|
| 610 |
+
|
| 611 |
for i, q in enumerate(questions):
|
| 612 |
ans = answers[i] if i < len(answers) else ""
|
| 613 |
+
|
| 614 |
+
correct_ans = q.get("answer", "")
|
| 615 |
+
|
| 616 |
+
correct = str(ans).strip().lower() == str(correct_ans).strip().lower()
|
| 617 |
+
|
| 618 |
if correct:
|
| 619 |
base = 10
|
| 620 |
t = times[i] if i < len(times) and times[i] else 999
|
| 621 |
+
bonus = max(0, int((max(0, 15 - min(t, 15)) / 15) * 5))
|
| 622 |
total += base + bonus
|
| 623 |
flags.append("Yes")
|
| 624 |
else:
|
| 625 |
flags.append("No")
|
| 626 |
+
|
| 627 |
return total, flags
|
| 628 |
|
| 629 |
+
|
| 630 |
# ----------------- UI -----------------
|
| 631 |
st.sidebar.title("Mode & Profile")
|
| 632 |
mode_choice = st.sidebar.selectbox("Mode", ["Offline (local JSON)", "Online (Firebase)"], index=0)
|
|
|
|
| 792 |
|
| 793 |
def create_game(host=None, topics=[], num_questions=5, auto_close=True, ai_topic=None):
|
| 794 |
games = unified_get("games") or {}
|
| 795 |
+
gid = f"GAME{int(time.time())}"
|
| 796 |
host = host or st.session_state.get("username", "Host")
|
| 797 |
|
|
|
|
| 798 |
questions = []
|
| 799 |
|
| 800 |
+
# 1️⃣ AI questions (already dict format)
|
| 801 |
if ai_topic:
|
| 802 |
ai_questions = generate_ai_questions(ai_topic, num_questions=num_questions)
|
| 803 |
if ai_questions:
|
| 804 |
+
for q in ai_questions:
|
| 805 |
+
questions.append({
|
| 806 |
+
"question": q.get("question", ""),
|
| 807 |
+
"options": q.get("options", []),
|
| 808 |
+
"answer": q.get("answer", "")
|
| 809 |
+
})
|
| 810 |
|
| 811 |
+
# 2️⃣ Static fallback (tuple → dict conversion)
|
| 812 |
if not questions:
|
| 813 |
for topic in topics:
|
| 814 |
+
qs = questions_db.get(topic, [])
|
| 815 |
+
for q in qs[:num_questions]:
|
| 816 |
+
questions.append({
|
| 817 |
+
"question": q[0],
|
| 818 |
+
"options": q[1],
|
| 819 |
+
"answer": q[2]
|
| 820 |
+
})
|
| 821 |
|
| 822 |
# 🔹 Store game
|
| 823 |
games[gid] = {
|
|
|
|
| 829 |
"closed": False,
|
| 830 |
"auto_close": auto_close
|
| 831 |
}
|
| 832 |
+
|
| 833 |
unified_set("games", games)
|
| 834 |
+
|
| 835 |
+
# ✅ REQUIRED: prepare play state
|
| 836 |
+
st.session_state['game_id'] = gid
|
| 837 |
+
st.session_state['active_game_id'] = gid
|
| 838 |
+
st.session_state['game_questions'] = questions
|
| 839 |
+
st.session_state['current_index'] = 0
|
| 840 |
+
st.session_state['answers'] = [""] * len(questions)
|
| 841 |
+
st.session_state['answer_times'] = [None] * len(questions)
|
| 842 |
+
st.session_state['question_started_at'] = None
|
| 843 |
+
|
| 844 |
st.success(f"Game created: {gid} with {len(questions)} questions.")
|
| 845 |
return gid
|
| 846 |
|
| 847 |
+
|
| 848 |
+
|
| 849 |
+
|
| 850 |
# -------------------------
|
| 851 |
# PLAY PAGE
|
| 852 |
# -------------------------
|
| 853 |
def play_page():
|
| 854 |
gid = st.session_state.get("active_game_id")
|
| 855 |
uname = st.session_state.get("username")
|
| 856 |
+
|
| 857 |
if not gid or not uname:
|
| 858 |
st.error("No active game or username found. Please join or create a game first.")
|
| 859 |
return
|
|
|
|
| 869 |
st.error("No questions loaded.")
|
| 870 |
return
|
| 871 |
|
| 872 |
+
# ---------------- FIX 3 : SAFE SESSION INIT ----------------
|
| 873 |
+
if 'current_index' not in st.session_state or st.session_state['current_index'] is None:
|
| 874 |
+
st.session_state['current_index'] = 0
|
| 875 |
+
|
| 876 |
+
if 'answers' not in st.session_state or len(st.session_state['answers']) != len(questions):
|
| 877 |
+
st.session_state['answers'] = [""] * len(questions)
|
| 878 |
+
|
| 879 |
+
if 'answer_times' not in st.session_state or len(st.session_state['answer_times']) != len(questions):
|
| 880 |
+
st.session_state['answer_times'] = [None] * len(questions)
|
| 881 |
+
|
| 882 |
if 'question_started_at' not in st.session_state or st.session_state['question_started_at'] is None:
|
| 883 |
st.session_state['question_started_at'] = time.time()
|
| 884 |
|
| 885 |
+
idx = st.session_state['current_index']
|
| 886 |
+
|
| 887 |
if idx >= len(questions):
|
| 888 |
st.success("All done — submit your answers!")
|
| 889 |
return
|
| 890 |
|
| 891 |
+
# ---------------- QUESTION UI ----------------
|
| 892 |
q = questions[idx]
|
| 893 |
st.subheader(f"Question {idx+1}/{len(questions)}")
|
| 894 |
st.write(q["question"])
|
|
|
|
| 897 |
time_limit = 15
|
| 898 |
st.markdown(f"**Time left:** {max(0, time_limit - elapsed)} seconds")
|
| 899 |
|
| 900 |
+
# ---------------- FIX 4 : STABLE RADIO ----------------
|
| 901 |
+
choice = st.radio(
|
| 902 |
+
"Choose an answer:",
|
| 903 |
+
q["options"],
|
| 904 |
+
key=f"choice_{gid}_{idx}"
|
| 905 |
+
)
|
| 906 |
|
| 907 |
col1, col2 = st.columns(2)
|
| 908 |
|
| 909 |
+
# ---------------- NEXT ----------------
|
| 910 |
with col1:
|
| 911 |
+
if st.button("Next", key=f"next_{gid}_{idx}"):
|
| 912 |
taken = time.time() - st.session_state['question_started_at']
|
| 913 |
+
|
| 914 |
+
answers = st.session_state['answers']
|
| 915 |
+
times = st.session_state['answer_times']
|
| 916 |
+
|
| 917 |
answers[idx] = choice
|
| 918 |
times[idx] = taken
|
| 919 |
+
|
|
|
|
| 920 |
st.session_state['current_index'] = idx + 1
|
| 921 |
st.session_state['question_started_at'] = time.time()
|
| 922 |
+
st.rerun()
|
| 923 |
|
| 924 |
+
# ---------------- SUBMIT ----------------
|
| 925 |
with col2:
|
| 926 |
+
if idx == len(questions) - 1:
|
| 927 |
+
if st.button("Submit All Answers", key=f"submit_{gid}_{idx}"):
|
| 928 |
+
|
| 929 |
+
answers = st.session_state['answers']
|
| 930 |
+
times = st.session_state['answer_times']
|
| 931 |
+
|
| 932 |
answers[idx] = choice
|
| 933 |
+
times[idx] = time.time() - st.session_state['question_started_at']
|
| 934 |
+
|
| 935 |
+
# ---------------- FIX 5 : CORRECT SCORING ----------------
|
| 936 |
+
score, flags = compute_score(
|
| 937 |
+
questions,
|
| 938 |
+
answers,
|
| 939 |
+
times
|
| 940 |
+
)
|
| 941 |
|
| 942 |
+
percentage = int(score / (len(questions) * 15) * 100)
|
|
|
|
|
|
|
| 943 |
|
| 944 |
# Update players
|
| 945 |
players = unified_get("players") or {}
|
|
|
|
| 960 |
games[gid]['closed_at'] = now_iso()
|
| 961 |
unified_set("games", games)
|
| 962 |
|
| 963 |
+
st.success(
|
| 964 |
+
f"Submitted! Score: {score} / {len(questions) * 15} ({percentage}%)"
|
| 965 |
+
)
|
| 966 |
st.balloons()
|
| 967 |
+
|
| 968 |
st.session_state['last_score'] = score
|
| 969 |
st.session_state['last_game'] = gid
|
| 970 |
st.session_state['current_index'] = 0
|
| 971 |
+
|
| 972 |
+
|
| 973 |
# Join game
|
| 974 |
def join_game_page():
|
| 975 |
st.header("Join Game")
|
|
|
|
| 987 |
if ok:
|
| 988 |
st.success(f"Joined game {game_id} successfully!")
|
| 989 |
|
| 990 |
+
# Load game safely
|
| 991 |
games = unified_get("games") or {}
|
| 992 |
g = games.get(game_id, {})
|
| 993 |
|
| 994 |
+
# ✅ REQUIRED: set questions
|
| 995 |
st.session_state['game_questions'] = g.get('questions', [])
|
| 996 |
|
| 997 |
+
# ✅ REQUIRED: set BOTH ids
|
| 998 |
st.session_state['game_id'] = game_id
|
| 999 |
+
st.session_state['active_game_id'] = game_id
|
| 1000 |
+
|
| 1001 |
+
# ✅ REQUIRED: reset play state (prevents stored answer bugs)
|
| 1002 |
+
st.session_state['current_index'] = 0
|
| 1003 |
+
st.session_state['answers'] = [""] * len(st.session_state['game_questions'])
|
| 1004 |
+
st.session_state['answer_times'] = [None] * len(st.session_state['game_questions'])
|
| 1005 |
+
st.session_state['question_started_at'] = None
|
| 1006 |
+
|
| 1007 |
st.session_state['username'] = username
|
| 1008 |
st.session_state['avatar'] = avatar
|
| 1009 |
+
|
| 1010 |
+
st.rerun()
|
| 1011 |
else:
|
| 1012 |
st.error(msg)
|
| 1013 |
|
| 1014 |
|
| 1015 |
+
|
| 1016 |
# Play page
|
| 1017 |
# ----------------- Create Game -----------------
|
| 1018 |
|