Skip to content

Ludo implementation #1170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Ludo-Champs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<center>
<h1 style="font-size: 36px; font-family: 'Roboto', sans-serif; font-weight: bold;"> 🎲 Ludo Game </h1>
</center>

- This project is a web-based **Ludo game** where players can compete against each other in a classic Ludo board game experience.

### ✨ ***Features***
- "🎮 **Multiplayer Support**: Play with 4 players in an interactive game of Ludo."
- "🎲 **Dice Rolling**: Roll the dice to move tokens across the board."
- "🏠 **Player Tokens**: Each player controls four tokens that must race to the finish."
- "📱 **Responsive Design**: The game adjusts to different screen sizes, ensuring a smooth experience on any device."

### ⌨️ ***How to Play***
1. **Roll the Dice**: Click the "Roll" button or press the "Enter" key to roll the dice.
2. **Move Tokens**: Move your token by the number rolled on the dice.
3. **Race to the Finish**: Be the first player to get all your tokens to the "Home" area.
4. **Activate Turns**: Players take turns, and each token is moved based on the dice rolls.

### 🔧 **Technologies Used**
- **HTML**: The structure of the game board and user interface.
- **CSS**: Styling and layout for the board, tokens, and game buttons.
- **JavaScript**: Handles the game logic, dice rolls, token movements, and player turns.

### ⚙️ **How It Works**
1. **Rolling the Dice**: When you click the "Roll" button or press the "Enter" key, the dice rolls and the result is displayed.
2. **Moving Tokens**: Players move their tokens based on the dice roll. Each player has four tokens that they must move across the board.
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.

### 📂 ***File Structure***
- `index.html`: The main HTML file that structures the Ludo game, including the board and player tokens.
- `style.css`: Contains all the styles for the game, including the layout, colors, and animations.
- `main.js`: The JavaScript file that handles the game logic, such as rolling dice, moving tokens, and player turns.
- `script.js`: Additional JavaScript for user interface interactions and game controls.


79 changes: 79 additions & 0 deletions Ludo-Champs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ludo Game</title>
<link rel="icon" type="image/jpg" href="./ludo/dice.png"/>
<link rel="stylesheet" href="./ludo/style.css">
</head>
<body>
<div class="ludo-container">
<div class="ludo">
<div class="player-pieces">
<div class="player-piece" player-id="P1" piece="0"></div>
<div class="player-piece" player-id="P1" piece="1"></div>
<div class="player-piece" player-id="P1" piece="2"></div>
<div class="player-piece" player-id="P1" piece="3"></div>

<div class="player-piece" player-id="P2" piece="0"></div>
<div class="player-piece" player-id="P2" piece="1"></div>
<div class="player-piece" player-id="P2" piece="2"></div>
<div class="player-piece" player-id="P2" piece="3"></div>

<div class="player-piece" player-id="P3" piece="0"></div>
<div class="player-piece" player-id="P3" piece="1"></div>
<div class="player-piece" player-id="P3" piece="2"></div>
<div class="player-piece" player-id="P3" piece="3"></div>

<div class="player-piece" player-id="P4" piece="0"></div>
<div class="player-piece" player-id="P4" piece="1"></div>
<div class="player-piece" player-id="P4" piece="2"></div>
<div class="player-piece" player-id="P4" piece="3"></div>
</div>

<div class="player-bases">
<div class="player-base" player-id="P1"></div>
<div class="player-base" player-id="P2"></div>
<div class="player-base" player-id="P3"></div>
<div class="player-base" player-id="P4"></div>
</div>
</div>
<div class="footer">
<div class="row">
<button id="dice-btn" class="btn btn-dice">Roll</button>
<div class="dice-value"></div>
<button id="reset-btn" class="btn btn-reset">Reset</button>
</div>
<h2 class="active-player">Active Player: <span></span></h2>
</div>
</div>




<div class="containeer">
<div class="dice">
<div class="face front"></div>
<div class="face back"></div>
<div class="face top"></div>
<div class="face bottom"></div>
<div class="face right"></div>
<div class="face left"></div>
</div>

<button class="roll">
Roll Dice
</button>

<div class="result">Result: 0</div> <!-- Result display area -->
</div>

<!-- JS File -->
<script src="script.js" type="text/javascript"></script>

<script src="./main.js" type="module"></script>
</body>
</html>

281 changes: 281 additions & 0 deletions Ludo-Champs/ludo/Ludo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
import { BASE_POSITIONS, HOME_ENTRANCE, HOME_POSITIONS, PLAYERS, SAFE_POSITIONS, START_POSITIONS, STATE, TURNING_POINTS } from './constants.js';
import { UI } from './UI.js';

// Define the custom turn order
const TURN_ORDER = [0, 2, 1, 3];

// Dice roll elements and function
const dice = document.querySelector('.dice');
const rollBtn = document.querySelector('.roll');
const resultDisplay = document.querySelector('.result');

const randomDice = () => {
const random = Math.floor(Math.random() * 6) + 1; // Generates a number between 1 and 6
rollDice(random);
return random;
}

const rollDice = (random) => {
dice.style.animation = 'rolling 1s'; // Set a faster animation duration

setTimeout(() => {
// Show the dice face based on the random number
switch (random) {
case 1:
dice.style.transform = 'rotateX(0deg) rotateY(0deg)';
break;
case 2:
dice.style.transform = 'rotateX(-90deg) rotateY(0deg)';
break;
case 3:
dice.style.transform = 'rotateX(0deg) rotateY(90deg)';
break;
case 4:
dice.style.transform = 'rotateX(0deg) rotateY(-90deg)';
break;
case 5:
dice.style.transform = 'rotateX(90deg) rotateY(0deg)';
break;
case 6:
dice.style.transform = 'rotateX(180deg) rotateY(0deg)';
break;
default:
break;
}

dice.style.animation = 'none'; // Stop the animation after it completes

// Display the result
resultDisplay.textContent = `Result: ${random}`;
}, 1050); // Slightly more than the animation duration
}

