add show answer

This commit is contained in:
lelo 2025-06-28 22:31:22 +02:00
parent 985a70abf3
commit 6fc98c48d9
3 changed files with 86 additions and 21 deletions

28
app.py
View File

@ -53,7 +53,8 @@ def host():
games[secret] = {
'players': {}, 'order': [],
'current_q': 0, 'answered': [],
'started': False, 'finished': False
'started': False, 'finished': False,
'revealed': False
}
# build join URL + QR
@ -256,6 +257,8 @@ def on_submit_answer(data):
if not secret or secret not in games or pid not in games[secret]['players']:
return
game = games[secret]
if game.get('revealed'):
return
if any(pid == p for p,_ in game['answered']):
return
@ -301,7 +304,22 @@ def on_rejoin_game():
join_room('player')
join_room(pid)
_send_sync_state(pid, game)
@socketio.on('show_answer')
def on_show_answer():
secret = session.get('secret')
if session.get('role') != 'host' or not secret or secret not in games:
return
game = games[secret]
game['revealed'] = True
# find the correct answer text for current question
correct = questions[game['current_q']]['correct']
# emit to host so they can display it
emit('answer_revealed', {'correct': correct}, room='host')
# emit to all players so they can highlight it
socketio.emit('answer_revealed', {'correct': correct}, room='player')
# helpers
@ -337,6 +355,7 @@ def _send_sync_state(pid, game):
def _send_question(secret):
game = games[secret]
game['revealed'] = False
q = questions[game['current_q']]
# clear previous answers
game['answered'].clear()
@ -403,4 +422,9 @@ def _score_and_emit(secret):
if __name__ == '__main__':
# show own IP address
import socket
hostname = socket.gethostname()
ip_address = socket.gethostbyname(hostname)
print(f"Running on http://{ip_address}:5000/")
socketio.run(app, host='0.0.0.0', port=5000)

View File

@ -25,14 +25,15 @@
<!-- Question & Current Leaderboard -->
<div id="host-question" style="display:none;">
<h2 id="qText" style="font-size:5rem;"></h2>
<button id="nextBtn" class="btn btn-warning mt-3">Nächste Frage</button>
<div id="host-results" style="display:none; margin:20px;">
<ul id="perQ" class="list-group mb-3 text-center"></ul>
</div>
<div class="mt-3">
<button id="showAnswerBtn" class="btn btn-info">Antwort zeigen</button>
<button id="nextBtn" class="btn btn-warning" disabled>Nächste Frage</button>
</div>
</div>
<!-- Per-Question and Overall Results -->
<div id="host-results" style="display:none;">
<h4>Question Results</h4>
<ul id="perQ" class="list-group mb-3"></ul>
</div>
<!-- Final Top-10 Leaderboard -->
<div id="final-results" style="display:none;" class="mt-4">
@ -90,6 +91,9 @@
$('#qrContainer, #startBtn, #rosterHeader, #roster').hide();
$('#host-question').show();
$('#qText').text(data.question);
$('#host-results').hide();
$('#showAnswerBtn').prop({ disabled: false }).show();
$('#nextBtn').prop('disabled', true);
});
// Overall leaderboard updates (max 5 rows)
@ -103,21 +107,31 @@
});
});
// Per-question results
socket.on('question_leader', data => {
console.log('Question leader:', data.results);
$('#perQ').empty();
data.results.forEach(r => {
$('#perQ').append(`<li class="list-group-item">${r.name}: +${r.points}</li>`);
});
});
// Next question
$('#nextBtn').off('click').on('click', () => {
console.log('Next question clicked');
socket.emit('next_question');
});
// when host clicks “Show Answer”
$('#showAnswerBtn').off('click').on('click', () => {
socket.emit('show_answer');
});
// once server confirms the answer is revealed
socket.on('answer_revealed', data => {
// display the correct answer on host screen
$('#perQ').empty();
$('#perQ').append(
`<li class="list-group-item list-group-item-success" style="font-size:5rem;">${data.correct}</li>`
);
$('#host-results').show();
// disable Show Answer button, enable Next
$('#showAnswerBtn').prop('disabled', true);
$('#nextBtn').prop('disabled', false);
});
// Show final top-10 when game_over arrives
socket.on('game_over', data => {
if (data.board) {

View File

@ -3,12 +3,15 @@
{% block content %}
<style>
.option.selected {
background-color: #0d6efd; /* bootstrap “primary” bg */
color: white;
background-color:rgb(110, 168, 255); /* bootstrap “primary” bg */
color: black;
}
.option.disabled {
background-color: #6c757d; /* bootstrap “secondary” bg */
color: white;
background-color:rgb(148, 148, 148); /* bootstrap “secondary” bg */
color: black;
}
.option.correct {
border: 5px solid #198754 !important; /* bootstrap “success” green */
}
</style>
<div class="container" id="player-app">
@ -93,6 +96,30 @@
renderOptions(data.options, false);
});
// when the right answer is revealed
socket.on('answer_revealed', data => {
$('.option').each(function() {
const $opt = $(this);
if ($opt.text() === data.correct) {
// highlight as correct with green outline
$opt
.addClass('correct')
.removeClass('btn-outline-primary disabled')
.prop('disabled', true);
} else {
// any un-clicked options become permanently disabled
if (!$opt.hasClass('selected')) {
$opt
.addClass('disabled')
.removeClass('btn-outline-primary btn-primary')
.prop('disabled', true);
}
}
});
});
// Handle answer clicks
$(document).on('click', '.option', function() {
const $all = $('.option');