Skip to content

Commit 665a8c6

Browse files
committed
Ludo implementation
1 parent 945281e commit 665a8c6

File tree

10 files changed

+1075
-0
lines changed

10 files changed

+1075
-0
lines changed

Ludo-Champs/README.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<center>
2+
<h1 style="font-size: 36px; font-family: 'Roboto', sans-serif; font-weight: bold;"> 🎲 Ludo Game </h1>
3+
</center>
4+
5+
- This project is a web-based **Ludo game** where players can compete against each other in a classic Ludo board game experience.
6+
7+
### ***Features***
8+
- "🎮 **Multiplayer Support**: Play with 4 players in an interactive game of Ludo."
9+
- "🎲 **Dice Rolling**: Roll the dice to move tokens across the board."
10+
- "🏠 **Player Tokens**: Each player controls four tokens that must race to the finish."
11+
- "📱 **Responsive Design**: The game adjusts to different screen sizes, ensuring a smooth experience on any device."
12+
13+
### ⌨️ ***How to Play***
14+
1. **Roll the Dice**: Click the "Roll" button or press the "Enter" key to roll the dice.
15+
2. **Move Tokens**: Move your token by the number rolled on the dice.
16+
3. **Race to the Finish**: Be the first player to get all your tokens to the "Home" area.
17+
4. **Activate Turns**: Players take turns, and each token is moved based on the dice rolls.
18+
19+
### 🔧 **Technologies Used**
20+
- **HTML**: The structure of the game board and user interface.
21+
- **CSS**: Styling and layout for the board, tokens, and game buttons.
22+
- **JavaScript**: Handles the game logic, dice rolls, token movements, and player turns.
23+
24+
### ⚙️ **How It Works**
25+
1. **Rolling the Dice**: When you click the "Roll" button or press the "Enter" key, the dice rolls and the result is displayed.
26+
2. **Moving Tokens**: Players move their tokens based on the dice roll. Each player has four tokens that they must move across the board.
27+
3. **Turn-Based Gameplay**: Players take turns rolling the dice and moving their tokens. The first player to get all their tokens to the "Home" area wins.
28+
29+
### 📂 ***File Structure***
30+
- `index.html`: The main HTML file that structures the Ludo game, including the board and player tokens.
31+
- `style.css`: Contains all the styles for the game, including the layout, colors, and animations.
32+
- `main.js`: The JavaScript file that handles the game logic, such as rolling dice, moving tokens, and player turns.
33+
- `script.js`: Additional JavaScript for user interface interactions and game controls.
34+
35+

Ludo-Champs/index.html

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Ludo Game</title>
8+
<link rel="icon" type="image/jpg" href="./ludo/dice.png"/>
9+
<link rel="stylesheet" href="./ludo/style.css">
10+
</head>
11+
<body>
12+
<div class="ludo-container">
13+
<div class="ludo">
14+
<div class="player-pieces">
15+
<div class="player-piece" player-id="P1" piece="0"></div>
16+
<div class="player-piece" player-id="P1" piece="1"></div>
17+
<div class="player-piece" player-id="P1" piece="2"></div>
18+
<div class="player-piece" player-id="P1" piece="3"></div>
19+
20+
<div class="player-piece" player-id="P2" piece="0"></div>
21+
<div class="player-piece" player-id="P2" piece="1"></div>
22+
<div class="player-piece" player-id="P2" piece="2"></div>
23+
<div class="player-piece" player-id="P2" piece="3"></div>
24+
25+
<div class="player-piece" player-id="P3" piece="0"></div>
26+
<div class="player-piece" player-id="P3" piece="1"></div>
27+
<div class="player-piece" player-id="P3" piece="2"></div>
28+
<div class="player-piece" player-id="P3" piece="3"></div>
29+
30+
<div class="player-piece" player-id="P4" piece="0"></div>
31+
<div class="player-piece" player-id="P4" piece="1"></div>
32+
<div class="player-piece" player-id="P4" piece="2"></div>
33+
<div class="player-piece" player-id="P4" piece="3"></div>
34+
</div>
35+
36+
<div class="player-bases">
37+
<div class="player-base" player-id="P1"></div>
38+
<div class="player-base" player-id="P2"></div>
39+
<div class="player-base" player-id="P3"></div>
40+
<div class="player-base" player-id="P4"></div>
41+
</div>
42+
</div>
43+
<div class="footer">
44+
<div class="row">
45+
<button id="dice-btn" class="btn btn-dice">Roll</button>
46+
<div class="dice-value"></div>
47+
<button id="reset-btn" class="btn btn-reset">Reset</button>
48+
</div>
49+
<h2 class="active-player">Active Player: <span></span></h2>
50+
</div>
51+
</div>
52+
53+
54+
55+
56+
<div class="containeer">
57+
<div class="dice">
58+
<div class="face front"></div>
59+
<div class="face back"></div>
60+
<div class="face top"></div>
61+
<div class="face bottom"></div>
62+
<div class="face right"></div>
63+
<div class="face left"></div>
64+
</div>
65+
66+
<button class="roll">
67+
Roll Dice
68+
</button>
69+
70+
<div class="result">Result: 0</div> <!-- Result display area -->
71+
</div>
72+
73+
<!-- JS File -->
74+
<script src="script.js" type="text/javascript"></script>
75+
76+
<script src="./main.js" type="module"></script>
77+
</body>
78+
</html>
79+