export class Ludo {
currentPositions = {
P1: [],
P2: [],
P3: [],
P4: []
};

_diceValue;
get diceValue() {
return this._diceValue;
}
set diceValue(value) {
this._diceValue = value;
UI.setDiceValue(value);
}

_turn;
get turn() {
return this._turn;
}
set turn(value) {
this._turn = value;
UI.setTurn(value);
}

_state;
get state() {
return this._state;
}
set state(value) {
this._state = value;

if (value === STATE.DICE_NOT_ROLLED) {
UI.enableDice();
UI.unhighlightPieces();
} else {
UI.disableDice();
}
}

constructor() {
console.log('Hello World! Lets play Ludo!');

// Initialize the turn index to -1, so the first call to incrementTurn sets it to the first player
this.turnIndex = -1;
this.incrementTurn(); // Start the game with the first player
this.listenRollButtonClick();
this.listenResetClick();
this.listenPieceClick();

this.resetGame();
}

listenRollButtonClick() {
rollBtn.addEventListener('click', this.onDiceClick.bind(this));
}

onDiceClick() {
console.log('dice clicked!');
this.diceValue = randomDice(); // Animate the dice and set the value
this.state = STATE.DICE_ROLLED;
this.checkForEligiblePieces();
}

checkForEligiblePieces() {
const player = PLAYERS[this.turn];
const eligiblePieces = this.getEligiblePieces(player);
if (eligiblePieces.length) {
// Highlight the pieces
UI.highlightPieces(player, eligiblePieces);
} else {
this.incrementTurn();
}
}

incrementTurn() {
// Increment the turn index and follow the custom turn order
this.turnIndex = (this.turnIndex + 1) % TURN_ORDER.length;
this.turn = TURN_ORDER[this.turnIndex];
this.state = STATE.DICE_NOT_ROLLED;
}

getEligiblePieces(player) {
return [0, 1, 2, 3].filter(piece => {
const currentPosition = this.currentPositions[player][piece];

if (currentPosition === HOME_POSITIONS[player]) {
return false;
}

if (
BASE_POSITIONS[player].includes(currentPosition) &&
this.diceValue !== 6
) {
return false;
}

if (
HOME_ENTRANCE[player].includes(currentPosition) &&
this.diceValue > HOME_POSITIONS[player] - currentPosition
) {
return false;
}

return true;
});
}

listenResetClick() {
UI.listenResetClick(this.resetGame.bind(this));
}

resetGame() {
console.log('reset game');
this.currentPositions = structuredClone(BASE_POSITIONS);
PLAYERS.forEach(player => {
[0, 1, 2, 3].forEach(piece => {
this.setPiecePosition(player, piece, this.currentPositions[player][piece]);
});
});
this.turnIndex = -1; // Start from the first player on reset
this.incrementTurn(); // Ensure the first player is selected
this.state = STATE.DICE_NOT_ROLLED;
}

listenPieceClick() {
UI.listenPieceClick(this.onPieceClick.bind(this));
}


onPieceClick(event) {
const target = event.target;

if (!target.classList.contains('player-piece') || !target.classList.contains('highlight')) {
return;
}
console.log('piece clicked');

const player = target.getAttribute('player-id');
const piece = target.getAttribute('piece');
this.handlePieceClick(player, piece);
}

handlePieceClick(player, piece) {
console.log(player, piece);
const currentPosition = this.currentPositions[player][piece];

if (BASE_POSITIONS[player].includes(currentPosition)) {
this.setPiecePosition(player, piece, START_POSITIONS[player]);
this.state = STATE.DICE_NOT_ROLLED;
return;
}

UI.unhighlightPieces();
this.movePiece(player, piece, this.diceValue);
}

setPiecePosition(player, piece, newPosition) {
this.currentPositions[player][piece] = newPosition;
UI.setPiecePosition(player, piece, newPosition);
}

movePiece(player, piece, moveBy) {
const interval = setInterval(() => {
this.incrementPiecePosition(player, piece);
moveBy--;

if (moveBy === 0) {
clearInterval(interval);

// Check if player won
if (this.hasPlayerWon(player)) {
alert(`Player: ${player} has won!`);
this.resetGame();
return;
}

const isKill = this.checkForKill(player, piece);

if (isKill || this.diceValue === 6) {
this.state = STATE.DICE_NOT_ROLLED;
return;
}

this.incrementTurn();
}
}, 200);
}

checkForKill(player, piece) {
const currentPosition = this.currentPositions[player][piece];
let kill = false;

PLAYERS.forEach(opponent => {
if (opponent !== player) {
[0, 1, 2, 3].forEach(piece => {
const opponentPosition = this.currentPositions[opponent][piece];

if (currentPosition === opponentPosition && !SAFE_POSITIONS.includes(currentPosition)) {
this.setPiecePosition(opponent, piece, BASE_POSITIONS[opponent][piece]);
kill = true;
}
});
}
});

return kill;
}

hasPlayerWon(player) {
return [0, 1, 2, 3].every(piece => this.currentPositions[player][piece] === HOME_POSITIONS[player]);
}

incrementPiecePosition(player, piece) {
this.setPiecePosition(player, piece, this.getIncrementedPosition(player, piece));
}

getIncrementedPosition(player, piece) {
const currentPosition = this.currentPositions[player][piece];

if (currentPosition === TURNING_POINTS[player]) {
return HOME_ENTRANCE[player][0];
} else if (currentPosition === 51) {
return 0;
}
return currentPosition + 1;
}
}
Loading
Loading