2026-06-20 15:23:53 +02:00
|
|
|
export function punteggio(striscia) {
|
|
|
|
|
let home = 0, guest = 0
|
|
|
|
|
for (const c of striscia.at(-1).ris) c === 'h' ? home++ : guest++
|
|
|
|
|
return { home, guest }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function servizio(striscia) {
|
|
|
|
|
const set = striscia.at(-1)
|
|
|
|
|
return set.ris.length === 0 ? set.serv === 'h' : set.ris.at(-1) === 'h'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function setVinti(striscia) {
|
|
|
|
|
return {
|
|
|
|
|
home: striscia.filter(s => s.vinc === 'h').length,
|
|
|
|
|
guest: striscia.filter(s => s.vinc === 'g').length,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-10 00:42:48 +01:00
|
|
|
export function createInitialState() {
|
|
|
|
|
return {
|
|
|
|
|
order: true,
|
|
|
|
|
visuForm: false,
|
|
|
|
|
visuStriscia: true,
|
|
|
|
|
modalitaPartita: "3/5",
|
|
|
|
|
sp: {
|
2026-06-20 15:23:53 +02:00
|
|
|
striscia: [{ serv: 'h', ris: '', vinc: null }],
|
2026-02-10 00:42:48 +01:00
|
|
|
nomi: { home: "Antoniana", guest: "Guest" },
|
|
|
|
|
form: {
|
|
|
|
|
home: ["1", "2", "3", "4", "5", "6"],
|
|
|
|
|
guest: ["1", "2", "3", "4", "5", "6"],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function checkVittoria(state) {
|
2026-06-20 15:23:53 +02:00
|
|
|
const { home: puntHome, guest: puntGuest } = punteggio(state.sp.striscia)
|
|
|
|
|
const sv = setVinti(state.sp.striscia)
|
|
|
|
|
const totSet = sv.home + sv.guest
|
2026-05-12 13:49:47 +02:00
|
|
|
const isSetDecisivo = state.modalitaPartita === "2/3" ? totSet >= 2 : totSet >= 4
|
2026-02-10 00:42:48 +01:00
|
|
|
const punteggioVittoria = isSetDecisivo ? 15 : 25
|
|
|
|
|
|
2026-05-12 13:49:47 +02:00
|
|
|
if (puntHome >= punteggioVittoria && puntHome - puntGuest >= 2) return true
|
|
|
|
|
if (puntGuest >= punteggioVittoria && puntGuest - puntHome >= 2) return true
|
2026-02-10 00:42:48 +01:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 10:06:38 +02:00
|
|
|
export function checkVittoriaPartita(state) {
|
|
|
|
|
const setsToWin = state.modalitaPartita === "2/3" ? 2 : 3
|
2026-06-20 15:23:53 +02:00
|
|
|
const sv = setVinti(state.sp.striscia)
|
|
|
|
|
return sv.home >= setsToWin || sv.guest >= setsToWin
|
2026-05-13 10:06:38 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-10 00:42:48 +01:00
|
|
|
export function applyAction(state, action) {
|
2026-05-12 12:22:26 +02:00
|
|
|
const s = structuredClone(state)
|
2026-02-10 00:42:48 +01:00
|
|
|
|
|
|
|
|
switch (action.type) {
|
|
|
|
|
case "incPunt": {
|
|
|
|
|
const team = action.team
|
|
|
|
|
if (checkVittoria(s)) break
|
|
|
|
|
|
2026-06-20 15:23:53 +02:00
|
|
|
const servHome = servizio(s.sp.striscia)
|
|
|
|
|
const cambioPalla = (team === "home") !== servHome
|
2026-05-13 09:35:13 +02:00
|
|
|
s.sp.striscia.at(-1).ris += team === 'home' ? 'h' : 'g'
|
2026-02-10 00:42:48 +01:00
|
|
|
|
|
|
|
|
if (cambioPalla) {
|
|
|
|
|
s.sp.form[team].push(s.sp.form[team].shift())
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "decPunt": {
|
2026-05-12 13:49:47 +02:00
|
|
|
const currentSet = s.sp.striscia.at(-1)
|
2026-05-13 09:35:13 +02:00
|
|
|
if (currentSet.ris.length === 0) break
|
2026-05-12 13:49:47 +02:00
|
|
|
|
2026-05-13 09:35:13 +02:00
|
|
|
const lastScorerShort = currentSet.ris.at(-1)
|
|
|
|
|
const prevServerShort = currentSet.ris.length >= 2
|
|
|
|
|
? currentSet.ris.at(-2)
|
2026-05-12 13:49:47 +02:00
|
|
|
: currentSet.serv
|
|
|
|
|
|
2026-05-13 09:35:13 +02:00
|
|
|
const wasCambioPalla = lastScorerShort !== prevServerShort
|
|
|
|
|
|
|
|
|
|
currentSet.ris = currentSet.ris.slice(0, -1)
|
|
|
|
|
|
|
|
|
|
const lastScorer = lastScorerShort === 'h' ? 'home' : 'guest'
|
2026-05-12 13:49:47 +02:00
|
|
|
|
|
|
|
|
if (wasCambioPalla) {
|
|
|
|
|
s.sp.form[lastScorer].unshift(s.sp.form[lastScorer].pop())
|
2026-02-10 00:42:48 +01:00
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "incSet": {
|
|
|
|
|
const team = action.team
|
2026-06-20 15:23:53 +02:00
|
|
|
const sv = setVinti(s.sp.striscia)
|
|
|
|
|
const count = sv[team]
|
|
|
|
|
const teamShort = team === 'home' ? 'h' : 'g'
|
|
|
|
|
if (count >= 2) {
|
|
|
|
|
// cicla a 0: rimuove le voci fantasma per questo team
|
|
|
|
|
s.sp.striscia = s.sp.striscia.filter(
|
|
|
|
|
entry => !(entry._phantom && entry.vinc === teamShort)
|
|
|
|
|
)
|
2026-02-10 00:42:48 +01:00
|
|
|
} else {
|
2026-06-20 15:23:53 +02:00
|
|
|
// inserisce una voce fantasma prima dell'ultimo set (quello in corso)
|
|
|
|
|
s.sp.striscia.splice(-1, 0, { serv: teamShort, ris: '', vinc: teamShort, _phantom: true })
|
2026-02-10 00:42:48 +01:00
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 13:18:18 +02:00
|
|
|
case "nuovoSet": {
|
|
|
|
|
const team = action.team
|
|
|
|
|
if (team !== 'home' && team !== 'guest') break
|
2026-05-13 10:06:38 +02:00
|
|
|
if (checkVittoriaPartita(s)) break
|
2026-06-20 15:23:53 +02:00
|
|
|
s.sp.striscia.at(-1).vinc = team === 'home' ? 'h' : 'g'
|
|
|
|
|
if (checkVittoriaPartita(s)) break
|
|
|
|
|
s.sp.striscia.push({ serv: team === 'home' ? 'h' : 'g', ris: '', vinc: null })
|
2026-05-12 13:18:18 +02:00
|
|
|
s.sp.form = {
|
|
|
|
|
home: ["1", "2", "3", "4", "5", "6"],
|
|
|
|
|
guest: ["1", "2", "3", "4", "5", "6"],
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-10 00:42:48 +01:00
|
|
|
case "cambiaPalla": {
|
2026-06-20 15:23:53 +02:00
|
|
|
const currentSet = s.sp.striscia.at(-1)
|
|
|
|
|
if (currentSet.ris.length === 0) {
|
|
|
|
|
currentSet.serv = currentSet.serv === 'h' ? 'g' : 'h'
|
2026-02-10 00:42:48 +01:00
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "resetta": {
|
|
|
|
|
s.visuForm = false
|
2026-06-20 15:23:53 +02:00
|
|
|
const servIniziale = s.sp.striscia[0]?.serv ?? 'h'
|
|
|
|
|
s.sp.striscia = [{ serv: servIniziale, ris: '', vinc: null }]
|
2026-02-10 00:42:48 +01:00
|
|
|
s.sp.form = {
|
|
|
|
|
home: ["1", "2", "3", "4", "5", "6"],
|
|
|
|
|
guest: ["1", "2", "3", "4", "5", "6"],
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "toggleFormazione": {
|
|
|
|
|
s.visuForm = !s.visuForm
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "toggleStriscia": {
|
|
|
|
|
s.visuStriscia = !s.visuStriscia
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "toggleOrder": {
|
|
|
|
|
s.order = !s.order
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "setNomi": {
|
|
|
|
|
if (action.home !== undefined) s.sp.nomi.home = action.home
|
|
|
|
|
if (action.guest !== undefined) s.sp.nomi.guest = action.guest
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "setModalita": {
|
|
|
|
|
s.modalitaPartita = action.modalita
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "setFormazione": {
|
|
|
|
|
if (action.team && action.form) {
|
|
|
|
|
s.sp.form[action.team] = [...action.form]
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "confermaCambi": {
|
|
|
|
|
const team = action.team
|
|
|
|
|
const cambi = action.cambi || []
|
|
|
|
|
const form = s.sp.form[team].map((val) => String(val).trim())
|
|
|
|
|
const formAggiornata = [...form]
|
|
|
|
|
|
|
|
|
|
let valid = true
|
|
|
|
|
for (const cambio of cambi) {
|
|
|
|
|
const cin = (cambio.in || "").trim()
|
|
|
|
|
const cout = (cambio.out || "").trim()
|
|
|
|
|
if (!cin || !cout) continue
|
|
|
|
|
if (!/^\d+$/.test(cin) || !/^\d+$/.test(cout)) { valid = false; break }
|
|
|
|
|
if (cin === cout) { valid = false; break }
|
|
|
|
|
if (formAggiornata.includes(cin)) { valid = false; break }
|
|
|
|
|
if (!formAggiornata.includes(cout)) { valid = false; break }
|
|
|
|
|
|
|
|
|
|
const idx = formAggiornata.findIndex((val) => String(val).trim() === cout)
|
|
|
|
|
if (idx !== -1) {
|
|
|
|
|
formAggiornata.splice(idx, 1, cin)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (valid) {
|
|
|
|
|
s.sp.form[team] = formAggiornata
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|