|
|
import { createMemo, createSignal, onCleanup, onMount } from 'solid-js'
|
|
|
|
|
|
import Box from '@suid/material/Box'
|
|
|
import Button from '@suid/material/Button'
|
|
|
import Card from '@suid/material/Card'
|
|
|
import CardContent from '@suid/material/CardContent'
|
|
|
import Divider from '@suid/material/Divider'
|
|
|
import Stack from '@suid/material/Stack'
|
|
|
import TextField from '@suid/material/TextField'
|
|
|
import Typography from '@suid/material/Typography'
|
|
|
|
|
|
import AttemptIndicator from '../ui/AttemptIndicator'
|
|
|
import ScoreDisplay from '../ui/ScoreDisplay'
|
|
|
import Timer from '../ui/Timer'
|
|
|
import { useTimer } from '../hooks/useTimer'
|
|
|
|
|
|
export default function GameRoute() {
|
|
|
// Placeholder game state until backend exists.
|
|
|
const [question] = createSignal({ theme: 'Général', text: 'Quel est le plus petit nombre premier ?' })
|
|
|
const [answer, setAnswer] = createSignal('')
|
|
|
const [attempts, setAttempts] = createSignal(0)
|
|
|
const [hintUsed, setHintUsed] = createSignal(false)
|
|
|
const [score, setScore] = createSignal(0)
|
|
|
const [message, setMessage] = createSignal<string | null>(null)
|
|
|
|
|
|
const durationMs = 30 * 60 * 1000
|
|
|
const { remainingMs, isExpired, start } = useTimer(durationMs)
|
|
|
|
|
|
onMount(() => start())
|
|
|
|
|
|
onCleanup(() => {
|
|
|
// no-op; hook cleans itself
|
|
|
})
|
|
|
|
|
|
const attemptsLeft = createMemo(() => Math.max(0, 3 - attempts()))
|
|
|
|
|
|
const submit = () => {
|
|
|
if (isExpired()) {
|
|
|
setMessage('Session terminée (timeout).')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
const normalized = answer().trim().toLowerCase()
|
|
|
const correct = normalized === '2' || normalized === 'deux'
|
|
|
|
|
|
setAttempts((a) => a + 1)
|
|
|
|
|
|
if (correct) {
|
|
|
const delta = hintUsed() ? 1 : 2
|
|
|
setScore((s) => s + delta)
|
|
|
setMessage(`Bonne réponse (+${delta}).`)
|
|
|
} else {
|
|
|
setMessage('Mauvaise réponse.')
|
|
|
}
|
|
|
|
|
|
setAnswer('')
|
|
|
}
|
|
|
|
|
|
const useHint = () => {
|
|
|
if (hintUsed()) return
|
|
|
setHintUsed(true)
|
|
|
setMessage('Indice: c’est le seul nombre premier pair.')
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
<Box>
|
|
|
<Stack spacing={2}>
|
|
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} alignItems="center">
|
|
|
<Box sx={{ flexGrow: 1 }}>
|
|
|
<Typography variant="h4" sx={{ fontWeight: 800 }}>
|
|
|
Partie
|
|
|
</Typography>
|
|
|
<Typography sx={{ opacity: 0.8 }}>Thème : {question().theme}</Typography>
|
|
|
</Box>
|
|
|
<Timer remainingMs={remainingMs()} />
|
|
|
<ScoreDisplay score={score()} />
|
|
|
</Stack>
|
|
|
|
|
|
<Card variant="outlined" sx={{ bgcolor: '#111827', borderColor: '#1f2937' }}>
|
|
|
<CardContent>
|
|
|
<Stack spacing={2}>
|
|
|
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
|
|
{question().text}
|
|
|
</Typography>
|
|
|
|
|
|
<TextField
|
|
|
label="Ta réponse"
|
|
|
value={answer()}
|
|
|
onInput={(e) => setAnswer(e.currentTarget.value)}
|
|
|
fullWidth
|
|
|
InputLabelProps={{ style: { color: '#cbd5e1' } }}
|
|
|
InputProps={{ style: { color: '#e5e7eb' } }}
|
|
|
onKeyDown={(e) => {
|
|
|
if (e.key === 'Enter') submit()
|
|
|
}}
|
|
|
/>
|
|
|
|
|
|
<AttemptIndicator attemptsUsed={attempts()} attemptsMax={3} />
|
|
|
|
|
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
|
|
|
<Button variant="contained" disabled={attemptsLeft() === 0} onClick={submit}>
|
|
|
Envoyer
|
|
|
</Button>
|
|
|
<Button variant="outlined" disabled={hintUsed()} onClick={useHint}>
|
|
|
Indice (score réduit)
|
|
|
</Button>
|
|
|
</Stack>
|
|
|
|
|
|
<Divider />
|
|
|
|
|
|
{message() && <Typography sx={{ opacity: 0.9 }}>{message()}</Typography>}
|
|
|
{attemptsLeft() === 0 && <Typography color="warning.main">Plus d'essais.</Typography>}
|
|
|
{isExpired() && <Typography color="error.main">Temps écoulé.</Typography>}
|
|
|
</Stack>
|
|
|
</CardContent>
|
|
|
</Card>
|
|
|
</Stack>
|
|
|
</Box>
|
|
|
)
|
|
|
}
|