From 58eb6ec4dc8f49a0c5eeee8ee97d39b951d031a3 Mon Sep 17 00:00:00 2001
From: steel <mael.acier@ensiie.fr>
Date: Sun, 1 Sep 2024 23:54:28 +0200
Subject: [PATCH] feat: variable score

---
 src/lib/game.ts                 |  7 +++++--
 src/routes/quiz/+page.server.ts | 22 ++++++++++++++++++----
 src/routes/quiz/+page.svelte    | 27 ++++++++++++++++++++-------
 3 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/src/lib/game.ts b/src/lib/game.ts
index e20f14d..85fa5c1 100644
--- a/src/lib/game.ts
+++ b/src/lib/game.ts
@@ -19,7 +19,8 @@ export const gameStateSchema = z.object({
 	year: z.number(),
 	maxYear: z.number().optional(),
 	label: z.string(),
-	solution: z.string()
+	solution: z.string(),
+	timestamp: z.number()
 });
 
 export type GameState = z.infer<typeof gameStateSchema>;
@@ -44,12 +45,14 @@ export class Game {
 			year: 0,
 			options: [],
 			solution: '',
-			label: '?'
+			label: '?',
+			timestamp: 0
 		};
 	}
 
 	save() {
 		// console.log('SAVE:', GameStage[this.state.stage], this.state);
+		this.state.timestamp = Date.now();
 		this.cookie.write(this.state);
 	}
 
diff --git a/src/routes/quiz/+page.server.ts b/src/routes/quiz/+page.server.ts
index db2214e..ce29c90 100644
--- a/src/routes/quiz/+page.server.ts
+++ b/src/routes/quiz/+page.server.ts
@@ -62,6 +62,19 @@ export async function load(event) {
 	};
 }
 