Ludo-Champs/ludo/Ludo.js

+281
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
import { BASE_POSITIONS, HOME_ENTRANCE, HOME_POSITIONS, PLAYERS, SAFE_POSITIONS, START_POSITIONS, STATE, TURNING_POINTS } from './constants.js';
2+
import { UI } from './UI.js';
3+
4+
// Define the custom turn order
5+
const TURN_ORDER = [0, 2, 1, 3];
6+
7+
// Dice roll elements and function
8+
const dice = document.querySelector('.dice');
9+
const rollBtn = document.querySelector('.roll');
10+
const resultDisplay = document.querySelector('.result');
11+
12+
const randomDice = () => {
13+
const random = Math.floor(Math.random() * 6) + 1; // Generates a number between 1 and 6
14+
rollDice(random);
15+
return random;
16+
}
17+
18+
const rollDice = (random) => {
19+
dice.style.animation = 'rolling 1s'; // Set a faster animation duration
20+
21+
setTimeout(() => {
22+
// Show the dice face based on the random number
23+
switch (random) {
24+
case 1:
25+
dice.style.transform = 'rotateX(0deg) rotateY(0deg)';
26+
break;
27+
case 2:
28+
dice.style.transform = 'rotateX(-90deg) rotateY(0deg)';
29+
break;
30+
case 3:
31+
dice.style.transform = 'rotateX(0deg) rotateY(90deg)';
32+
break;
33+
case 4:
34+
dice.style.transform = 'rotateX(0deg) rotateY(-90deg)';
35+
break;
36+
case 5:
37+
dice.style.transform = 'rotateX(90deg) rotateY(0deg)';
38+
break;
39+
case 6:
40+
dice.style.transform = 'rotateX(180deg) rotateY(0deg)';
41+
break;
42+
default:
43+
break;
44+
}
45+
46+
dice.style.animation = 'none'; // Stop the animation after it completes
47+
48+
// Display the result
49+
resultDisplay.textContent = `Result: ${random}`;
50+
}, 1050); // Slightly more than the animation duration
51+
}
52+
53+
export class Ludo {
54+
currentPositions = {
55+
P1: [],
56+
P2: [],
57+
P3: [],
58+
P4: []
59+
};
60+
61+
_diceValue;
62+
get diceValue() {
63+
return this._diceValue;
64+
}
65+
set diceValue(value) {
66+
this._diceValue = value;
67+
UI.setDiceValue(value);
68+
}
69+
70+
_turn;
71+
get turn() {
72+
return this._turn;
73+
}
74+
set turn(value) {
75+
this._turn = value;
76+
UI.setTurn(value);
77+
}
78+
79+
_state;
80+
get state() {
81+
return this._state;
82+
}
83+
set state(value) {
84+
this._state = value;
85+
86+
if (value === STATE.DICE_NOT_ROLLED) {
87+
UI.enableDice();
88+
UI.unhighlightPieces();
89+
} else {
90+
UI.disableDice();
91+
}
92+
}
93+
94+
constructor() {
95+
console.log('Hello World! Lets play Ludo!');
96+
97+
// Initialize the turn index to -1, so the first call to incrementTurn sets it to the first player
98+
this.turnIndex = -1;
99+
this.incrementTurn(); // Start the game with the first player
100+
this.listenRollButtonClick();
101+
this.listenResetClick();
102+
this.listenPieceClick();
103+
104+
this.resetGame();
105+
}
106+
107+
listenRollButtonClick() {
108+
rollBtn.addEventListener('click', this.onDiceClick.bind(this));
109+
}
110+
111+
onDiceClick() {
112+
console.log('dice clicked!');
113+
this.diceValue = randomDice(); // Animate the dice and set the value
114+
this.state = STATE.DICE_ROLLED;
115+
this.checkForEligiblePieces();
116+
}
117+
118+
checkForEligiblePieces() {
119+
const player = PLAYERS[this.turn];
120+
const eligiblePieces = this.getEligiblePieces(player);
121+
if (eligiblePieces.length) {
122+
// Highlight the pieces
123+
UI.highlightPieces(player, eligiblePieces);
124+
} else {
125+
this.incrementTurn();
126+
}
127+
}
128+
129+
incrementTurn() {
130+
// Increment the turn index and follow the custom turn order
131+
this.turnIndex = (this.turnIndex + 1) % TURN_ORDER.length;
132+
this.turn = TURN_ORDER[this.turnIndex];
133+
this.state = STATE.DICE_NOT_ROLLED;
134+
}
135+
136+
getEligiblePieces(player) {
137+
return [0, 1, 2, 3].filter(piece => {
138+
const currentPosition = this.currentPositions[player][piece];
139+
140+
if (currentPosition === HOME_POSITIONS[player]) {
141+
return false;
142+
}
143+
144+
if (
145+
BASE_POSITIONS[player].includes(currentPosition) &&
146+
this.diceValue !== 6
147+
) {
148+
return false;
149+
}
150+
151+
if (
152+
HOME_ENTRANCE[player].includes(currentPosition) &&
153+
this.diceValue > HOME_POSITIONS[player] - currentPosition
154+
) {
155+
return false;
156+
}
157+
158+
return true;
159+
});
160+
}
161+
162+
listenResetClick() {
163+
UI.listenResetClick(this.resetGame.bind(this));
164+
}
165+
166+
resetGame() {
167+
console.log('reset game');
168+
this.currentPositions = structuredClone(BASE_POSITIONS);
169+
PLAYERS.forEach(player => {
170+
[0, 1, 2, 3].forEach(piece => {
171+
this.setPiecePosition(player, piece, this.currentPositions[player][piece]);
172+
});
173+
});
174+
this.turnIndex = -1; // Start from the first player on reset
175+
this.incrementTurn(); // Ensure the first player is selected
176+
this.state = STATE.DICE_NOT_ROLLED;
177+
}
178+
179+
listenPieceClick() {
180+
UI.listenPieceClick(this.onPieceClick.bind(this));
181+
}
182+
183+
184+
onPieceClick(event) {
185+
const target = event.target;
186+
187+
if (!target.classList.contains('player-piece') || !target.classList.contains('highlight')) {
188+
return;
189+
}
190+
console.log('piece clicked');
191+
192+
const player = target.getAttribute('player-id');
193+
const piece = target.getAttribute('piece');
194+
this.handlePieceClick(player, piece);
195+
}
196+
197+
handlePieceClick(player, piece) {
198+
console.log(player, piece);
199+
const currentPosition = this.currentPositions[player][piece];
200+
201+
if (BASE_POSITIONS[player].includes(currentPosition)) {
202+
this.setPiecePosition(player, piece, START_POSITIONS[player]);
203+
this.state = STATE.DICE_NOT_ROLLED;
204+
return;
205+
}
206+
207+
UI.unhighlightPieces();
208+
this.movePiece(player, piece, this.diceValue);
209+
}
210+
211+
setPiecePosition(player, piece, newPosition) {
212+
this.currentPositions[player][piece] = newPosition;
213+
UI.setPiecePosition(player, piece, newPosition);
214+
}
215+
216+
movePiece(player, piece, moveBy) {
217+
const interval = setInterval(() => {
218+
this.incrementPiecePosition(player, piece);
219+
moveBy--;
220+
221+
if (moveBy === 0) {
222+
clearInterval(interval);
223+
224+
// Check if player won
225+
if (this.hasPlayerWon(player)) {
226+
alert(`Player: ${player} has won!`);
227+
this.resetGame();
228+
return;
229+
}
230+
231+
const isKill = this.checkForKill(player, piece);
232+
233+
if (isKill || this.diceValue === 6) {
234+
this.state = STATE.DICE_NOT_ROLLED;
235+
return;
236+
}
237+
238+
this.incrementTurn();
239+
}
240+
}, 200);
241+
}
242+
243+
checkForKill(player, piece) {
244+
const currentPosition = this.currentPositions[player][piece];
245+
let kill = false;
246+
247+
PLAYERS.forEach(opponent => {
248+
if (opponent !== player) {
249+
[0, 1, 2, 3].forEach(piece => {
250+
const opponentPosition = this.currentPositions[opponent][piece];
251+
252+
if (currentPosition === opponentPosition && !SAFE_POSITIONS.includes(currentPosition)) {
253+
this.setPiecePosition(opponent, piece, BASE_POSITIONS[opponent][piece]);
254+
kill = true;
255+
}
256+
});
257+
}
258+
});
259+
260+
return kill;
261+
}
262+
263+
hasPlayerWon(player) {
264+
return [0, 1, 2, 3].every(piece => this.currentPositions[player][piece] === HOME_POSITIONS[player]);
265+
}
266+
267+
incrementPiecePosition(player, piece) {
268+
this.setPiecePosition(player, piece, this.getIncrementedPosition(player, piece));
269+
}
270+
271+
getIncrementedPosition(player, piece) {
272+
const currentPosition = this.currentPositions[player][piece];
273+
274+
if (currentPosition === TURNING_POINTS[player]) {
275+
return HOME_ENTRANCE[player][0];
276+
} else if (currentPosition === 51) {
277+
return 0;
278+
}
279+
return currentPosition + 1;
280+
}
281+
}

0 commit comments

Comments
 (0)