Ruleta actvidad completa en React
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
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 }
|
||||
}
|
||||
Reference in New Issue
Block a user