+const MAX_POINTS = 10;
+const MIN_POINTS = 0;
+// Durée maximale pour obtenir le score maximal
+const SERVER_DELAY_MAX = 3;
+
+function computePoints(timestamp: number): number {
+	const elapsed = (Date.now() - timestamp) / 1000;
+	return Math.max(
+		MIN_POINTS,
+		Math.floor(MAX_POINTS - Math.min(MAX_POINTS + SERVER_DELAY_MAX, elapsed))
+	);
+}
+
 export const actions = {
 	async results(event) {
 		const form = await superValidate(event, zod(schema));
@@ -72,15 +85,16 @@ export const actions = {
 
 		const state = new Game(event.cookies);
 
-		if (form.data.choice === state.state.solution) {
-			state.state.score += 10;
-		}
+		const points =
+			form.data.choice === state.state.solution ? computePoints(state.state.timestamp) : 0;
+		state.state.score += points;
 		state.state.history.push(state.state.solution);
 		state.save();
 
 		return {
 			form,
-			solution: state.state.solution
+			solution: state.state.solution,
+			points
 		};
 	},
 
diff --git a/src/routes/quiz/+page.svelte b/src/routes/quiz/+page.svelte
index 4533b4c..a0f2d9b 100644
--- a/src/routes/quiz/+page.svelte
+++ b/src/routes/quiz/+page.svelte
@@ -1,6 +1,7 @@
 <script lang="ts">
 	import { Fieldset, Control, Label } from 'formsnap';
 	import { superForm } from 'sveltekit-superforms';
+	import { scale } from 'svelte/transition';
 
 	export let data;
 	export let form;
@@ -17,7 +18,7 @@
 
 <div class="relative mx-auto my-12 w-full grow">
 	<section class="relative m-auto flex flex-col items-center">
-		<h1 class="absolute -top-8 text-xl">Qui est-ce ? {data.label}</h1>
+		<h1 class="absolute -top-8 text-xl">Qui est-ce ?</h1>
 		<div class="relative">
 			<span class="absolute right-[calc(100%_+_0.5rem)] text-right">
 				<span class="flex flex-col items-end">
@@ -27,14 +28,20 @@
 				<span class="inline-block h-1.5 w-10 bg-zinc-800"></span>
 				<br />
 				<span class="top-24 pt-4 text-xl text-zinc-800">
-					{data.score}
+					{data.score - (form?.points ?? 0)}
 					<small>pts</small>
 				</span>
 			</span>
-			<div
-				class="relative z-10 flex h-64 w-64 items-center justify-center overflow-hidden rounded-2xl border-6 border-solid border-zinc-800 bg-white before:absolute before:-z-10 before:h-32 before:w-32 before:rounded-full before:bg-slate-300 before:opacity-100 before:transition-[0.65s] before:duration-[ease-in-out] after:absolute after:-z-10 after:h-32 after:w-32 after:scale-0 after:rounded-full after:border-solid after:border-slate-300 after:transition-[0.4s] after:duration-[ease-in-out]"
-			>
-				<img src={data.photo?.url} alt="Chargement..." class="w-auto" />
+			<div>
+				<div
+					class="relative z-10 flex h-64 w-64 items-center justify-center overflow-hidden rounded-2xl border-6 border-solid border-zinc-800 bg-white before:absolute before:-z-10 before:h-32 before:w-32 before:rounded-full before:bg-slate-300 before:opacity-100 before:transition-[0.65s] before:duration-[ease-in-out] after:absolute after:-z-10 after:h-32 after:w-32 after:scale-0 after:rounded-full after:border-solid after:border-slate-300 after:transition-[0.4s] after:duration-[ease-in-out]"
+				>
+					<img src={data.photo?.url} alt="Chargement..." class="w-auto" />
+				</div>
+				<span
+					class="absolute left-0 top-0 z-20 rounded-br-xl rounded-tl-2xl bg-zinc-800 px-2 text-xl text-white"
+					>{data.label}</span
+				>
 			</div>
 			<form method="post" use:enhance>
 				<Fieldset form={sForm} name="choice">
@@ -42,6 +49,7 @@
 						class="relative -top-6 z-20 mx-auto my-0 flex w-44 flex-col items-center space-y-0.5 rounded-2xl bg-zinc-800 px-5 py-0"
 					>
 						{#each data.options as option}
+							{@const valid = (form?.solution && option.value === form?.solution) || null}
 							<div class="max-h-14 w-full min-w-52">
 								<Control let:attrs>
 									<input
@@ -51,13 +59,18 @@
 										value={option.value}
 										class="peer sr-only"
 										disabled={form?.solution !== undefined}
-										data-valid={(form?.solution && option.value === form?.solution) || null}
+										data-valid={valid}
 									/>
 									<Label
 										tabindex={0}
 										class="relative block cursor-pointer select-none overflow-hidden rounded-2xl border-6 border-solid border-zinc-800 bg-slate-300 p-2 text-center text-lg transition-[0.45s] before:absolute before:left-2/4 before:top-2/4 before:-z-10 before:h-[200px] before:w-[200px] before:-translate-x-2/4 before:-translate-y-2/4 before:scale-0 before:rounded-full before:bg-indigo-300 before:transition-[0.2s] before:duration-[ease-in-out] focus:[outline:none] active:before:-translate-x-2/4 active:before:-translate-y-2/4 active:before:scale-100 peer-enabled:hover:translate-y-[-3px] peer-enabled:hover:bg-slate-400 peer-enabled:focus:border-indigo-500 peer-checked:peer-enabled:bg-indigo-300 peer-disabled:cursor-default peer-disabled:text-[#94acbd] peer-checked:peer-disabled:bg-red-400 peer-checked:peer-disabled:text-inherit peer-data-[valid]:!bg-lime-400 peer-data-[valid]:text-inherit"
 									>
 										{option.label}
+										{#if valid && form?.points}
+											<span class="absolute right-0 mr-1.5 rounded-full bg-white px-1.5" in:scale>
+												+{form.points}
+											</span>
+										{/if}
 									</Label>
 								</Control>
 							</div>
-- 
GitLab