diff --git a/Ludo-Champs/README.md b/Ludo-Champs/README.md new file mode 100644 index 000000000..5d407327d --- /dev/null +++ b/Ludo-Champs/README.md @@ -0,0 +1,35 @@ +
+

🎲 Ludo Game

+
+ +- 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. + + diff --git a/Ludo-Champs/index.html b/Ludo-Champs/index.html new file mode 100644 index 000000000..b64070037 --- /dev/null +++ b/Ludo-Champs/index.html @@ -0,0 +1,79 @@ + + + + + + + Ludo Game + + + + +
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+ + + + +
+
+
+
+
+
+
+
+
+ + + +
Result: 0
+
+ + + + + + + + diff --git a/Ludo-Champs/ludo/Ludo.js b/Ludo-Champs/ludo/Ludo.js new file mode 100644 index 000000000..c087df9a1 --- /dev/null +++ b/Ludo-Champs/ludo/Ludo.js @@ -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; + } +} diff --git a/Ludo-Champs/ludo/UI.js b/Ludo-Champs/ludo/UI.js new file mode 100644 index 000000000..c7006165d --- /dev/null +++ b/Ludo-Champs/ludo/UI.js @@ -0,0 +1,102 @@ +import { COORDINATES_MAP, PLAYERS, STEP_LENGTH } from './constants.js'; + +const diceButtonElement = document.querySelector('#dice-btn'); +const playerPiecesElements = { + P1: document.querySelectorAll('[player-id="P1"].player-piece'), + P2: document.querySelectorAll('[player-id="P2"].player-piece'), + P3: document.querySelectorAll('[player-id="P3"].player-piece'), + P4: document.querySelectorAll('[player-id="P4"].player-piece'), +} + + +export class UI { + static listenDiceClick(callback) { + diceButtonElement.addEventListener('click', callback); + } + + static listenResetClick(callback) { + document.querySelector('button#reset-btn').addEventListener('click', callback); + } + + static listenPieceClick(callback) { + document.querySelector('.player-pieces').addEventListener('click', callback); + } + + /** + * + * @param {string} player + * @param {Number} piece + * @param {Number} newPosition + */ + static setPiecePosition(player, piece, newPosition) { + if (!playerPiecesElements[player] || !playerPiecesElements[player][piece]) { + console.error(`Player element of given player: ${player} and piece: ${piece} not found`); + return; + } + + const [x, y] = COORDINATES_MAP[newPosition]; + + const pieceElement = playerPiecesElements[player][piece]; + pieceElement.style.top = y * STEP_LENGTH + '%'; + pieceElement.style.left = x * STEP_LENGTH + '%'; + } + + static setTurn(index) { + if (index < 0 || index >= PLAYERS.length) { + console.error('index out of bound!'); + return; + } + + const player = PLAYERS[index]; + + // Display player ID + document.querySelector('.active-player span').innerText = player; + + const activePlayerBase = document.querySelector('.player-base.highlight'); + if (activePlayerBase) { + activePlayerBase.classList.remove('highlight'); + } + // highlight + document.querySelector(`[player-id="${player}"].player-base`).classList.add('highlight'); + } + + static enableDice() { + diceButtonElement.removeAttribute('disabled'); + } + + static disableDice() { + diceButtonElement.setAttribute('disabled', ''); + } + + /** + * + * @param {string} player + * @param {Number[]} pieces + */ + static highlightPieces(player, pieces) { + pieces.forEach(piece => { + const pieceElement = playerPiecesElements[player][piece]; + pieceElement.classList.add('highlight'); + }); + } + + static unhighlightPieces() { + document.querySelectorAll('.player-piece.highlight').forEach(ele => { + ele.classList.remove('highlight'); + }); + } + + static setDiceValue(value) { + document.querySelector('.dice-value').innerText = value; + } +} + +// UI.setPiecePosition('P1', 0, 0); +// UI.setTurn(0); +// UI.setTurn(1); + +// UI.disableDice(); +// UI.enableDice(); +// UI.highlightPieces('P1', [0]); +// UI.unhighlightPieces(); +// UI.setDiceValue(5); diff --git a/Ludo-Champs/ludo/constants.js b/Ludo-Champs/ludo/constants.js new file mode 100644 index 000000000..58b9bc3e3 --- /dev/null +++ b/Ludo-Champs/ludo/constants.js @@ -0,0 +1,160 @@ +export const COORDINATES_MAP = { + 0: [6, 13], + 1: [6, 12], + 2: [6, 11], + 3: [6, 10], + 4: [6, 9], + 5: [5, 8], + 6: [4, 8], + 7: [3, 8], + 8: [2, 8], + 9: [1, 8], + 10: [0, 8], + 11: [0, 7], + 12: [0, 6], + 13: [1, 6], + 14: [2, 6], + 15: [3, 6], + 16: [4, 6], + 17: [5, 6], + 18: [6, 5], + 19: [6, 4], + 20: [6, 3], + 21: [6, 2], + 22: [6, 1], + 23: [6, 0], + 24: [7, 0], + 25: [8, 0], + 26: [8, 1], + 27: [8, 2], + 28: [8, 3], + 29: [8, 4], + 30: [8, 5], + 31: [9, 6], + 32: [10, 6], + 33: [11, 6], + 34: [12, 6], + 35: [13, 6], + 36: [14, 6], + 37: [14, 7], + 38: [14, 8], + 39: [13, 8], + 40: [12, 8], + 41: [11, 8], + 42: [10, 8], + 43: [9, 8], + 44: [8, 9], + 45: [8, 10], + 46: [8, 11], + 47: [8, 12], + 48: [8, 13], + 49: [8, 14], + 50: [7, 14], + 51: [6, 14], + + // HOME ENTRANCE + + // P1 + 100: [7, 13], + 101: [7, 12], + 102: [7, 11], + 103: [7, 10], + 104: [7, 9], + 105: [7, 8], + + // P2 + 200: [7, 1], + 201: [7, 2], + 202: [7, 3], + 203: [7, 4], + 204: [7, 5], + 205: [7, 6], + + // p3 + 300: [13, 7], + 301: [12, 7], + 302: [11, 7], + 303: [10, 7], + 304: [9, 7], + 305: [8, 7], + + // p4 + 400: [1, 7], + 401: [2, 7], + 402: [3, 7], + 403: [4, 7], + 404: [5, 7], + 405: [6, 7], + + // BASE POSITIONS + + // P1 + 500: [1.5, 10.58], + 501: [3.57, 10.58], + 502: [1.5, 12.43], + 503: [3.57, 12.43], + + // P2 + 600: [10.5, 1.58], + 601: [12.54, 1.58], + 602: [10.5, 3.45], + 603: [12.54, 3.45], + + // p3 + 700: [10.5, 10.58], + 701: [12.57, 10.58], + 702: [10.5, 12.43], + 703: [12.57, 12.43], + + // p4 + 800: [1.5, 1.58], + 801: [3.57, 1.58], + 802: [1.5, 3.45], + 803: [3.55, 3.45], +}; + +export const STEP_LENGTH = 6.66; + +export const PLAYERS = ['P1', 'P2', 'P3', 'P4']; + +export const BASE_POSITIONS = { + P1: [500, 501, 502, 503], + P2: [600, 601, 602, 603], + P3: [700, 701, 702, 703], + P4: [800, 801, 802, 803], +} + +export const START_POSITIONS = { + P1: 0, + P2: 26, + P3: 39, + P4: 13, +} + +export const HOME_ENTRANCE = { + P1: [100, 101, 102, 103, 104], + P2: [200, 201, 202, 203, 204], + P3: [300, 301, 302, 303, 304], + P4: [400, 401, 402, 403, 404] +} + +export const HOME_POSITIONS = { + P1: 105, + P2: 205, + P3: 305, + P4: 405 +} + +export const TURNING_POINTS = { + P1: 50, + P2: 24, + P3: 37, + P4: 11 +} + +export const SAFE_POSITIONS = [0, 8, 13, 21, 26, 34, 39, 47]; + +export const STATE = { + DICE_NOT_ROLLED: 'DICE_NOT_ROLLED', + DICE_ROLLED: 'DICE_ROLLED', +} diff --git a/Ludo-Champs/ludo/dice.png b/Ludo-Champs/ludo/dice.png new file mode 100644 index 000000000..1a141509e Binary files /dev/null and b/Ludo-Champs/ludo/dice.png differ diff --git a/Ludo-Champs/ludo/ludo-bggg.png b/Ludo-Champs/ludo/ludo-bggg.png new file mode 100644 index 000000000..2e060fa66 Binary files /dev/null and b/Ludo-Champs/ludo/ludo-bggg.png differ diff --git a/Ludo-Champs/ludo/script.js b/Ludo-Champs/ludo/script.js new file mode 100644 index 000000000..9179ea9dd --- /dev/null +++ b/Ludo-Champs/ludo/script.js @@ -0,0 +1,47 @@ +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); +} + + +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 +} + +// Add event listener to the roll button +rollBtn.addEventListener('click', randomDice); diff --git a/Ludo-Champs/ludo/style.css b/Ludo-Champs/ludo/style.css new file mode 100644 index 000000000..5a0fbe088 --- /dev/null +++ b/Ludo-Champs/ludo/style.css @@ -0,0 +1,368 @@ +* { + box-sizing: border-box; +} + +.ludo-container { + width: 450px; + margin: 20px auto; + /* background-color: red; */ +} + +.ludo-container .ludo { + height: 450px; + width: 100%; + /* background-image: url('./ludo-bg.jpg'); */ + background-image: url(./ludo-bggg.png); + background-size: contain; + position: relative; + +} + + +.player-pieces { + height: 100%; + width: 100%; + /* background-color: red; */ +} + +.player-piece { + width: 3%; + height: 3%; + border: 2px solid; + border-radius: 10px; + position: absolute; + transform: translate(50%, 50%); + transition: all .2s; + z-index: 1; +} + +.player-piece.highlight { + cursor: pointer; + border: 2px dashed; + animation: spin 1s infinite linear; +} + +@keyframes spin { + 0% { + transform: translate(50%, 50%) rotate(0deg); + } + 50% { + transform: translate(50%, 50%) rotate(180deg) scale(1.4); + } + 100% { + transform: translate(50%, 50%) rotate(360deg); + } +} + +[player-id="P1"].player-piece { + background-color: #2eafff; + +} + +[player-id="P2"].player-piece { + background-color: #00b550; +} + +[player-id="P3"].player-piece { + background-color: #ff4757; + +} + +[player-id="P4"].player-piece { + background-color: #ffa502; +} + +.player-base { + width: 40%; + height: 40%; + border: 30px solid; + position: absolute; + +} + +.player-bases [player-id="P1"].player-base { + /* bottom: 0; */ + /* left: 0; */ + + border-color: #1295e7; + height: 38%; + width: 38.5%; + margin-top: -177px; + margin-left: 6px; + border-width: 22px; + border-right-width: 26px; + border-top-width: 24px; + + + + +} + +.player-bases [player-id="P2"].player-base { + /* top: 0; */ + /* right: 0; */ + border-color: #049645; + margin-top: -445px; + margin-left: 275px; + height: 38%; + width: 37.5%; + border-width: 22px; + border-top-width: 24px; +} + +.player-bases [player-id="P3"].player-base { + /* bottom: 0; + right: 0; */ + height: 38%; + width: 38%; + border-width: 22px; + border-top-width: 24px; + border-color: #d63031; + margin-left: 273px; + margin-top: -178px; + border-left-width: 24px; + border-bottom-width: 23px; +} + +.player-bases [player-id="P4"].player-base { + /* top: 0; + left: 0; */ + border-color: #f39c12; + height: 38%; + width: 38.5%; + margin-top: -444.6px; + margin-left: 6px; + border-width: 22px; + border-right-width: 27px; + border-top-width: 23px; + border-bottom-width: 24px; +} + +.player-base.highlight { + animation: border-blink .7s infinite ease-in-out; +} + +@keyframes border-blink { + 50% { + border-color: rgba(255, 255, 255, 0.8); + } +} + +.btn { + padding: 8px 20px; + border: none; + cursor: pointer; + font-size: 16px; + display: none; +} + +.btn:disabled { + opacity: 0.5; + display: none; +} + +.btn-dice { + background-color: #009d60; + color: white; + display: none; +} + +.row { + display: flex; + justify-content: space-between; + margin-top: 15px; +} + +.dice-value { + font-size: 24px; + font-weight: bold; + display: none; +} + + +* { + margin: 0; + padding: 0; + border: 0; + outline: 0; + box-sizing: border-box; +} + +body { + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + /* background: #b33951; */ +} + +.containeer { + display: grid; + place-items: center; + width: 250px; + padding: 60px 0 40px; + border-radius: 30px; + background: #eeeeee; + box-shadow: 0 0 20px rgba(0, 0, 0, .1); + margin-left: 1000px; + position: absolute; +} +.result { + margin-top: 20px; + font-size: 20px; + font-weight: bold; + color: #b33951; + font-family: 'Montserrat', sans-serif; +} + +.dice { + position: relative; + width: 100px; + height: 100px; + transform-style: preserve-3d; + transition: 1s ease; + /* margin-left: -100px; */ +} + +@keyframes rolling { + 50% { + transform: rotateX(455deg) rotateY(455deg); + } +} + +.face { + position: absolute; + width: 100%; + height: 100%; + border-radius: 20px; + border: 5px solid #f6f3f0; + transform-style: preserve-3d; + background: linear-gradient( + 145deg, #dddbd8, #fff + ); +} + +.face::before { + position: absolute; + content: ''; + width: 100%; + height: 100%; + border-radius: 20px; + background: #f6f3f0; + transform: translateZ(-1px); +} + +.face::after { + position: absolute; + content: ''; + top: 50%; + left: 50%; + width: 18px; + height: 18px; + border-radius: 50%; + background: #131210; +} + +.front { + transform: translateZ(50px); +} + +.back { + transform: rotateX(180deg) translateZ(50px); +} + +.top { + transform: rotateX(90deg) translateZ(50px); +} + +.bottom { + transform: rotateX(-90deg) translateZ(50px); +} + +.right { + transform: rotateY(90deg) translateZ(50px); +} + +.left { + transform: rotateY(-90deg) translateZ(50px); +} + +.front::after { + width: 30px; + height: 30px; + background: #f63330; + margin: -15px 0 0 -15px; +} + +.back::after { + margin: -35px 0 0 -30px; + box-shadow: 40px 0, + 0 25px, + 40px 25px, + 0 50px, + 40px 50px; +} + +.top::after { + margin: -30px 0 0 -30px; + box-shadow: 40px 40px; +} + +.bottom::after { + margin: -36px 0 0 -36px; + box-shadow: 26px 26px, + 52px 52px, + 52px 0, + 0 52px; +} + +.right::after { + margin: -30px 0 0 -30px; + box-shadow: 40px 0, + 0 40px, + 40px 40px; +} + +.left::after { + margin: -35px 0 0 -35px; + box-shadow: 25px 25px, + 50px 50px; +} + +.roll { + cursor: pointer; + color: #b33951; + margin-top: 60px; + padding: 6px 12px; + border-radius: 3px; + font: 700 16px 'Montserrat'; + border: 2px solid #b33951; + transition: .4s; +} + +.roll:hover { + color: #fff; + background: #b33951; +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ludo-Champs/main.js b/Ludo-Champs/main.js new file mode 100644 index 000000000..e7690a105 --- /dev/null +++ b/Ludo-Champs/main.js @@ -0,0 +1,3 @@ +import { Ludo } from './ludo/Ludo.js'; + +const ludo = new Ludo();