Passa al contenuto principale

Dice Game

Interfaccia principale del gioco dei dadi che mostra le regole di gioco e 5 dadi Interfaccia di gioco che mostra una scala minore con un punteggio di 30 punti Pannello della cronologia dei punteggi che mostra un punteggio totale di 30, ottenuto da una scala minore del valore di 30 punti Schermata di fine partita che mostra il punteggio finale

Il Progetto

Dice Game sviluppato con JavaScript puro, focalizzato su logica algoritmica, gestione dello stato e manipolazione del DOM. Un progetto che richiede di mettere insieme molteplici concetti precedentemente appresi in un contesto con poca guida strutturata.

Codice Sorgente

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Advanced Dice Game</title>
<link rel="stylesheet" href="styles.css" />
</head>

<body>
<header>
<h1>Advanced Dice Game</h1>
<button class="btn" id="rules-btn" type="button">Show rules</button>
<div class="rules-container">
<h2>Rules</h2>
<ul>
<li>There are total of six rounds</li>
<li>You can only roll the dice three times per round</li>
<li>To start the game, roll the dice</li>
<li>
Then, choose from one of the selected scores or roll the dice again
</li>
<li>
If you choose a selected score, then you will move to the next round
</li>
<li>
If you decline to choose a selected score, then you can roll the
dice again two more times
</li>
</ul>
<h2 class="points">Points</h2>
<ul>
<li>Three of a kind: Sum of all five dice</li>
<li>Four of a kind: Sum of all five dice</li>
<li>Full house: Three of a kind and a pair - 25 points</li>
<li>
Small straight: Four of the dice have consecutive values - 30 points
</li>
<li>
Large straight: All five dice have consecutive values - 40 points
</li>
</ul>
</div>
</header>

<main>
<form id="game">
<div id="dice">
<div class="die"></div>
<div class="die"></div>
<div class="die"></div>
<div class="die"></div>
<div class="die"></div>
</div>

<p class="rounds-text">
<strong>Rolls:</strong> <span id="current-round-rolls">0</span> |
<strong>Round:</strong> <span id="current-round">1</span>
</p>

<div id="score-options">
<div>
<input type="radio" name="score-options" id="three-of-a-kind" value="three-of-a-kind" disabled />
<label for="three-of-a-kind">Three of a kind<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="four-of-a-kind" value="four-of-a-kind" disabled />
<label for="four-of-a-kind">Four of a kind<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="full-house" value="full-house" disabled />
<label for="full-house">Full house<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="small-straight" value="small-straight" disabled />
<label for="small-straight">Small straight<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="large-straight" value="large-straight" disabled />
<label for="large-straight">Large straight<span></span></label>
</div>

<div>
<input type="radio" name="score-options" id="none" value="none" disabled />
<label for="none">None of the above<span></span></label>
</div>
</div>

<button class="btn" id="keep-score-btn" type="button">
Keep the above selected score
</button>
<button class="btn" id="roll-dice-btn" type="button">
Roll the dice
</button>
</form>

<div id="scores">
<h3>Score history (Total score: <span id="total-score">0</span>)</h3>
<ol id="score-history"></ol>
</div>
</main>
<script src="./script.js"></script>
</body>

</html>

Il Progetto Più Difficile

È stato il progetto più difficile in assoluto. Erano pochi step ma molto complessi. Ora capisco perché sia il corso di freeCodeCamp con il più alto tasso di abbandono.
Non mi è piaciuto in sé per una questione di argomento: non era un gioco che conoscevo e non mi ha emozionato giocarci prima di iniziare il progetto per capire la logica, e nemmeno dopo averla implementata. Fatto sta che ho imparato tanto. In progetti come questo, che lasciano "carta bianca", proprio come nei certification project, la difficoltà principale sta nel mettere insieme i pezzi e quindi nel riassumere tutto ciò che si è imparato prima.
Mi è mancato il non avere un unico vademecum completo ma sono ugualmente riuscito a reperire le informazioni dato che è come se ne avessi due, aspettavo di finire il corso per unirli ma è possibile che lo faccia prima, perché è un passaggio che mi ha sempre aiutato a riordinare i concetti nella mente.

La Riflessione: Strategia di Apprendimento

Sono indeciso tra fare il corso 6 di Google UX o iniziare subito il certification project, ma leggendo quanto ho scritto, la cosa più logica che emerge è: unire il vademecum, fare il corso Google UX per staccare dalla full immersion con freeCodeCamp, poi iniziare il 4° certification project con massima carica.
Riflettendoci, sarebbe intelligente anche per capire se il vademecum necessita di modifiche, dopotutto, la maggior parte di queste le ho fatte proprio mentre lo usavo per i certification project, dove diventava essenziale. Quando ho dubbi, consultare il vademecum è il mio primo step, il secondo MDN Web Docs, anche se ultimamente meno, il terzo, come ultima spiaggia, è il code tutor.
L'AI per approfondire i concetti è eccezionale, sebbene tu stia un po' giocando a testa o croce nella speranza che sia giusto ciò che dice. È ottima anche per suggerire alternative, ma è uno strumento che impigrisce parecchio. Una bici con le rotelle che se non impari a togliere te le tieni a vita.
Per mitigare il rischio allucinazione, da quasi 2 mesi ho sostituito Claude con Perplexity, impostando sempre Claude o Gemini come modello, entrambi con chain of thought attiva, spazio tra un modello all'altro in base al temperamento desiderato. Così mi assicuro le prestazioni dei migliori modelli accompagnate da fonti verificabili. A volte lo faccio attivamente, anche se dovrei farlo più spesso, e scopro siti veramente interessanti.

