✨ Added scoreboard
This commit is contained in:
@@ -109,7 +109,6 @@ async def join_game(sid: str, data: dict):
|
||||
room=redis_res.admin,
|
||||
)
|
||||
lol = fernet.encrypt(datetime.now().isoformat().encode("utf-8")).decode("utf-8")
|
||||
print(lol)
|
||||
await sio.emit("time_sync", lol, room=sid)
|
||||
sio.enter_room(sid, data.game_pin)
|
||||
|
||||
@@ -220,6 +219,7 @@ class _AnswerData(BaseModel):
|
||||
answer: str
|
||||
right: bool
|
||||
time_taken: float # In milliseconds
|
||||
score: int
|
||||
|
||||
|
||||
class _AnswerDataList(BaseModel):
|
||||
@@ -257,7 +257,17 @@ async def submit_answer(sid: str, data: dict):
|
||||
time_q_started = datetime.fromisoformat(await redis.get(f"game:{session['game_pin']}:current_time"))
|
||||
answers = await redis.get(f"game_session:{session['game_pin']}:{data.question_index}")
|
||||
diff = (time_q_started - now).total_seconds() * 1000 # - timedelta(milliseconds=latency)
|
||||
|
||||
# print(abs(diff) - latency, latency, abs(diff))
|
||||
def calculate_score(z: float, t: int) -> int:
|
||||
t = t * 1000
|
||||
res = (t - z) / t
|
||||
return int(res * 1000)
|
||||
|
||||
score = 0
|
||||
if answer_right:
|
||||
score = calculate_score(abs(diff) - latency, int(game_data.questions[int(data.question_index)].time))
|
||||
|
||||
if answers is None:
|
||||
await redis.set(
|
||||
f"game_session:{session['game_pin']}:{data.question_index}",
|
||||
@@ -268,6 +278,7 @@ async def submit_answer(sid: str, data: dict):
|
||||
answer=data.answer,
|
||||
right=answer_right,
|
||||
time_taken=abs(diff) - latency,
|
||||
score=score,
|
||||
)
|
||||
]
|
||||
).json(),
|
||||
@@ -277,7 +288,11 @@ async def submit_answer(sid: str, data: dict):
|
||||
answers = _AnswerDataList.parse_raw(answers)
|
||||
answers.__root__.append(
|
||||
_AnswerData(
|
||||
username=session["username"], answer=data.answer, right=answer_right, time_taken=abs(diff) - latency
|
||||
username=session["username"],
|
||||
answer=data.answer,
|
||||
right=answer_right,
|
||||
time_taken=abs(diff) - latency,
|
||||
score=score,
|
||||
)
|
||||
)
|
||||
await redis.set(
|
||||
@@ -297,7 +312,6 @@ async def get_final_results(sid: str, _data: dict):
|
||||
if not session["admin"]:
|
||||
return
|
||||
results = await generate_final_results(game_data, session["game_pin"])
|
||||
print(results)
|
||||
await sio.emit("final_results", results, room=session["game_pin"])
|
||||
|
||||
|
||||
@@ -328,17 +342,5 @@ async def echo_time_sync(sid: str, data: str):
|
||||
then = datetime.fromisoformat(then_dec)
|
||||
now = datetime.now()
|
||||
delta = now - then
|
||||
print(
|
||||
"delta:",
|
||||
delta,
|
||||
"then:",
|
||||
then,
|
||||
"now:",
|
||||
now,
|
||||
"seconds delay:",
|
||||
delta.seconds,
|
||||
"microseconds delay",
|
||||
delta.microseconds / 1000,
|
||||
)
|
||||
async with sio.session(sid) as session:
|
||||
session["ping"] = delta.microseconds / 1000
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import { QuizQuestionType } from '$lib/quiz_types';
|
||||
import { kahoot_icons } from './play/kahoot_mode_assets/kahoot_icons';
|
||||
import CircularTimer from '$lib/play/circular_progress.svelte';
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
|
||||
export let game_token: string;
|
||||
export let quiz_data: QuizData;
|
||||
@@ -26,7 +27,7 @@
|
||||
let shown_question_now: number;
|
||||
let final_results_clicked = false;
|
||||
|
||||
console.log(quiz_data);
|
||||
let player_scores = {};
|
||||
|
||||
export const set_question_number = (q_number: number) => {
|
||||
question_results = null;
|
||||
@@ -143,7 +144,7 @@
|
||||
{/if}
|
||||
{/if}
|
||||
<div class:mt-28={game_mode === 'kahoot'} class="w-full h-full">
|
||||
{#if timer_res !== undefined && !final_results_clicked}
|
||||
{#if timer_res !== undefined && !final_results_clicked && !question_results}
|
||||
<div class="flex flex-col justify-center w-screen h-1/6">
|
||||
<h1 class="text-6xl text-center">
|
||||
{quiz_data.questions[selected_question].question}
|
||||
@@ -205,49 +206,15 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="w-full flex justify-center">
|
||||
<div class="relative overflow-x-auto shadow-md rounded-lg">
|
||||
<table class="w-fit text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<thead
|
||||
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"
|
||||
>
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
{$t('words.username')}
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
{$t('words.answer')}
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
{$t('words.correct')}?
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each question_results as result}
|
||||
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
||||
<th
|
||||
scope="row"
|
||||
class="px-6 py-4 font-medium text-gray-900 dark:text-white whitespace-nowrap"
|
||||
>
|
||||
{result.username}
|
||||
</th>
|
||||
<td class="px-6 py-4">
|
||||
{result.answer}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
{#if result.right}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{#await import('$lib/play/admin/results.svelte')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<svelte:component
|
||||
this={c.default}
|
||||
bind:data={player_scores}
|
||||
bind:new_data={question_results}
|
||||
/>
|
||||
{/await}
|
||||
{/if}
|
||||
{:else if timer_res !== undefined}
|
||||
{#if game_mode === 'normal'}
|
||||
|
||||
@@ -14,3 +14,7 @@ export const invertColor = (hexTripletColor: string): string => {
|
||||
color = '#' + color; // prepend #
|
||||
return color;
|
||||
};
|
||||
|
||||
export const calculate_score = (q_time: number, time_taken: number): number => {
|
||||
return q_time / time_taken;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<!--
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { flip } from 'svelte/animate';
|
||||
import { fly } from 'svelte/transition';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
export let new_data: Array<{
|
||||
username: string;
|
||||
answer: string;
|
||||
right: boolean;
|
||||
time_taken: number;
|
||||
score: number;
|
||||
}>;
|
||||
|
||||
function sortObjectbyValue(obj) {
|
||||
const ret = {};
|
||||
Object.keys(obj)
|
||||
.sort((a, b) => obj[b] - obj[a])
|
||||
.forEach((s) => (ret[s] = obj[s]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// let data_by_username = {};
|
||||
let score_by_username = {};
|
||||
|
||||
for (const i of new_data) {
|
||||
/* data_by_username[i.username] = {
|
||||
answer: i.answer,
|
||||
right: i.right,
|
||||
time_taken: i.time_taken,
|
||||
score: i.score
|
||||
};*/
|
||||
score_by_username[i.username] = i.score;
|
||||
}
|
||||
|
||||
let player_names;
|
||||
|
||||
if (JSON.stringify(data) === '{}') {
|
||||
for (const i of new_data) {
|
||||
data[i.username] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$: data = sortObjectbyValue(data);
|
||||
$: player_names = Object.keys(data);
|
||||
|
||||
let show_new_score_clicked = false;
|
||||
|
||||
const show_new_score = () => {
|
||||
// for (let i = 0; i++; i < player_names.length) {
|
||||
// console.log(data)
|
||||
for (const i of player_names) {
|
||||
data[i] = score_by_username[i] + data[i];
|
||||
}
|
||||
show_new_score_clicked = true;
|
||||
setTimeout(() => {
|
||||
data = data;
|
||||
}, 800);
|
||||
|
||||
// console.log(data)
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(show_new_score, 1000);
|
||||
});
|
||||
|
||||
// https://svelte.dev/repl/96a58afdea2248a5b7e489160ffba887?version=3.44.2
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<button on:click={show_new_score}>Show new Score</button>
|
||||
<div class="flex justify-center">
|
||||
<div>
|
||||
<table class="table-auto text-xl">
|
||||
<tr>
|
||||
<th class="p-2 border-r border-r-black border-b-2 border-b-black">Name</th>
|
||||
<th class="p-2 border-b-2 border-b-black">Points</th>
|
||||
{#if show_new_score_clicked}
|
||||
<th in:fly={{ x: 300 }} class="p-2 border-b-2 border-b-black"
|
||||
>Points added</th
|
||||
>
|
||||
{/if}
|
||||
</tr>
|
||||
{#each player_names as player, i (player)}
|
||||
<tr animate:flip>
|
||||
<td class:hidden={i > 3} class="p-2 border-r border-r-black">{player}</td>
|
||||
<td class:hidden={i > 3} class="p-2">{data[player]}</td>
|
||||
{#if show_new_score_clicked}
|
||||
<td
|
||||
in:fly={{ x: 300 }}
|
||||
class:hidden={i > 3}
|
||||
class="p-2"
|
||||
class:text-red-600={score_by_username[player] === 0}
|
||||
>
|
||||
+{score_by_username[player]}
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -15,7 +15,7 @@
|
||||
export let game_pin: string;
|
||||
export let game_mode;
|
||||
|
||||
let username = '';
|
||||
export let username;
|
||||
let hcaptchaSitekey = import.meta.env.VITE_HCAPTCHA;
|
||||
|
||||
let hcaptcha = {
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
<CircularTimer bind:text={timer_res} bind:progress={circular_prgoress} color="#ef4444" />
|
||||
</div>
|
||||
</div>
|
||||
{#if question.image !== null}
|
||||
{#if question.image !== null && game_mode !== 'kahoot'}
|
||||
<div>
|
||||
<img
|
||||
src={question.image}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<!--
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let scores;
|
||||
|
||||
export let question_results: Array<{
|
||||
username: string;
|
||||
answer: string;
|
||||
right: boolean;
|
||||
time_taken: number;
|
||||
score: number;
|
||||
}>;
|
||||
|
||||
function sortObjectbyValue(obj) {
|
||||
const ret = {};
|
||||
Object.keys(obj)
|
||||
.sort((a, b) => obj[b] - obj[a])
|
||||
.forEach((s) => (ret[s] = obj[s]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
export let username;
|
||||
let score_by_username = {};
|
||||
|
||||
if (JSON.stringify(scores) === '{}') {
|
||||
for (const i of question_results) {
|
||||
scores[i.username] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (const i of question_results) {
|
||||
score_by_username[i.username] = i.score;
|
||||
}
|
||||
|
||||
$: scores = sortObjectbyValue(scores);
|
||||
|
||||
for (const i of Object.keys(scores)) {
|
||||
scores[i] = score_by_username[i] + scores[i];
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="flex justify-center h-screen">
|
||||
<div class="m-auto flex flex-col">
|
||||
<p class="p-4 bg-black bg-opacity-40 rounded-lg text-2xl">
|
||||
+{score_by_username[username]}
|
||||
</p>
|
||||
<p>Total score: {scores[username]}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -8,6 +8,7 @@
|
||||
import ShowResults from '$lib/play/show_results.svelte';
|
||||
import { navbarVisible } from '$lib/stores';
|
||||
import ShowEndScreen from '$lib/play/end.svelte';
|
||||
import KahootResults from '$lib/play/results_kahoot.svelte';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
const { t } = getLocalization();
|
||||
@@ -38,6 +39,8 @@
|
||||
let answer_results: Array<Answer>;
|
||||
let gameData;
|
||||
let solution: QuestionType;
|
||||
let username = '';
|
||||
let scores = {};
|
||||
let gameMeta: GameMeta = {
|
||||
started: false
|
||||
};
|
||||
@@ -122,7 +125,7 @@
|
||||
</svelte:head>
|
||||
<div>
|
||||
{#if !gameMeta.started && gameData === undefined}
|
||||
<JoinGame {game_pin} bind:game_mode />
|
||||
<JoinGame {game_pin} bind:game_mode bind:username />
|
||||
{:else if JSON.stringify(final_results) !== JSON.stringify([null])}
|
||||
<ShowEndScreen bind:final_results bind:question_count={gameData.question_count} />
|
||||
{:else if gameData !== undefined && question_index === ''}
|
||||
@@ -140,6 +143,13 @@
|
||||
<div class="w-full flex justify-center">
|
||||
<h1 class="text-3xl">{$t('admin_page.no_answers')}</h1>
|
||||
</div>
|
||||
{:else if game_mode === 'kahoot'}
|
||||
<div>
|
||||
<h2 class="text-center text-3xl mb-8">{$t('words.result', { count: 2 })}</h2>
|
||||
</div>
|
||||
{#key unique}
|
||||
<KahootResults bind:username bind:question_results={answer_results} bind:scores />
|
||||
{/key}
|
||||
{:else}
|
||||
{#key unique}
|
||||
<ShowResults
|
||||
|
||||
Reference in New Issue
Block a user