🎯 Cel ćwiczenia
Gra w kości to doskonały projekt do nauki JavaScript! Nauczysz się generowania liczb losowych, manipulacji DOM, obsługi zdarzeń, przechowywania stanów gry i tworzenia logiki gier.
Czego się nauczysz:
- Generowania liczb losowych z
Math.random()
- Manipulacji elementami DOM
- Obsługi zdarzeń (kliknięcia, naciśnięcia klawiszy)
- Zarządzania stanem gry (punkty, tury, gracze)
- Tworzenia animacji i efektów wizualnych
- Walidacji reguł gry i logiki biznesowej
🎲 Część 1: Podstawy - Losowanie Kości
Generowanie liczb losowych
W JavaScript używamy Math.random() do generowania liczb losowych.
Przykład 1: Prosta kostka (1-6)
JavaScript:
function rollDice() {
const result = Math.floor(Math.random() * 6) + 1;
return result;
}
const diceValue = rollDice();
console.log("Wyrzuciłeś: " + diceValue);
Wzór ogólny: Aby losować od MIN do MAX:
Math.floor(Math.random() * (MAX - MIN + 1)) + MIN
Przykład 2: Wyświetlanie wyniku na stronie
HTML:
<div id="dice-result">?</div>
<button onclick="throwDice()">Rzuć kostką</button>
JavaScript:
function throwDice() {
const result = Math.floor(Math.random() * 6) + 1;
const diceElement = document.getElementById('dice-result');
diceElement.textContent = result;
}
🎨 Część 2: Wizualizacja Kości
Przykład 3: Kostka z kropkami (Unicode)
Możemy użyć znaków Unicode do reprezentacji kości:
function getDiceEmoji(value) {
const diceEmojis = {
1: '⚀',
2: '⚁',
3: '⚂',
4: '⚃',
5: '⚄',
6: '⚅'
};
return diceEmojis[value];
}
function throwDice() {
const result = Math.floor(Math.random() * 6) + 1;
const emoji = getDiceEmoji(result);
document.getElementById('dice-result').textContent = emoji;
}
Przykład 4: Kostka HTML/CSS z kropkami
HTML:
<div class="dice" id="dice">
<div class="dot dot1"></div>
<div class="dot dot2"></div>
<div class="dot dot3"></div>
<div class="dot dot4"></div>
<div class="dot dot5"></div>
<div class="dot dot6"></div>
</div>
CSS:
.dice {
width: 100px;
height: 100px;
background: white;
border: 3px solid #333;
border-radius: 15px;
position: relative;
}
.dot {
width: 15px;
height: 15px;
background: #333;
border-radius: 50%;
position: absolute;
display: none;
}
.dot1 { top: 20px; left: 20px; }
.dot2 { top: 20px; right: 20px; }
.dot3 { top: 42.5px; left: 42.5px; }
.dot4 { bottom: 20px; left: 20px; }
.dot5 { bottom: 20px; right: 20px; }
.dot6 { top: 42.5px; right: 42.5px; }
.dice.show-1 .dot3 { display: block; }
.dice.show-2 .dot1, .dice.show-2 .dot5 { display: block; }
.dice.show-3 .dot1, .dice.show-3 .dot3, .dice.show-3 .dot5 { display: block; }
.dice.show-4 .dot1, .dice.show-4 .dot2,
.dice.show-4 .dot4, .dice.show-4 .dot5 { display: block; }
.dice.show-5 .dot1, .dice.show-5 .dot2, .dice.show-5 .dot3,
.dice.show-5 .dot4, .dice.show-5 .dot5 { display: block; }
.dice.show-6 .dot1, .dice.show-6 .dot2, .dice.show-6 .dot6,
.dice.show-6 .dot4, .dice.show-6 .dot5 { display: block; }
JavaScript:
function showDiceValue(value) {
const dice = document.getElementById('dice');
dice.className = 'dice';
dice.classList.add('show-' + value);
}
function throwDice() {
const result = Math.floor(Math.random() * 6) + 1;
showDiceValue(result);
}
🎮 Część 3: Prosta Gra - "Wyścig do 50"
📋 Zasady gry:
- Dwóch graczy na zmianę rzuca kostką
- Wyrzucony wynik dodaje się do punktów gracza
- Ale! Jeśli wyrzucisz 1, tracisz wszystkie punkty z tej tury
- Gracz może zdecydować, czy rzucić jeszcze raz, czy zatrzymać się
- Pierwszy gracz, który osiągnie 50 punktów, wygrywa!
Przykład 5: Kompletna gra "Wyścig do 50"
HTML:
<div class="game-container">
<h2>Wyścig do 50 punktów</h2>
<div class="players">
<div class="player active" id="player1">
<h3>Gracz 1</h3>
<p>Punkty ogółem: <span id="score1">0</span></p>
<p>Punkty w turze: <span id="current1">0</span></p>
</div>
<div class="player" id="player2">
<h3>Gracz 2</h3>
<p>Punkty ogółem: <span id="score2">0</span></p>
<p>Punkty w turze: <span id="current2">0</span></p>
</div>
</div>
<div class="dice-display">
<div id="dice">?</div>
</div>
<div class="controls">
<button id="roll-btn">Rzuć kostką</button>
<button id="hold-btn">Zatrzymaj</button>
<button id="new-btn">Nowa gra</button>
</div>
</div>
JavaScript:
let scores = [0, 0];
let currentScore = 0;
let activePlayer = 0;
let playing = true;
const player1El = document.getElementById('player1');
const player2El = document.getElementById('player2');
const score1El = document.getElementById('score1');
const score2El = document.getElementById('score2');
const current1El = document.getElementById('current1');
const current2El = document.getElementById('current2');
const diceEl = document.getElementById('dice');
const rollBtn = document.getElementById('roll-btn');
const holdBtn = document.getElementById('hold-btn');
const newBtn = document.getElementById('new-btn');
rollBtn.addEventListener('click', function() {
if (!playing) return;
const dice = Math.floor(Math.random() * 6) + 1;
diceEl.textContent = dice;
if (dice !== 1) {
currentScore += dice;
document.getElementById(`current${activePlayer + 1}`).textContent = currentScore;
} else {
switchPlayer();
}
});
holdBtn.addEventListener('click', function() {
if (!playing) return;
scores[activePlayer] += currentScore;
document.getElementById(`score${activePlayer + 1}`).textContent = scores[activePlayer];
if (scores[activePlayer] >= 50) {
playing = false;
alert(`Gracz ${activePlayer + 1} wygrał!`);
} else {
switchPlayer();
}
});
function switchPlayer() {
currentScore = 0;
document.getElementById(`current${activePlayer + 1}`).textContent = 0;
activePlayer = activePlayer === 0 ? 1 : 0;
player1El.classList.toggle('active');
player2El.classList.toggle('active');
}
newBtn.addEventListener('click', function() {
scores = [0, 0];
currentScore = 0;
activePlayer = 0;
playing = true;
score1El.textContent = 0;
score2El.textContent = 0;
current1El.textContent = 0;
current2El.textContent = 0;
diceEl.textContent = '?';
player1El.classList.add('active');
player2El.classList.remove('active');
});
✨ Część 4: Animacje i Efekty
Przykład 6: Animacja rzutu kostką
CSS:
@keyframes shake {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-15deg); }
75% { transform: rotate(15deg); }
}
.dice.rolling {
animation: shake 0.5s;
}
JavaScript:
function throwDiceWithAnimation() {
const dice = document.getElementById('dice');
dice.classList.add('rolling');
let counter = 0;
const interval = setInterval(() => {
const tempValue = Math.floor(Math.random() * 6) + 1;
dice.textContent = tempValue;
counter++;
if (counter > 10) {
clearInterval(interval);
const finalValue = Math.floor(Math.random() * 6) + 1;
dice.textContent = finalValue;
dice.classList.remove('rolling');
}
}, 50);
}
✏️ ZADANIA PRAKTYCZNE
Cel: Stwórz prostą stronę z kostką, którą można rzucić.
Wymagania:
- Przycisk "Rzuć kostką"
- Wyświetlanie wyniku (1-6) w dużym elemencie
- Licznik rzutów (ile razy rzucono kostką)
- Przycisk "Reset" do zerowania licznika
- Stylowanie CSS (kolorowa kostka)
Wskazówka: Użyj zmiennej globalnej do przechowywania liczby rzutów.
Cel: Rozszerz zadanie 1 o statystyki.
Wymagania:
- Zliczaj ile razy wypadła każda liczba (1-6)
- Wyświetlaj statystyki w tabeli lub listach
- Pokaż procentowy udział każdej liczby
- Znajdź i wyświetl najczęściej wyrzucaną liczbę
- Przycisk do czyszczenia statystyk
Wskazówka: Użyj tablicy lub obiektu do przechowywania liczby wystąpień każdej wartości.
Cel: Komputer losuje liczbę 1-6, gracz musi zgadnąć przed rzutem.
Wymagania:
- 6 przycisków do wyboru liczby (1-6)
- Po wybraniu, kostka rzuca automatycznie
- Komunikat "Trafione!" lub "Pudło!"
- Licznik trafień i pudłów
- Pokaż serię trafień (streak)
- Dodaj dźwięk przy trafieniu (opcjonalnie)
Wskazówka: Użyj new Audio('sound.mp3').play() dla dźwięku.
Cel: Zaawansowana wersja gry "Wyścig do 50".
Wymagania:
- Cel: 100 punktów (zamiast 50)
- Rzut 1 = strata wszystkich punktów w turze
- Rzut podwójnej szóstki (dwa razy 6 pod rząd) = bonus +10 punktów
- Przycisk "Rzuć 2 kostkami" - rzut dwiema kostkami jednocześnie
- Historia ostatnich 5 rzutów dla każdego gracza
- Zapisz wynik najlepszej gry (localStorage)
- Animacja przy zmianie gracza
Wskazówka: Użyj localStorage.setItem() i localStorage.getItem().
Cel: Uproszczona wersja gry Generał z 5 kostkami.
Wymagania:
- 5 kostek - wszystkie rzucają się jednocześnie
- Możliwość "trzymania" wybranych kostek (nie rzucają się ponownie)
- 3 rzuty na turę
- Punktacja:
- Para (dwie takie same) = suma tych kostek
- Dwie pary = suma wszystkich
- Trójka = suma wszystkich
- Kareta = suma wszystkich
- Full (para + trójka) = 25 punktów
- Mały strit (4 kolejne) = 30 punktów
- Duży strit (5 kolejnych) = 40 punktów
- Generał (5 takich samych) = 50 punktów
- Tabela wyników z możliwością wyboru kategorii
- Suma końcowa po wypełnieniu wszystkich kategorii
Wskazówka: To zaawansowane zadanie - podziel na mniejsze części!
Cel: Gra przeciwko komputerowi z prostą sztuczną inteligencją.
Wymagania:
- Tryb gry: Gracz vs Komputer
- AI podejmuje decyzje:
- Rzuca kostką, jeśli punkty w turze < 20
- Zatrzymuje się, jeśli punkty w turze ≥ 20
- Gra agresywniej, jeśli jest w tyle
- Opóźnienia dla ruchów AI (żeby było realistyczne)
- Animacja "myślenia" AI
- 3 poziomy trudności: Łatwy, Średni, Trudny
- Statystyki zwycięstw gracza vs AI
- Możliwość włączenia trybu dla 2 graczy
Wskazówka: Użyj setTimeout() dla opóźnień ruchów AI.
💡 WSKAZÓWKI I NAJLEPSZE PRAKTYKI
1. Organizacja kodu
Dla bardziej złożonych gier, użyj obiektów do organizacji kodu:
const game = {
players: [
{ name: 'Gracz 1', score: 0, currentScore: 0 },
{ name: 'Gracz 2', score: 0, currentScore: 0 }
],
activePlayer: 0,
playing: true,
rollDice() {
return Math.floor(Math.random() * 6) + 1;
},
switchPlayer() {
this.activePlayer = this.activePlayer === 0 ? 1 : 0;
},
reset() {
this.players.forEach(player => {
player.score = 0;
player.currentScore = 0;
});
this.activePlayer = 0;
this.playing = true;
}
};
2. Debugowanie gier losowych
Podczas testowania, możesz tymczasowo zastąpić losowanie stałymi wartościami:
const DEBUG = true;
function rollDice() {
if (DEBUG) {
return 6;
}
return Math.floor(Math.random() * 6) + 1;
}
3. Walidacja stanów gry
Zawsze sprawdzaj, czy gra jest w trakcie przed wykonaniem akcji:
rollBtn.addEventListener('click', function() {
if (!playing) {
alert('Gra się skończyła! Kliknij "Nowa gra"');
return;
}
});
4. LocalStorage dla zapisywania gier
function saveGame() {
const gameState = {
scores: scores,
activePlayer: activePlayer,
currentScore: currentScore
};
localStorage.setItem('diceGame', JSON.stringify(gameState));
}
function loadGame() {
const saved = localStorage.getItem('diceGame');
if (saved) {
const gameState = JSON.parse(saved);
scores = gameState.scores;
activePlayer = gameState.activePlayer;
currentScore = gameState.currentScore;
}
}
Częste błędy do uniknięcia:
- ❌ Zapominanie o użyciu
Math.floor() - bez tego dostaniesz liczby zmiennoprzecinkowe
- ❌ Nieprawidłowy zakres:
Math.random() * 6 daje 0-5, musisz dodać +1
- ❌ Brak walidacji stanu gry - przyciski działają nawet po zakończeniu
- ❌ Niezerowanie zmiennych przy nowej grze
- ❌ Brak zabezpieczenia przed szybkim klikaniem podczas animacji
🎨 Część 5: Ulepszenia wizualne
Style CSS dla lepszego wyglądu gry
.dice-3d {
width: 100px;
height: 100px;
background: linear-gradient(145deg, #fff, #e6e6e6);
border-radius: 20px;
box-shadow:
0 10px 30px rgba(0,0,0,0.3),
inset 0 -2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
.dice-3d:hover {
transform: scale(1.05);
}
.player {
padding: 30px;
border-radius: 15px;
transition: all 0.3s;
opacity: 0.5;
}
.player.active {
opacity: 1;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.5);
}
@keyframes winner {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.winner {
animation: winner 0.5s ease-in-out 3;
background: gold !important;
}
.btn {
padding: 15px 30px;
font-size: 18px;
border: none;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 7px 20px rgba(0,0,0,0.3);
}
.btn:active {
transform: translateY(0);
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-success {
background: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%);
color: white;
}
🎮 Przykładowe rozszerzenia gier
Pomysły na dodatkowe funkcje:
- Tryb turnieju: Najlepszy z 3/5 gier
- Tabela wyników: Top 10 najlepszych wyników
- Efekty dźwiękowe: Dźwięki rzutu, wygranej, przegranej
- Motywy: Jasny/ciemny tryb, różne kolory kostek
- Osiągnięcia: Odznaki za specjalne wyczyny
- Multiplayer online: Gra przez internet (zaawansowane)
- Różne warianty kostek: 4, 8, 10, 12, 20 ścian
- Power-upy: Specjalne karty zmieniające zasady
- Tryb story: Kampania z misjami
🔧 Przykład kodu pomocniczego
Funkcje użytkowe (utility functions)
function rollMultipleDice(count) {
const results = [];
for (let i = 0; i < count; i++) {
results.push(Math.floor(Math.random() * 6) + 1);
}
return results;
}
function sumDice(diceArray) {
return diceArray.reduce((sum, dice) => sum + dice, 0);
}
function hasPair(diceArray) {
const counts = {};
diceArray.forEach(dice => {
counts[dice] = (counts[dice] || 0) + 1;
});
return Object.values(counts).some(count => count >= 2);
}
function isStraight(diceArray) {
const sorted = [...diceArray].sort((a, b) => a - b);
for (let i = 0; i < sorted.length - 1; i++) {
if (sorted[i + 1] - sorted[i] !== 1) {
return false;
}
}
return true;
}
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
function playSound(soundFile) {
const audio = new Audio(soundFile);
audio.play().catch(err => console.log('Nie można odtworzyć dźwięku'));
}
function saveHighScore(score) {
const highScore = localStorage.getItem('highScore') || 0;
if (score > highScore) {
localStorage.setItem('highScore', score);
return true;
}
return false;
}
🎓 Test Wiedzy
Pytania kontrolne - sprawdź swoją wiedzę!
- Jaki zakres zwraca
Math.random()? (Odp: 0 do 1, wyłącznie 1)
- Jak uzyskać losową liczbę od 1 do 6? (Odp: Math.floor(Math.random() * 6) + 1)
- Co robi
Math.floor()? (Odp: Zaokrągla w dół do najbliższej liczby całkowitej)
- Jak zmienić zawartość elementu HTML? (Odp: element.textContent lub element.innerHTML)
- Jak dodać nasłuchiwacz zdarzenia kliknięcia? (Odp: element.addEventListener('click', function))
- Jak zapisać dane w localStorage? (Odp: localStorage.setItem('key', 'value'))
- Czym różni się textContent od innerHTML? (Odp: textContent to tylko tekst, innerHTML może zawierać HTML)
- Co to jest callback function? (Odp: Funkcja przekazana jako argument do innej funkcji)
🎉 PODSUMOWANIE
Gratulacje! Po ukończeniu tego ćwiczenia powinieneś umieć:
- ✅ Generować liczby losowe w JavaScript
- ✅ Manipulować elementami DOM
- ✅ Obsługiwać zdarzenia (kliknięcia, naciśnięcia klawiszy)
- ✅ Zarządzać stanem gry
- ✅ Tworzyć logikę gier
- ✅ Dodawać animacje i efekty wizualne
- ✅ Przechowywać dane w localStorage
- ✅ Debugować kod JavaScript
Następny krok: Spróbuj stworzyć własną grę! Połącz wiedzę z tego ćwiczenia z kreatywnością i stwórz coś unikalnego.
Powodzenia w tworzeniu gier! 🎮🚀
💼 Dlaczego warto umieć tworzyć gry?
- Gry uczą logicznego myślenia i rozwiązywania problemów
- Są świetnym sposobem na praktykę programowania
- Pokazują umiejętności w portfolio
- Rozwijają kreatywność i wyobraźnię
- Uczą zarządzania stanem i strukturami danych
- Są fajne i motywujące do nauki! 😊
🏆 Wyzwanie finalne
Stwórz swoją własną unikalną grę w kości!
Pomysły na inspirację:
- Gra RPG z walkami na kostki
- Symulator kasyna z różnymi grami
- Gra planszowa online
- Gra edukacyjna do nauki matematyki
- Gra przygodowa z elementami losowymi
Wykorzystaj wszystko, czego się nauczyłeś i dodaj własne pomysły. Powodzenia! 🌟
przykład działającego rozwiązania