I Tre Concetti Critici

Mi sono appuntato alcuni concetti basati su confronti tra l'approccio di freeCodeCamp e alternative migliori. Niente era sbagliato, d'altronde chi sono io per dirlo? Ma approfondire le alternative mi ha portato a ragionare con pensiero critico.

1. style.display vs classList.toggle() - Il SRP

FreeCodeCamp ha proposto:

if (isModalShowing) {
rulesBtn.textContent = "Hide rules";
rulesContainer.style.display = "block";
} else {
rulesBtn.textContent = "Show rules";
rulesContainer.style.display = "none";
}

Questa soluzione modifica direttamente gli stili inline dell'elemento HTML. Seguendo il Single Responsibility Principle ha molto più senso separare il CSS dal JavaScript:

.rules-container {
display: none;
}

.rules-container.show {
display: block;
}

E nel JavaScript:

rulesContainer.classList.toggle("show");
rulesBtn.textContent = isModalShowing ? "Hide rules" : "Show rules";

Così facendo non abbiamo pezzi di CSS nel JavaScript, bensì due documenti ben distinti: il JavaScript si occupa solo di richiamare lo stile nel CSS, non di crearlo.

2. parseInt() vs Number() - Rigore Semantico

L'approccio di freeCodeCamp è stato score += parseInt(selectedValue);

Io avevo inserito score += Number(selectedValue);

Sono corrette entrambe, ma Number() sembra essere più rigoroso. Ecco perché:

AspettoparseInt()Number()
"15"15 ✅15 ✅
"15.7"15 (tronca i decimali)15.7 (mantiene decimali)
"15px"15 (ignora resto)NaN (errore)
""NaN0

Number() è più rigoroso perché fallisce quando incontra input invalidi, mentre parseInt() fa conversioni "ottimiste". A seconda del contesto, la rigorosità di Number() può essere preferibile per catturare errori di input.

3. innerHTML += vs createElement() - Performance e DOM Integrity

Questo punto è emerso dal code tutor che ha giustamente fatto notare che:

scoreHistory.innerHTML += `<li>${achieved} : ${selectedValue}</li>`;

È breve e leggibile, ma la soluzione migliore, seppur più prolissa, è:

const historyItem = document.createElement("li");
historyItem.textContent = `${achieved} : ${selectedValue}`;
scoreHistory.appendChild(historyItem);

Il perché è veramente interessante: con innerHTML +=, avendo già presenti gli elementi:

<li>Three of a kind : 15</li>
<li>Four of a kind : 20</li>
<li>Full house : 25</li>

Aggiungendo il 4° elemento con innerHTML +=, il browser distrugge i primi 3 e ricrea tutti e 4 da zero! Con appendChild() viene semplicemente aggiunto un nuovo elemento, lasciando intatti gli altri.
Questo accade perché in scoreHistory, trovandosi gli altri 3 elementi, il browser andrà a leggere tutto l'HTML esistente (riferito solo a scoreHistory in questo caso), convertire in stringa, aggiungere quella nuova e distruggere quindi gli altri elementi del DOM, rischiando che cambi gli stati da com'erano prima.
innerHTML rimane ottimo quando si vuole creare da zero il contenuto di un elemento (con assegnazione, non con aggiunta), ma per append iterativi createElement() + appendChild() è decisamente superiore.

Cosa Ho Imparato

Algoritmi di Pattern Matching:

  • Rilevamento Three/Four of a Kind contando occorrenze di numeri
  • Rilevamento Full House verificando la presenza di gruppi di 3 e 2
  • Rilevamento Straight convertendo array in stringhe per pattern matching
  • Uso di Set per rimuovere duplicati: [...new Set(arr)]

Array Methods e Iterazione:

  • .forEach() per iterare e modificare elementi DOM
  • .reduce() per accumulare valori (somma dei dadi)
  • .sort() con callback numerica per ordinare array
  • .includes() per cercare patterns in stringhe
  • .some() per verificare se almeno uno elemento soddisfa una condizione

Manipolazione Oggetti:

  • Conteggio occorrenze di elementi creando oggetti { numero: count }
  • Object.values() per estrarre solo i valori da un oggetto
  • Operatore ternario inline: counts[num] = counts[num] ? counts[num] + 1 : 1

Event Handling e Validazione:

  • Guard clauses per controllare precondizioni prima di eseguire codice
  • Validazione input prima di processare selezioni utente
  • addEventListener() per gestire interazioni
  • setTimeout() per ritardare esecuzione (permette UI update prima di alert)

Pattern di Codice Professionali:

  • DRY (Don't Repeat Yourself): refactoring della chiamata updateRadioOption(5, 0) in un'unica posizione
  • Separazione delle responsabilità: funzioni specifiche per ogni tipo di rilevamento
  • Early exit: uso di break per uscire da loop quando trovato ciò che cerchi
  • Nomi semantici che spiegano il "cosa" non il "come"

Differenze Critiche tra Metodi:

  • textContent vs innerHTML: textContent è più sicuro, innerHTML ricrea DOM
  • Assegnazione vs concatenazione: = vs += hanno implicazioni di performance diverse
  • style.display vs classList.toggle(): separazione CSS/JS vs inline styles

Prossimo Progetto: Costruire un Cash Register (CERTIFICATION PROJECT!)