135 lines
4.3 KiB
TypeScript
135 lines
4.3 KiB
TypeScript
import { useState, useRef } from 'react'
|
|
import type { Pregunta, Categoria, PreguntasData } from '../types'
|
|
import data from '../data/preguntas.json'
|
|
import { sounds } from '../audio/AudioController'
|
|
|
|
const { categorias, bancoGenerico } = data as PreguntasData
|
|
const MAX_PREGUNTAS = 10
|
|
const MIN_CORRECTAS = 8
|
|
const SECCIONES = categorias.length
|
|
const ANGULO_SECCION = 360 / SECCIONES
|
|
|
|
export interface EstadoJuego {
|
|
isSpinning: boolean
|
|
rotacion: number
|
|
preguntaActual: Pregunta | null
|
|
categoriaActual: Categoria | null
|
|
mostrarFeedback: boolean
|
|
feedbackCorrecto: boolean
|
|
preguntasRespondidas: number
|
|
correctas: number
|
|
completado: boolean
|
|
aprobado: boolean
|
|
preguntasUsadas: Record<number, number[]>
|
|
bancoPreguntasUsadas: number[]
|
|
}
|
|
|
|
export function useRuleta() {
|
|
const [estado, setEstado] = useState<EstadoJuego>({
|
|
isSpinning: false,
|
|
rotacion: 0,
|
|
preguntaActual: null,
|
|
categoriaActual: null,
|
|
mostrarFeedback: false,
|
|
feedbackCorrecto: false,
|
|
preguntasRespondidas: 0,
|
|
correctas: 0,
|
|
completado: false,
|
|
aprobado: false,
|
|
preguntasUsadas: {},
|
|
bancoPreguntasUsadas: [],
|
|
})
|
|
|
|
const rotacionRef = useRef(0)
|
|
|
|
function seleccionarPregunta(categoria: Categoria, preguntasUsadas: Record<number, number[]>, bancoPreguntasUsadas: number[]): { pregunta: Pregunta, esGenerica: boolean } {
|
|
const usadas = preguntasUsadas[categoria.id] ?? []
|
|
const disponibles = categoria.preguntas.filter((_, i) => !usadas.includes(i))
|
|
|
|
if (disponibles.length > 0) {
|
|
const idx = Math.floor(Math.random() * disponibles.length)
|
|
return { pregunta: disponibles[idx], esGenerica: false }
|
|
}
|
|
|
|
// Banco genérico
|
|
const disponiblesGenericas = bancoGenerico.filter((_, i) => !bancoPreguntasUsadas.includes(i))
|
|
const pool = disponiblesGenericas.length > 0 ? disponiblesGenericas : bancoGenerico
|
|
const idx = Math.floor(Math.random() * pool.length)
|
|
return { pregunta: pool[idx], esGenerica: true }
|
|
}
|
|
|
|
function girar() {
|
|
if (estado.isSpinning || estado.completado) return
|
|
|
|
sounds.wheelSpin?.play()
|
|
|
|
const vueltas = 3 + Math.random() * 2
|
|
const anguloFinal = Math.random() * 360
|
|
const rotacionTotal = rotacionRef.current + vueltas * 360 + anguloFinal
|
|
rotacionRef.current = rotacionTotal
|
|
|
|
setEstado((prev) => ({ ...prev, isSpinning: true, rotacion: rotacionTotal }))
|
|
|
|
setTimeout(() => {
|
|
const anguloNormalizado = ((360 - (rotacionRef.current % 360)) + 360) % 360
|
|
const indiceCategoria = Math.floor(anguloNormalizado / ANGULO_SECCION) % SECCIONES
|
|
const categoria = categorias[indiceCategoria]
|
|
|
|
setEstado((prev) => {
|
|
const { pregunta, esGenerica } = seleccionarPregunta(categoria, prev.preguntasUsadas, prev.bancoPreguntasUsadas)
|
|
const originalIdx = esGenerica
|
|
? bancoGenerico.indexOf(pregunta)
|
|
: categoria.preguntas.indexOf(pregunta)
|
|
|
|
return {
|
|
...prev,
|
|
preguntaActual: pregunta,
|
|
categoriaActual: categoria,
|
|
preguntasUsadas: esGenerica ? prev.preguntasUsadas : {
|
|
...prev.preguntasUsadas,
|
|
[categoria.id]: [...(prev.preguntasUsadas[categoria.id] ?? []), originalIdx],
|
|
},
|
|
bancoPreguntasUsadas: esGenerica
|
|
? [...prev.bancoPreguntasUsadas, originalIdx]
|
|
: prev.bancoPreguntasUsadas,
|
|
}
|
|
})
|
|
}, 4000)
|
|
}
|
|
|
|
function responder(indiceOpcion: number | null) {
|
|
if (!estado.preguntaActual) return
|
|
const esCorrecta = indiceOpcion !== null && indiceOpcion === estado.preguntaActual.correcta
|
|
|
|
if (esCorrecta) sounds.correct?.play()
|
|
else sounds.incorrect?.play()
|
|
|
|
const nuevasCorrectas = estado.correctas + (esCorrecta ? 1 : 0)
|
|
const nuevasRespondidas = estado.preguntasRespondidas + 1
|
|
const completado = nuevasRespondidas >= MAX_PREGUNTAS
|
|
const aprobado = completado && nuevasCorrectas >= MIN_CORRECTAS
|
|
|
|
setEstado((prev) => ({
|
|
...prev,
|
|
correctas: nuevasCorrectas,
|
|
preguntasRespondidas: nuevasRespondidas,
|
|
mostrarFeedback: true,
|
|
feedbackCorrecto: esCorrecta,
|
|
completado,
|
|
aprobado,
|
|
}))
|
|
}
|
|
|
|
function cerrarFeedback() {
|
|
setEstado((prev) => ({
|
|
...prev,
|
|
preguntaActual: null,
|
|
categoriaActual: null,
|
|
mostrarFeedback: false,
|
|
isSpinning: false,
|
|
}))
|
|
}
|
|
|
|
return { estado, categorias, girar, responder, cerrarFeedback, MAX_PREGUNTAS, MIN_CORRECTAS }
|
|
}
|