Soglie dei badge ricalibrate su ~71 eventi a stagione; obiettivo pagelle a 1000
Presenza fissa 15/35/55, Sempre in palestra 5/10/20, Sherpa dei palloni 3/5/8; obiettivo o13 da 200 a 1000 voti. Test di integrazione ora derivano le soglie dalle definizioni; documenti e CHANGELOG aggiornati. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
50930ac142
commit
cfbba613dc
11 files changed
+107
-86
No files matched your search
@@ -13,6 +13,18 @@ versioni `0.y.z` erano pre-release.
|
|||||||
|
|
||||||
## [Non rilasciato]
|
## [Non rilasciato]
|
||||||
|
|
||||||
|
### Modificato
|
||||||
|
|
||||||
|
- Badge: nuove soglie calibrate su circa 71 eventi a stagione. «Presenza fissa» passa a
|
||||||
|
15/35/55 presenze, «Sempre in palestra» a 5/10/20 allenamenti di fila, «Sherpa dei palloni»
|
||||||
|
a 3/5/8 turni. I badge si calcolano a runtime: chi ha già un grado può scendere.
|
||||||
|
- Obiettivi: «pagelle compilate» passa da 200 a 1000 voti (con 90-150 voti a partita, 200 si
|
||||||
|
raggiungevano dopo 2-3 partite).
|
||||||
|
- Nessun cambio di logica: i calcoli di badge e obiettivi sono stati spostati in `src/lib/`
|
||||||
|
(`percentuale`, `quantiAlGrado`, `mediaPresenzeRosa`, …) e le soglie dei badge segreti sono
|
||||||
|
costanti usate anche nei testi. Cambia solo il testo della celebrazione di «Aspettate,
|
||||||
|
arrivo!», che non ripete più la descrizione.
|
||||||
|
|
||||||
## [1.6.2] - 2026-11-11
|
## [1.6.2] - 2026-11-11
|
||||||
|
|
||||||
Layout corretto sugli schermi stretti e statistiche tecniche dai + dello scout in Squadra e
|
Layout corretto sugli schermi stretti e statistiche tecniche dai + dello scout in Squadra e
|
||||||
|
|||||||
@@ -90,9 +90,9 @@ soglia raggiunta o superata (soglie inclusive), oltre l'oro resta oro.
|
|||||||
| ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | --------------- |
|
| ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | --------------- |
|
||||||
| `mvp` | MVP | partite vinte nettamente al voto MVP dei compagni (`g.mvp`, vedi pipeline sopra) | 1 / 3 / 5 |
|
| `mvp` | MVP | partite vinte nettamente al voto MVP dei compagni (`g.mvp`, vedi pipeline sopra) | 1 / 3 / 5 |
|
||||||
| `pagella` | Pagellone | media dei voti pagella ricevuti dai compagni a fine partita (`g.mediaVoto`), solo se ne ha ricevuti almeno `VOTI_MINIMI_PAGELLA` (5) | 6.5 / 7.5 / 8.5 |
|
| `pagella` | Pagellone | media dei voti pagella ricevuti dai compagni a fine partita (`g.mediaVoto`), solo se ne ha ricevuti almeno `VOTI_MINIMI_PAGELLA` (5) | 6.5 / 7.5 / 8.5 |
|
||||||
| `palloni` | Sherpa dei palloni | quante volte hai confermato il turno palloni (`g.palloni`) — le proposte automatiche non ancora confermate non contano | 3 / 6 / 10 |
|
| `palloni` | Sherpa dei palloni | quante volte hai confermato il turno palloni (`g.palloni`) — le proposte automatiche non ancora confermate non contano | 3 / 5 / 8 |
|
||||||
| `presenze` | Presenza fissa | totale presenze (presente o ritardo) a eventi/partite di sempre, non solo della stagione in corso (`g.presenze`) | 5 / 15 / 30 |
|
| `presenze` | Presenza fissa | totale presenze (presente o ritardo) a eventi/partite di sempre, non solo della stagione in corso (`g.presenze`) | 15 / 35 / 55 |
|
||||||
| `serie-allenamenti` | Sempre in palestra | allenamenti consecutivi presenti (`g.serieAllenamenti`); un infortunio non spezza la serie, un'assenza sì | 3 / 6 / 10 |
|
| `serie-allenamenti` | Sempre in palestra | allenamenti consecutivi presenti (`g.serieAllenamenti`); un infortunio non spezza la serie, un'assenza sì | 5 / 10 / 20 |
|
||||||
|
|
||||||
### Badge segreti (booleani, nascosti finché non sbloccati)
|
### Badge segreti (booleani, nascosti finché non sbloccati)
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ corrente dinamico (vedi sotto).
|
|||||||
| `o2` | Tutti rispondono alle convocazioni | risposte totali / eventi possibili (esclusi i compleanni) | 90% | `risposte_presenze` |
|
| `o2` | Tutti rispondono alle convocazioni | risposte totali / eventi possibili (esclusi i compleanni) | 90% | `risposte_presenze` |
|
||||||
| `o7` | Presenze collettive | % di presenze (presente/ritardo) su tutti gli allenamenti e le partite definiti, passati e futuri; posti = convocati dell'evento (tutta la rosa se vuoto) | 90 | `eventi_app` + `risposte_presenze` |
|
| `o7` | Presenze collettive | % di presenze (presente/ritardo) su tutti gli allenamenti e le partite definiti, passati e futuri; posti = convocati dell'evento (tutta la rosa se vuoto) | 90 | `eventi_app` + `risposte_presenze` |
|
||||||
| `o12` | Media pagelle da 7.5 | media di tutti i voti, arrotondata a una cifra decimale | 7.5 | `pagelle_voti` |
|
| `o12` | Media pagelle da 7.5 | media di tutti i voti, arrotondata a una cifra decimale | 7.5 | `pagelle_voti` |
|
||||||
| `o13` | 200 pagelle compilate | conteggio voti | 200 | `pagelle_voti` |
|
| `o13` | 1000 pagelle compilate | conteggio voti | 1000 | `pagelle_voti` |
|
||||||
| `o11` | Continuità di squadra | giocatori con ≥3 allenamenti consecutivi | 12 (min. per un 6vs6) | `serieAllenamenti` |
|
| `o11` | Continuità di squadra | giocatori con ≥3 allenamenti consecutivi | 12 (min. per un 6vs6) | `serieAllenamenti` |
|
||||||
| `o3` | Prima vittoria del campionato | `min(vittorie, 1)` | 1 | JSON partite CSI (vedi sotto) |
|
| `o3` | Prima vittoria del campionato | `min(vittorie, 1)` | 1 | JSON partite CSI (vedi sotto) |
|
||||||
| `o4` | 5 vittorie in campionato | `min(vittorie, 5)` | 5 | JSON partite CSI |
|
| `o4` | 5 vittorie in campionato | `min(vittorie, 5)` | 5 | JSON partite CSI |
|
||||||
@@ -58,7 +58,7 @@ badge) con stato attuale, come si calcola, cosa conta e cosa no, quando vale, da
|
|||||||
dati e un esempio con numeri. Per le due percentuali di presenze (`o1`, `o7`) la card distingue «Presenza persa» (assente, forse, infortunato, nessuna risposta: il posto resta vuoto) da «Non entra nel calcolo» (cene, eventi di squadra, compleanni). I testi stanno nel campo `dettaglio` di ogni obiettivo in
|
dati e un esempio con numeri. Per le due percentuali di presenze (`o1`, `o7`) la card distingue «Presenza persa» (assente, forse, infortunato, nessuna risposta: il posto resta vuoto) da «Non entra nel calcolo» (cene, eventi di squadra, compleanni). I testi stanno nel campo `dettaglio` di ogni obiettivo in
|
||||||
`obiettivi.ts`: chi cambia il calcolo o il target di un obiettivo aggiorna anche il suo
|
`obiettivi.ts`: chi cambia il calcolo o il target di un obiettivo aggiorna anche il suo
|
||||||
`dettaglio`, altrimenti la card mente. Un obiettivo che supera il 90% genera anche una notifica
|
`dettaglio`, altrimenti la card mente. Un obiettivo che supera il 90% genera anche una notifica
|
||||||
smart (`notifiche-smart.ts`). I target fissi (200 pagelle, 7.5 di media, 1/5/10
|
smart (`notifiche-smart.ts`). I target fissi (1000 pagelle, 7.5 di media, 1/5/10
|
||||||
vittorie) sono scelte editoriali da rivedere a mano a ogni stagione — nessuna configurazione o
|
vittorie) sono scelte editoriali da rivedere a mano a ogni stagione — nessuna configurazione o
|
||||||
UI per farlo, si cambia il numero in `obiettivi.ts`. Fa eccezione "Continuità di squadra"
|
UI per farlo, si cambia il numero in `obiettivi.ts`. Fa eccezione "Continuità di squadra"
|
||||||
(vedi sotto): il suo target ha un significato specifico, non va scalato come gli altri.
|
(vedi sotto): il suo target ha un significato specifico, non va scalato come gli altri.
|
||||||
|
|||||||
@@ -244,8 +244,8 @@ fastidio, va distinto lì — il calcolo non sa differenziare "mai partito" da "
|
|||||||
Toccare la regola di calcolo muove anche questi, che non hanno logica propria:
|
Toccare la regola di calcolo muove anche questi, che non hanno logica propria:
|
||||||
|
|
||||||
| Dove | Cosa | Soglie |
|
| Dove | Cosa | Soglie |
|
||||||
| ------------------------------- | -------------------------------------------------------------- | --------------------------- |
|
| ------------------------------- | -------------------------------------------------------------- | ---------------------------- |
|
||||||
| `badges.ts` `serie-allenamenti` | "Sempre in palestra", su `serieAllenamenti` | bronzo 3, argento 6, oro 10 |
|
| `badges.ts` `serie-allenamenti` | "Sempre in palestra", su `serieAllenamenti` | bronzo 5, argento 10, oro 20 |
|
||||||
| `obiettivi.ts` `o11` | "Continuità di squadra": giocatori con `serieAllenamenti >= 3` | target 12 |
|
| `obiettivi.ts` `o11` | "Continuità di squadra": giocatori con `serieAllenamenti >= 3` | target 12 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+3
-3
@@ -100,7 +100,7 @@ export const badgeDefs: BadgeDef[] = [
|
|||||||
"Quante volte ti sei caricato la sacca dei palloni: lavoro oscuro, badge luminoso.",
|
"Quante volte ti sei caricato la sacca dei palloni: lavoro oscuro, badge luminoso.",
|
||||||
unita: "turni palloni",
|
unita: "turni palloni",
|
||||||
icon: CircleDot,
|
icon: CircleDot,
|
||||||
soglie: { bronzo: 3, argento: 6, oro: 10 },
|
soglie: { bronzo: 3, argento: 5, oro: 8 },
|
||||||
valore: (g) => g.palloni,
|
valore: (g) => g.palloni,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -109,7 +109,7 @@ export const badgeDefs: BadgeDef[] = [
|
|||||||
descrizione: "Per chi è sempre lì, partita dopo partita, senza tirarsi indietro.",
|
descrizione: "Per chi è sempre lì, partita dopo partita, senza tirarsi indietro.",
|
||||||
unita: "presenze",
|
unita: "presenze",
|
||||||
icon: Repeat,
|
icon: Repeat,
|
||||||
soglie: { bronzo: 5, argento: 15, oro: 30 },
|
soglie: { bronzo: 15, argento: 35, oro: 55 },
|
||||||
valore: (g) => g.presenze,
|
valore: (g) => g.presenze,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -119,7 +119,7 @@ export const badgeDefs: BadgeDef[] = [
|
|||||||
"Allenamenti consecutivi a cui sei stato presente: la costanza paga più del talento.",
|
"Allenamenti consecutivi a cui sei stato presente: la costanza paga più del talento.",
|
||||||
unita: "allenamenti di fila",
|
unita: "allenamenti di fila",
|
||||||
icon: Rocket,
|
icon: Rocket,
|
||||||
soglie: { bronzo: 3, argento: 6, oro: 10 },
|
soglie: { bronzo: 5, argento: 10, oro: 20 },
|
||||||
valore: (g) => g.serieAllenamenti,
|
valore: (g) => g.serieAllenamenti,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -284,10 +284,10 @@ export function obiettiviSquadra(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "o13",
|
id: "o13",
|
||||||
titolo: "200 pagelle compilate",
|
titolo: "1000 pagelle compilate",
|
||||||
descrizione: "Quanti voti la squadra ha dato nel corso della stagione",
|
descrizione: "Quanti voti la squadra ha dato nel corso della stagione",
|
||||||
valore: ctx.pagelle.length,
|
valore: ctx.pagelle.length,
|
||||||
target: 200,
|
target: 1000,
|
||||||
unita: "voti",
|
unita: "voti",
|
||||||
emoji: "🗳️",
|
emoji: "🗳️",
|
||||||
impatto: "Vota i compagni a fine partita: bastano due minuti.",
|
impatto: "Vota i compagni a fine partita: bastano due minuti.",
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ if (!locale) {
|
|||||||
// m1-m3: turno confermato esplicitamente a pv1 (3 turni -> soglia bronzo).
|
// m1-m3: turno confermato esplicitamente a pv1 (3 turni -> soglia bronzo).
|
||||||
// m4: NESSUN turno confermato -> non deve contare per nessuno, anche se un algoritmo
|
// m4: NESSUN turno confermato -> non deve contare per nessuno, anche se un algoritmo
|
||||||
// di rotazione (usato altrove per la UI) proporrebbe qualcuno in automatico.
|
// di rotazione (usato altrove per la UI) proporrebbe qualcuno in automatico.
|
||||||
// m5-m7: confermati di nuovo a pv1 (6 turni totali -> soglia argento).
|
// m5-m6: confermati di nuovo a pv1 (5 turni totali -> soglia argento).
|
||||||
const eventi = [
|
const eventi = [
|
||||||
[`${PREFISSO}-m1`, "2020-01-01", "pv1"],
|
[`${PREFISSO}-m1`, "2020-01-01", "pv1"],
|
||||||
[`${PREFISSO}-m2`, "2020-01-02", "pv1"],
|
[`${PREFISSO}-m2`, "2020-01-02", "pv1"],
|
||||||
@@ -114,7 +114,6 @@ if (!locale) {
|
|||||||
[`${PREFISSO}-m4`, "2020-01-04", null],
|
[`${PREFISSO}-m4`, "2020-01-04", null],
|
||||||
[`${PREFISSO}-m5`, "2020-01-05", "pv1"],
|
[`${PREFISSO}-m5`, "2020-01-05", "pv1"],
|
||||||
[`${PREFISSO}-m6`, "2020-01-06", "pv1"],
|
[`${PREFISSO}-m6`, "2020-01-06", "pv1"],
|
||||||
[`${PREFISSO}-m7`, "2020-01-07", "pv1"],
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
for (const [id, data] of eventi.slice(0, 3)) await creaEvento(id, data);
|
for (const [id, data] of eventi.slice(0, 3)) await creaEvento(id, data);
|
||||||
@@ -128,7 +127,7 @@ if (!locale) {
|
|||||||
assert.equal(
|
assert.equal(
|
||||||
statoBadge(palloniDef, giocatoreAzzerato(conteggioA3["pv1"]!)).grado,
|
statoBadge(palloniDef, giocatoreAzzerato(conteggioA3["pv1"]!)).grado,
|
||||||
"bronzo",
|
"bronzo",
|
||||||
"3 turni: bronzo, non oltre (soglia argento è 6)",
|
"3 turni: bronzo, non oltre (soglia argento è 5)",
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const [id, data] of eventi.slice(3)) await creaEvento(id, data);
|
for (const [id, data] of eventi.slice(3)) await creaEvento(id, data);
|
||||||
@@ -136,13 +135,13 @@ if (!locale) {
|
|||||||
if (giocatore) await confermaTurno(id, giocatore);
|
if (giocatore) await confermaTurno(id, giocatore);
|
||||||
|
|
||||||
const eventiLetti = await leggiEventi();
|
const eventiLetti = await leggiEventi();
|
||||||
assert.equal(eventiLetti.length, 7, "tutti gli eventi scritti si rileggono");
|
assert.equal(eventiLetti.length, 6, "tutti gli eventi scritti si rileggono");
|
||||||
|
|
||||||
const salvati = await leggiTurniSalvati();
|
const salvati = await leggiTurniSalvati();
|
||||||
assert.equal(Object.keys(salvati).length, 6, "6 turni confermati esplicitamente, m4 no");
|
assert.equal(Object.keys(salvati).length, 5, "5 turni confermati esplicitamente, m4 no");
|
||||||
|
|
||||||
const conteggio = conteggioTurni(salvati, eventiLetti, OGGI);
|
const conteggio = conteggioTurni(salvati, eventiLetti, OGGI);
|
||||||
assert.equal(conteggio["pv1"], 6, "6 turni confermati per pv1");
|
assert.equal(conteggio["pv1"], 5, "5 turni confermati per pv1");
|
||||||
assert.equal(
|
assert.equal(
|
||||||
conteggio["pv2"] ?? 0,
|
conteggio["pv2"] ?? 0,
|
||||||
0,
|
0,
|
||||||
@@ -150,7 +149,7 @@ if (!locale) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const badgePv1 = statoBadge(palloniDef, giocatoreAzzerato(conteggio["pv1"]!));
|
const badgePv1 = statoBadge(palloniDef, giocatoreAzzerato(conteggio["pv1"]!));
|
||||||
assert.equal(badgePv1.grado, "argento", "6 turni: soglia argento raggiunta");
|
assert.equal(badgePv1.grado, "argento", "5 turni: soglia argento raggiunta");
|
||||||
|
|
||||||
const badgePv2 = statoBadge(palloniDef, giocatoreAzzerato(conteggio["pv2"] ?? 0));
|
const badgePv2 = statoBadge(palloniDef, giocatoreAzzerato(conteggio["pv2"] ?? 0));
|
||||||
assert.equal(badgePv2.grado, null, "zero turni confermati: nessun badge");
|
assert.equal(badgePv2.grado, null, "zero turni confermati: nessun badge");
|
||||||
|
|||||||
@@ -122,54 +122,63 @@ if (!locale) {
|
|||||||
await prova(
|
await prova(
|
||||||
"il badge presenze passa da bronzo a oro con presenze vere lette dal database",
|
"il badge presenze passa da bronzo a oro con presenze vere lette dal database",
|
||||||
async () => {
|
async () => {
|
||||||
// 30 allenamenti passati. "pp1" risponde a tutti e 30 (i primi 5 "presente", un
|
// Tanti allenamenti passati quanti ne chiede l'oro. "pp1" risponde a tutti (un "ritardo"
|
||||||
// "ritardo" nel mezzo per verificare che conti come presenza, il resto "presente"),
|
// nel mezzo per verificare che conti come presenza), così una sola scrittura basta a
|
||||||
// così una sola scrittura basta a verificare le tre soglie 5/15/30.
|
// verificare le tre soglie.
|
||||||
for (let i = 1; i <= 30; i += 1) {
|
const { bronzo, argento, oro } = presenzeDef.soglie;
|
||||||
|
for (let i = 1; i <= oro; i += 1) {
|
||||||
await creaEvento(`${PREFISSO}-a${String(i).padStart(2, "0")}`, i);
|
await creaEvento(`${PREFISSO}-a${String(i).padStart(2, "0")}`, i);
|
||||||
}
|
}
|
||||||
for (let i = 1; i <= 30; i += 1) {
|
for (let i = 1; i <= oro; i += 1) {
|
||||||
const stato = i === 10 ? "ritardo" : "presente";
|
const stato = i === 10 ? "ritardo" : "presente";
|
||||||
await rispondi(`${PREFISSO}-a${String(i).padStart(2, "0")}`, "pp1", stato);
|
await rispondi(`${PREFISSO}-a${String(i).padStart(2, "0")}`, "pp1", stato);
|
||||||
}
|
}
|
||||||
// "pp2" risponde solo ai primi 5: resta a bronzo, non oltre.
|
// "pp2" risponde solo ai primi `bronzo`: resta a bronzo, non oltre.
|
||||||
for (let i = 1; i <= 5; i += 1) {
|
for (let i = 1; i <= bronzo; i += 1) {
|
||||||
await rispondi(`${PREFISSO}-a${String(i).padStart(2, "0")}`, "pp2", "presente");
|
await rispondi(`${PREFISSO}-a${String(i).padStart(2, "0")}`, "pp2", "presente");
|
||||||
}
|
}
|
||||||
// "pp2" risponde anche "assente" a un evento: non deve contare.
|
// "pp2" risponde anche "assente" a un evento: non deve contare.
|
||||||
await rispondi(`${PREFISSO}-a06`, "pp2", "assente");
|
await rispondi(`${PREFISSO}-a${String(bronzo + 1).padStart(2, "0")}`, "pp2", "assente");
|
||||||
|
|
||||||
const eventi = await leggiEventi();
|
const eventi = await leggiEventi();
|
||||||
assert.equal(eventi.length, 30, "tutti gli eventi scritti si rileggono");
|
assert.equal(eventi.length, oro, "tutti gli eventi scritti si rileggono");
|
||||||
const presenze = await leggiPresenze();
|
const presenze = await leggiPresenze();
|
||||||
|
|
||||||
const contoPp1 = contaPresenzeGiocatore("pp1", eventi, presenze, OGGI);
|
const contoPp1 = contaPresenzeGiocatore("pp1", eventi, presenze, OGGI);
|
||||||
assert.equal(contoPp1, 30, "30 risposte, incluso un ritardo contato come presenza");
|
assert.equal(
|
||||||
|
contoPp1,
|
||||||
|
oro,
|
||||||
|
"una risposta per evento, incluso un ritardo contato come presenza",
|
||||||
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
statoBadge(presenzeDef, giocatoreAzzerato(contoPp1)).grado,
|
statoBadge(presenzeDef, giocatoreAzzerato(contoPp1)).grado,
|
||||||
"oro",
|
"oro",
|
||||||
"30 presenze: soglia oro raggiunta",
|
`${oro} presenze: soglia oro raggiunta`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const contoPp2 = contaPresenzeGiocatore("pp2", eventi, presenze, OGGI);
|
const contoPp2 = contaPresenzeGiocatore("pp2", eventi, presenze, OGGI);
|
||||||
assert.equal(contoPp2, 5, "5 presenti, l'assente su a06 non conta");
|
assert.equal(contoPp2, bronzo, "solo i presenti contano, l'assente su a-extra no");
|
||||||
const badgePp2 = statoBadge(presenzeDef, giocatoreAzzerato(contoPp2));
|
const badgePp2 = statoBadge(presenzeDef, giocatoreAzzerato(contoPp2));
|
||||||
assert.equal(badgePp2.grado, "bronzo", "5 presenze: soglia bronzo raggiunta, non oltre");
|
assert.equal(
|
||||||
|
badgePp2.grado,
|
||||||
|
"bronzo",
|
||||||
|
`${bronzo} presenze: soglia bronzo raggiunta, non oltre`,
|
||||||
|
);
|
||||||
assert.equal(badgePp2.prossimo, "argento");
|
assert.equal(badgePp2.prossimo, "argento");
|
||||||
assert.equal(badgePp2.prossimaSoglia, 15);
|
assert.equal(badgePp2.prossimaSoglia, argento);
|
||||||
|
|
||||||
// Un pp2 a metà strada (15 presenze) sblocca l'argento.
|
// Un pp2 a metà strada (`argento` presenze) sblocca l'argento.
|
||||||
for (let i = 7; i <= 16; i += 1) {
|
for (let i = bronzo + 2; i <= argento + 1; i += 1) {
|
||||||
await rispondi(`${PREFISSO}-a${String(i).padStart(2, "0")}`, "pp2", "presente");
|
await rispondi(`${PREFISSO}-a${String(i).padStart(2, "0")}`, "pp2", "presente");
|
||||||
}
|
}
|
||||||
const eventiDopo = await leggiEventi();
|
const eventiDopo = await leggiEventi();
|
||||||
const presenzeDopo = await leggiPresenze();
|
const presenzeDopo = await leggiPresenze();
|
||||||
const contoPp2Dopo = contaPresenzeGiocatore("pp2", eventiDopo, presenzeDopo, OGGI);
|
const contoPp2Dopo = contaPresenzeGiocatore("pp2", eventiDopo, presenzeDopo, OGGI);
|
||||||
assert.equal(contoPp2Dopo, 15, "5 + 10 nuove presenze");
|
assert.equal(contoPp2Dopo, argento, "bronzo + nuove presenze fino all'argento");
|
||||||
assert.equal(
|
assert.equal(
|
||||||
statoBadge(presenzeDef, giocatoreAzzerato(contoPp2Dopo)).grado,
|
statoBadge(presenzeDef, giocatoreAzzerato(contoPp2Dopo)).grado,
|
||||||
"argento",
|
"argento",
|
||||||
"15 presenze: soglia argento raggiunta",
|
`${argento} presenze: soglia argento raggiunta`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -125,39 +125,40 @@ if (!locale) {
|
|||||||
await prova(
|
await prova(
|
||||||
"il badge passa da bronzo a oro con presenze consecutive vere, e un buco lo azzera",
|
"il badge passa da bronzo a oro con presenze consecutive vere, e un buco lo azzera",
|
||||||
async () => {
|
async () => {
|
||||||
// 10 allenamenti passati, tutti presenti per "sa1": bronzo (3), poi argento (6), poi
|
// Tanti allenamenti passati quanti ne chiede l'oro, tutti presenti per "sa1": le prime
|
||||||
// oro (10) man mano che si rileggono i dati.
|
// `bronzo` presenze valgono bronzo, tutte e `oro` valgono oro.
|
||||||
for (let i = 1; i <= 10; i += 1) {
|
const { bronzo, oro } = serieDef.soglie;
|
||||||
|
for (let i = 1; i <= oro; i += 1) {
|
||||||
await creaAllenamento(`${PREFISSO}-a${String(i).padStart(2, "0")}`, i);
|
await creaAllenamento(`${PREFISSO}-a${String(i).padStart(2, "0")}`, i);
|
||||||
await rispondi(`${PREFISSO}-a${String(i).padStart(2, "0")}`, "sa1", "presente");
|
await rispondi(`${PREFISSO}-a${String(i).padStart(2, "0")}`, "sa1", "presente");
|
||||||
}
|
}
|
||||||
const eventi10 = await leggiEventi();
|
const eventiOro = await leggiEventi();
|
||||||
const presenze10 = await leggiPresenze();
|
const presenzeOro = await leggiPresenze();
|
||||||
const serieA3 = serieConsecutiva(
|
const serieBronzo = serieConsecutiva(
|
||||||
"sa1",
|
"sa1",
|
||||||
eventi10.slice(0, 3),
|
eventiOro.slice(0, bronzo),
|
||||||
presenze10,
|
presenzeOro,
|
||||||
"allenamento",
|
"allenamento",
|
||||||
OGGI,
|
OGGI,
|
||||||
);
|
);
|
||||||
assert.equal(serieA3, 3);
|
assert.equal(serieBronzo, bronzo);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
statoBadge(serieDef, giocatoreAzzerato(serieA3)).grado,
|
statoBadge(serieDef, giocatoreAzzerato(serieBronzo)).grado,
|
||||||
"bronzo",
|
"bronzo",
|
||||||
"3 allenamenti di fila: soglia bronzo",
|
`${bronzo} allenamenti di fila: soglia bronzo`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const serieCompleta = serieConsecutiva("sa1", eventi10, presenze10, "allenamento", OGGI);
|
const serieCompleta = serieConsecutiva("sa1", eventiOro, presenzeOro, "allenamento", OGGI);
|
||||||
assert.equal(serieCompleta, 10, "presente a tutti e 10, nessun buco");
|
assert.equal(serieCompleta, oro, `presente a tutti e ${oro}, nessun buco`);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
statoBadge(serieDef, giocatoreAzzerato(serieCompleta)).grado,
|
statoBadge(serieDef, giocatoreAzzerato(serieCompleta)).grado,
|
||||||
"oro",
|
"oro",
|
||||||
"10 allenamenti di fila: soglia oro",
|
`${oro} allenamenti di fila: soglia oro`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Un undicesimo allenamento con un buco (assente) azzera la serie.
|
// Un allenamento in più con un buco (assente) azzera la serie.
|
||||||
await creaAllenamento(`${PREFISSO}-a11`, 11);
|
await creaAllenamento(`${PREFISSO}-a${oro + 1}`, oro + 1);
|
||||||
await rispondi(`${PREFISSO}-a11`, "sa1", "assente");
|
await rispondi(`${PREFISSO}-a${oro + 1}`, "sa1", "assente");
|
||||||
const eventiConBuco = await leggiEventi();
|
const eventiConBuco = await leggiEventi();
|
||||||
const presenzeConBuco = await leggiPresenze();
|
const presenzeConBuco = await leggiPresenze();
|
||||||
const serieDopoBuco = serieConsecutiva(
|
const serieDopoBuco = serieConsecutiva(
|
||||||
@@ -167,7 +168,7 @@ if (!locale) {
|
|||||||
"allenamento",
|
"allenamento",
|
||||||
OGGI,
|
OGGI,
|
||||||
);
|
);
|
||||||
assert.equal(serieDopoBuco, 0, "un'assenza azzera la serie, anche dopo 10 di fila");
|
assert.equal(serieDopoBuco, 0, `un'assenza azzera la serie, anche dopo ${oro} di fila`);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
statoBadge(serieDef, giocatoreAzzerato(serieDopoBuco)).grado,
|
statoBadge(serieDef, giocatoreAzzerato(serieDopoBuco)).grado,
|
||||||
null,
|
null,
|
||||||
@@ -182,7 +183,7 @@ if (!locale) {
|
|||||||
// ripartire da 1.
|
// ripartire da 1.
|
||||||
const eventi = [`${PREFISSO}-b01`, `${PREFISSO}-b02`, `${PREFISSO}-b03`, `${PREFISSO}-b04`];
|
const eventi = [`${PREFISSO}-b01`, `${PREFISSO}-b02`, `${PREFISSO}-b03`, `${PREFISSO}-b04`];
|
||||||
for (let i = 0; i < eventi.length; i += 1) {
|
for (let i = 0; i < eventi.length; i += 1) {
|
||||||
await creaAllenamento(eventi[i]!, 20 + i);
|
await creaAllenamento(eventi[i]!, serieDef.soglie.oro + 10 + i);
|
||||||
}
|
}
|
||||||
await rispondi(eventi[0]!, "sa2", "presente");
|
await rispondi(eventi[0]!, "sa2", "presente");
|
||||||
await rispondi(eventi[1]!, "sa2", "presente");
|
await rispondi(eventi[1]!, "sa2", "presente");
|
||||||
@@ -195,8 +196,8 @@ if (!locale) {
|
|||||||
assert.equal(serie, 3, "l'infortunio è saltato: 3 presenze restano consecutive");
|
assert.equal(serie, 3, "l'infortunio è saltato: 3 presenze restano consecutive");
|
||||||
assert.equal(
|
assert.equal(
|
||||||
statoBadge(serieDef, giocatoreAzzerato(serie)).grado,
|
statoBadge(serieDef, giocatoreAzzerato(serie)).grado,
|
||||||
"bronzo",
|
null,
|
||||||
"3 di fila (infortunio escluso dal conteggio): soglia bronzo",
|
"3 di fila: ancora sotto la soglia bronzo, ma la serie non si è azzerata",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
+26
-26
@@ -111,39 +111,39 @@ assert.equal(
|
|||||||
"valore azzerato dal gate: progresso azzerato anch'esso, non ingannevole",
|
"valore azzerato dal gate: progresso azzerato anch'esso, non ingannevole",
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- palloni: soglie 3 / 6 / 10 -----------------------------------------------
|
// --- palloni: soglie 3 / 5 / 8 -----------------------------------------------
|
||||||
const palloniDef = badgeDefs.find((b) => b.id === "palloni")!;
|
const palloniDef = badgeDefs.find((b) => b.id === "palloni")!;
|
||||||
assert.equal(gradoRaggiunto(palloniDef, 2), null, "sotto la prima soglia nessun grado");
|
assert.equal(gradoRaggiunto(palloniDef, 2), null, "sotto la prima soglia nessun grado");
|
||||||
assert.equal(gradoRaggiunto(palloniDef, 3), "bronzo", "la soglia è inclusiva");
|
assert.equal(gradoRaggiunto(palloniDef, 3), "bronzo", "la soglia è inclusiva");
|
||||||
assert.equal(gradoRaggiunto(palloniDef, 5), "bronzo");
|
assert.equal(gradoRaggiunto(palloniDef, 4), "bronzo");
|
||||||
assert.equal(gradoRaggiunto(palloniDef, 6), "argento");
|
assert.equal(gradoRaggiunto(palloniDef, 5), "argento");
|
||||||
assert.equal(gradoRaggiunto(palloniDef, 9), "argento");
|
assert.equal(gradoRaggiunto(palloniDef, 7), "argento");
|
||||||
assert.equal(gradoRaggiunto(palloniDef, 10), "oro");
|
assert.equal(gradoRaggiunto(palloniDef, 8), "oro");
|
||||||
assert.equal(gradoRaggiunto(palloniDef, 99), "oro", "oltre l'oro resta oro");
|
assert.equal(gradoRaggiunto(palloniDef, 99), "oro", "oltre l'oro resta oro");
|
||||||
assert.equal(
|
assert.equal(
|
||||||
statoBadge(palloniDef, g({ palloni: 5 })).progresso,
|
statoBadge(palloniDef, g({ palloni: 4 })).progresso,
|
||||||
83,
|
80,
|
||||||
"5 turni su 6 per l'argento: 83%",
|
"4 turni su 5 per l'argento: 80%",
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- presenze: soglie 5 / 15 / 30 ---------------------------------------------
|
// --- presenze: soglie 15 / 35 / 55 ---------------------------------------------
|
||||||
const presenzeDef = badgeDefs.find((b) => b.id === "presenze")!;
|
const presenzeDef = badgeDefs.find((b) => b.id === "presenze")!;
|
||||||
assert.equal(gradoRaggiunto(presenzeDef, 4), null, "sotto la prima soglia nessun grado");
|
assert.equal(gradoRaggiunto(presenzeDef, 14), null, "sotto la prima soglia nessun grado");
|
||||||
assert.equal(gradoRaggiunto(presenzeDef, 5), "bronzo", "la soglia è inclusiva");
|
assert.equal(gradoRaggiunto(presenzeDef, 15), "bronzo", "la soglia è inclusiva");
|
||||||
assert.equal(gradoRaggiunto(presenzeDef, 14), "bronzo");
|
assert.equal(gradoRaggiunto(presenzeDef, 34), "bronzo");
|
||||||
assert.equal(gradoRaggiunto(presenzeDef, 15), "argento");
|
assert.equal(gradoRaggiunto(presenzeDef, 35), "argento");
|
||||||
assert.equal(gradoRaggiunto(presenzeDef, 29), "argento");
|
assert.equal(gradoRaggiunto(presenzeDef, 54), "argento");
|
||||||
assert.equal(gradoRaggiunto(presenzeDef, 30), "oro");
|
assert.equal(gradoRaggiunto(presenzeDef, 55), "oro");
|
||||||
assert.equal(gradoRaggiunto(presenzeDef, 999), "oro", "oltre l'oro resta oro");
|
assert.equal(gradoRaggiunto(presenzeDef, 999), "oro", "oltre l'oro resta oro");
|
||||||
|
|
||||||
// --- serie-allenamenti: soglie 3 / 6 / 10 -------------------------------------
|
// --- serie-allenamenti: soglie 5 / 10 / 20 -------------------------------------
|
||||||
const serieAllenamentiDef = badgeDefs.find((b) => b.id === "serie-allenamenti")!;
|
const serieAllenamentiDef = badgeDefs.find((b) => b.id === "serie-allenamenti")!;
|
||||||
assert.equal(gradoRaggiunto(serieAllenamentiDef, 2), null, "sotto la prima soglia nessun grado");
|
assert.equal(gradoRaggiunto(serieAllenamentiDef, 4), null, "sotto la prima soglia nessun grado");
|
||||||
assert.equal(gradoRaggiunto(serieAllenamentiDef, 3), "bronzo", "la soglia è inclusiva");
|
assert.equal(gradoRaggiunto(serieAllenamentiDef, 5), "bronzo", "la soglia è inclusiva");
|
||||||
assert.equal(gradoRaggiunto(serieAllenamentiDef, 5), "bronzo");
|
assert.equal(gradoRaggiunto(serieAllenamentiDef, 9), "bronzo");
|
||||||
assert.equal(gradoRaggiunto(serieAllenamentiDef, 6), "argento");
|
assert.equal(gradoRaggiunto(serieAllenamentiDef, 10), "argento");
|
||||||
assert.equal(gradoRaggiunto(serieAllenamentiDef, 9), "argento");
|
assert.equal(gradoRaggiunto(serieAllenamentiDef, 19), "argento");
|
||||||
assert.equal(gradoRaggiunto(serieAllenamentiDef, 10), "oro");
|
assert.equal(gradoRaggiunto(serieAllenamentiDef, 20), "oro");
|
||||||
assert.equal(gradoRaggiunto(serieAllenamentiDef, 50), "oro", "oltre l'oro resta oro");
|
assert.equal(gradoRaggiunto(serieAllenamentiDef, 50), "oro", "oltre l'oro resta oro");
|
||||||
|
|
||||||
// --- badgeGiocatore ----------------------------------------------------------
|
// --- badgeGiocatore ----------------------------------------------------------
|
||||||
@@ -200,7 +200,7 @@ assert.equal(vuota.inProgresso.length, badgeDefs.length);
|
|||||||
assert.equal(vuota.nascosti, badgeSegreti.length);
|
assert.equal(vuota.nascosti, badgeSegreti.length);
|
||||||
|
|
||||||
const piena = collezioneBadge(
|
const piena = collezioneBadge(
|
||||||
g({ mvp: 5, mediaVoto: 8, votiPagella: 5, presenze: 30, infortuni: 3 }),
|
g({ mvp: 5, mediaVoto: 8, votiPagella: 5, presenze: 55, infortuni: 3 }),
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
piena.ottenuti,
|
piena.ottenuti,
|
||||||
@@ -219,9 +219,9 @@ assert.equal(
|
|||||||
mvp: 5,
|
mvp: 5,
|
||||||
mediaVoto: 10,
|
mediaVoto: 10,
|
||||||
votiPagella: 5,
|
votiPagella: 5,
|
||||||
palloni: 10,
|
palloni: 8,
|
||||||
presenze: 30,
|
presenze: 55,
|
||||||
serieAllenamenti: 10,
|
serieAllenamenti: 20,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ assert.equal(segreta.emoji, "⏰");
|
|||||||
assert.equal(segreta.testo, "Forse è il momento di puntare la sveglia 10 minuti prima! 😄");
|
assert.equal(segreta.testo, "Forse è il momento di puntare la sveglia 10 minuti prima! 😄");
|
||||||
|
|
||||||
// --- "sei a un passo": solo entro due unità dalla soglia ---------------------
|
// --- "sei a un passo": solo entro due unità dalla soglia ---------------------
|
||||||
assert.ok(ids(g({ presenze: 4 })).some((id) => id.startsWith("quasi:presenze")));
|
assert.ok(ids(g({ presenze: 13 })).some((id) => id.startsWith("quasi:presenze")));
|
||||||
assert.ok(
|
assert.ok(
|
||||||
!ids(g({ mvp: 5, presenze: 2 })).some((id) => id.startsWith("quasi:presenze")),
|
!ids(g({ mvp: 5, presenze: 12 })).some((id) => id.startsWith("quasi:presenze")),
|
||||||
"a 3 di distanza non si notifica",
|
"a 3 di distanza non si notifica",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user