refactor(striscia): nuova struttura array-di-set, elimina storicoServizio

La striscia diventa un array di set: ogni elemento è { serv, r[] }
dove r è la sequenza di scorer ('home'|'guest') del set.

- Un rally = un elemento in r: minimo non-derivabile
- Tutti i set (passati e corrente) sono conservati nell'array
- Dal set corrente si derivano: punteggio, servizio, cambio palla, rotazione
- Dal set completo si derivano: vincitore (r.at(-1)), score finale (count)
- storicoServizio eliminato: l'undo legge l'entry precedente di r

DisplayPage calcola le strip visive (home/guest) tramite computed
da striscia.at(-1).r senza dati ridondanti nel modello.
This commit is contained in:
2026-05-12 13:49:47 +02:00
parent 5f9e37062c
commit c900153eed
4 changed files with 70 additions and 103 deletions
+1 -2
View File
@@ -224,7 +224,7 @@ export default {
visuStriscia: true, visuStriscia: true,
modalitaPartita: "3/5", modalitaPartita: "3/5",
sp: { sp: {
striscia: { home: [0], guest: [0] }, striscia: [{ serv: 'home', r: [] }],
servHome: true, servHome: true,
punt: { home: 0, guest: 0 }, punt: { home: 0, guest: 0 },
set: { home: 0, guest: 0 }, set: { home: 0, guest: 0 },
@@ -233,7 +233,6 @@ export default {
home: ["1", "2", "3", "4", "5", "6"], home: ["1", "2", "3", "4", "5", "6"],
guest: ["1", "2", "3", "4", "5", "6"], guest: ["1", "2", "3", "4", "5", "6"],
}, },
storicoServizio: [],
}, },
}, },
} }
+22 -17
View File
@@ -92,16 +92,16 @@
<div class="striscia" v-if="state.visuStriscia"> <div class="striscia" v-if="state.visuStriscia">
<span class="striscia-nome text-bold">{{ state.sp.nomi.home }}</span> <span class="striscia-nome text-bold">{{ state.sp.nomi.home }}</span>
<div class="striscia-items" ref="homeItems"> <div class="striscia-items" ref="homeItems">
<div v-for="(h, i) in state.sp.striscia.home" :key="'sh'+i" <div v-for="(h, i) in stricciaStrip.home" :key="'sh'+i"
class="item" :class="{ 'item-vuoto': String(h).trim() === '' }"> class="item" :class="{ 'item-vuoto': h === ' ' }">
{{ String(h) }} {{ h }}
</div> </div>
</div> </div>
<span class="striscia-nome text-bold guest-striscia">{{ state.sp.nomi.guest }}</span> <span class="striscia-nome text-bold guest-striscia">{{ state.sp.nomi.guest }}</span>
<div class="striscia-items guest-striscia" ref="guestItems"> <div class="striscia-items guest-striscia" ref="guestItems">
<div v-for="(h, i) in state.sp.striscia.guest" :key="'sg'+i" <div v-for="(h, i) in stricciaStrip.guest" :key="'sg'+i"
class="item" :class="{ 'item-vuoto': String(h).trim() === '' }"> class="item" :class="{ 'item-vuoto': h === ' ' }">
{{ String(h) }} {{ h }}
</div> </div>
</div> </div>
</div> </div>
@@ -132,7 +132,7 @@ export default {
visuStriscia: true, visuStriscia: true,
modalitaPartita: "3/5", modalitaPartita: "3/5",
sp: { sp: {
striscia: { home: [0], guest: [0] }, striscia: [{ serv: 'home', r: [] }],
servHome: true, servHome: true,
punt: { home: 0, guest: 0 }, punt: { home: 0, guest: 0 },
set: { home: 0, guest: 0 }, set: { home: 0, guest: 0 },
@@ -141,7 +141,6 @@ export default {
home: ["1", "2", "3", "4", "5", "6"], home: ["1", "2", "3", "4", "5", "6"],
guest: ["1", "2", "3", "4", "5", "6"], guest: ["1", "2", "3", "4", "5", "6"],
}, },
storicoServizio: [],
}, },
}, },
} }
@@ -192,23 +191,29 @@ export default {
this.ws = null this.ws = null
} }
}, },
watch: { computed: {
'state.sp.striscia.home': { stricciaStrip() {
deep: true, const currentSet = this.state.sp.striscia.at(-1)
handler() { if (!currentSet) return { home: [], guest: [] }
this.$nextTick(() => { let h = 0, g = 0
if (this.$refs.homeItems) this.$refs.homeItems.scrollLeft = this.$refs.homeItems.scrollWidth const home = [], guest = []
}) for (const scorer of currentSet.r) {
if (scorer === 'home') { h++; home.push(h); guest.push(' ') }
else { g++; guest.push(g); home.push(' ') }
} }
return { home, guest }
}, },
'state.sp.striscia.guest': { },
watch: {
'state.sp.striscia': {
deep: true, deep: true,
handler() { handler() {
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.homeItems) this.$refs.homeItems.scrollLeft = this.$refs.homeItems.scrollWidth
if (this.$refs.guestItems) this.$refs.guestItems.scrollLeft = this.$refs.guestItems.scrollWidth if (this.$refs.guestItems) this.$refs.guestItems.scrollLeft = this.$refs.guestItems.scrollWidth
}) })
} }
} },
}, },
methods: { methods: {
isMobile() { isMobile() {
+24 -51
View File
@@ -1,8 +1,3 @@
/**
* Logica di gioco condivisa per il segnapunti.
* Utilizzata sia dal server WebSocket sia dal client per l'anteprima locale.
*/
export function createInitialState() { export function createInitialState() {
return { return {
order: true, order: true,
@@ -10,7 +5,7 @@ export function createInitialState() {
visuStriscia: true, visuStriscia: true,
modalitaPartita: "3/5", modalitaPartita: "3/5",
sp: { sp: {
striscia: { home: [0], guest: [0] }, striscia: [{ serv: 'home', r: [] }],
servHome: true, servHome: true,
punt: { home: 0, guest: 0 }, punt: { home: 0, guest: 0 },
set: { home: 0, guest: 0 }, set: { home: 0, guest: 0 },
@@ -19,7 +14,6 @@ export function createInitialState() {
home: ["1", "2", "3", "4", "5", "6"], home: ["1", "2", "3", "4", "5", "6"],
guest: ["1", "2", "3", "4", "5", "6"], guest: ["1", "2", "3", "4", "5", "6"],
}, },
storicoServizio: [],
}, },
} }
} }
@@ -31,22 +25,11 @@ export function checkVittoria(state) {
const setGuest = state.sp.set.guest const setGuest = state.sp.set.guest
const totSet = setHome + setGuest const totSet = setHome + setGuest
let isSetDecisivo = false const isSetDecisivo = state.modalitaPartita === "2/3" ? totSet >= 2 : totSet >= 4
if (state.modalitaPartita === "2/3") {
isSetDecisivo = totSet >= 2
} else {
isSetDecisivo = totSet >= 4
}
const punteggioVittoria = isSetDecisivo ? 15 : 25 const punteggioVittoria = isSetDecisivo ? 15 : 25
if (puntHome >= punteggioVittoria && puntHome - puntGuest >= 2) { if (puntHome >= punteggioVittoria && puntHome - puntGuest >= 2) return true
return true if (puntGuest >= punteggioVittoria && puntGuest - puntHome >= 2) return true
}
if (puntGuest >= punteggioVittoria && puntGuest - puntHome >= 2) {
return true
}
return false return false
} }
@@ -59,16 +42,8 @@ export function applyAction(state, action) {
if (checkVittoria(s)) break if (checkVittoria(s)) break
const cambioPalla = (team === "home") !== s.sp.servHome const cambioPalla = (team === "home") !== s.sp.servHome
s.sp.storicoServizio.push({ servHome: s.sp.servHome, cambioPalla })
s.sp.punt[team]++ s.sp.punt[team]++
if (team === "home") { s.sp.striscia.at(-1).r.push(team)
s.sp.striscia.home.push(s.sp.punt.home)
s.sp.striscia.guest.push(" ")
} else {
s.sp.striscia.guest.push(s.sp.punt.guest)
s.sp.striscia.home.push(" ")
}
if (cambioPalla) { if (cambioPalla) {
s.sp.form[team].push(s.sp.form[team].shift()) s.sp.form[team].push(s.sp.form[team].shift())
@@ -79,23 +54,22 @@ export function applyAction(state, action) {
} }
case "decPunt": { case "decPunt": {
if (s.sp.storicoServizio.length > 0) { const currentSet = s.sp.striscia.at(-1)
const tmpHome = s.sp.striscia.home.pop() if (currentSet.r.length === 0) break
s.sp.striscia.guest.pop()
const statoServizio = s.sp.storicoServizio.pop()
if (tmpHome === " ") { const lastScorer = currentSet.r[currentSet.r.length - 1]
s.sp.punt.guest-- const prevServer = currentSet.r.length >= 2
if (statoServizio.cambioPalla) { ? currentSet.r[currentSet.r.length - 2]
s.sp.form.guest.unshift(s.sp.form.guest.pop()) : currentSet.serv
}
} else { const wasCambioPalla = lastScorer !== prevServer
s.sp.punt.home--
if (statoServizio.cambioPalla) { currentSet.r.pop()
s.sp.form.home.unshift(s.sp.form.home.pop()) s.sp.punt[lastScorer]--
} s.sp.servHome = prevServer === 'home'
}
s.sp.servHome = statoServizio.servHome if (wasCambioPalla) {
s.sp.form[lastScorer].unshift(s.sp.form[lastScorer].pop())
} }
break break
} }
@@ -116,8 +90,8 @@ export function applyAction(state, action) {
s.sp.set[team]++ s.sp.set[team]++
s.sp.punt.home = 0 s.sp.punt.home = 0
s.sp.punt.guest = 0 s.sp.punt.guest = 0
s.sp.striscia = { home: [0], guest: [0] } s.sp.servHome = team === 'home'
s.sp.storicoServizio = [] s.sp.striscia.push({ serv: team, r: [] })
s.sp.form = { s.sp.form = {
home: ["1", "2", "3", "4", "5", "6"], home: ["1", "2", "3", "4", "5", "6"],
guest: ["1", "2", "3", "4", "5", "6"], guest: ["1", "2", "3", "4", "5", "6"],
@@ -128,7 +102,7 @@ export function applyAction(state, action) {
case "cambiaPalla": { case "cambiaPalla": {
if (s.sp.punt.home === 0 && s.sp.punt.guest === 0) { if (s.sp.punt.home === 0 && s.sp.punt.guest === 0) {
s.sp.servHome = !s.sp.servHome s.sp.servHome = !s.sp.servHome
s.sp.striscia = { home: [0], guest: [0] } s.sp.striscia.at(-1).serv = s.sp.servHome ? 'home' : 'guest'
} }
break break
} }
@@ -143,8 +117,7 @@ export function applyAction(state, action) {
home: ["1", "2", "3", "4", "5", "6"], home: ["1", "2", "3", "4", "5", "6"],
guest: ["1", "2", "3", "4", "5", "6"], guest: ["1", "2", "3", "4", "5", "6"],
} }
s.sp.striscia = { home: [0], guest: [0] } s.sp.striscia = [{ serv: s.sp.servHome ? 'home' : 'guest', r: [] }]
s.sp.storicoServizio = []
break break
} }
+23 -33
View File
@@ -31,13 +31,10 @@ describe('Game Logic (gameState.js)', () => {
expect(state.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"]) expect(state.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"])
}) })
it('dovrebbe avere la striscia iniziale a [0]', () => { it('dovrebbe avere la striscia iniziale con un set vuoto', () => {
expect(state.sp.striscia.home).toEqual([0]) expect(state.sp.striscia).toHaveLength(1)
expect(state.sp.striscia.guest).toEqual([0]) expect(state.sp.striscia[0].serv).toBe('home')
}) expect(state.sp.striscia[0].r).toEqual([])
it('dovrebbe avere storico servizio vuoto', () => {
expect(state.sp.storicoServizio).toEqual([])
}) })
it('dovrebbe avere modalità 3/5 di default', () => { it('dovrebbe avere modalità 3/5 di default', () => {
@@ -116,21 +113,19 @@ describe('Game Logic (gameState.js)', () => {
it('dovrebbe aggiornare la striscia per punto Home', () => { it('dovrebbe aggiornare la striscia per punto Home', () => {
const s = applyAction(state, { type: 'incPunt', team: 'home' }) const s = applyAction(state, { type: 'incPunt', team: 'home' })
expect(s.sp.striscia.home).toEqual([0, 1]) expect(s.sp.striscia.at(-1).r).toEqual(['home'])
expect(s.sp.striscia.guest).toEqual([0, " "])
}) })
it('dovrebbe aggiornare la striscia per punto Guest', () => { it('dovrebbe aggiornare la striscia per punto Guest', () => {
const s = applyAction(state, { type: 'incPunt', team: 'guest' }) const s = applyAction(state, { type: 'incPunt', team: 'guest' })
expect(s.sp.striscia.guest).toEqual([0, 1]) expect(s.sp.striscia.at(-1).r).toEqual(['guest'])
expect(s.sp.striscia.home).toEqual([0, " "])
}) })
it('dovrebbe registrare lo storico servizio', () => { it('dovrebbe registrare scorer nella striscia', () => {
const s = applyAction(state, { type: 'incPunt', team: 'home' }) let s = applyAction(state, { type: 'incPunt', team: 'home' })
expect(s.sp.storicoServizio).toHaveLength(1) s = applyAction(s, { type: 'incPunt', team: 'guest' })
expect(s.sp.storicoServizio[0]).toHaveProperty('servHome') s = applyAction(s, { type: 'incPunt', team: 'home' })
expect(s.sp.storicoServizio[0]).toHaveProperty('cambioPalla') expect(s.sp.striscia.at(-1).r).toEqual(['home', 'guest', 'home'])
}) })
it('non dovrebbe incrementare i punti dopo vittoria', () => { it('non dovrebbe incrementare i punti dopo vittoria', () => {
@@ -185,7 +180,7 @@ describe('Game Logic (gameState.js)', () => {
it('dovrebbe ripristinare la striscia', () => { it('dovrebbe ripristinare la striscia', () => {
const s1 = applyAction(state, { type: 'incPunt', team: 'home' }) const s1 = applyAction(state, { type: 'incPunt', team: 'home' })
const s2 = applyAction(s1, { type: 'decPunt' }) const s2 = applyAction(s1, { type: 'decPunt' })
expect(s2.sp.striscia.home).toEqual([0]) expect(s2.sp.striscia.at(-1).r).toEqual([])
}) })
it('dovrebbe gestire undo multipli in sequenza', () => { it('dovrebbe gestire undo multipli in sequenza', () => {
@@ -250,16 +245,17 @@ describe('Game Logic (gameState.js)', () => {
expect(s.sp.punt.guest).toBe(0) expect(s.sp.punt.guest).toBe(0)
}) })
it('dovrebbe resettare la striscia', () => { it('dovrebbe aggiungere un nuovo set vuoto alla striscia', () => {
state.sp.punt.home = 25
const s = applyAction(state, { type: 'nuovoSet', team: 'home' }) const s = applyAction(state, { type: 'nuovoSet', team: 'home' })
expect(s.sp.striscia).toEqual({ home: [0], guest: [0] }) expect(s.sp.striscia).toHaveLength(2)
expect(s.sp.striscia.at(-1).r).toEqual([])
expect(s.sp.striscia.at(-1).serv).toBe('home')
}) })
it('dovrebbe resettare lo storico servizio', () => { it('dovrebbe conservare il set precedente nella striscia', () => {
state.sp.storicoServizio = [{ servHome: true, cambioPalla: false }] state.sp.striscia[0].r = ['home', 'guest', 'home']
const s = applyAction(state, { type: 'nuovoSet', team: 'home' }) const s = applyAction(state, { type: 'nuovoSet', team: 'home' })
expect(s.sp.storicoServizio).toEqual([]) expect(s.sp.striscia[0].r).toEqual(['home', 'guest', 'home'])
}) })
it('dovrebbe resettare le formazioni', () => { it('dovrebbe resettare le formazioni', () => {
@@ -664,17 +660,11 @@ describe('Game Logic (gameState.js)', () => {
expect(s.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"]) expect(s.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"])
}) })
it('dovrebbe resettare la striscia', () => { it('dovrebbe resettare la striscia a un set vuoto', () => {
state.sp.striscia = { home: [0, 1, 2, 3], guest: [0, " ", " ", 1] } state.sp.striscia = [{ serv: 'home', r: ['home', 'guest', 'home'] }, { serv: 'home', r: ['guest'] }]
const s = applyAction(state, { type: 'resetta' }) const s = applyAction(state, { type: 'resetta' })
expect(s.sp.striscia.home).toEqual([0]) expect(s.sp.striscia).toHaveLength(1)
expect(s.sp.striscia.guest).toEqual([0]) expect(s.sp.striscia[0].r).toEqual([])
})
it('dovrebbe resettare lo storico servizio', () => {
state.sp.storicoServizio = [{ servHome: true, cambioPalla: false }]
const s = applyAction(state, { type: 'resetta' })
expect(s.sp.storicoServizio).toEqual([])
}) })
it('dovrebbe impostare visuForm a false', () => { it('dovrebbe impostare visuForm a false', () => {