Commit fda0afc5 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix: Ludo board completely rewritten to match standard layout

Standard Ludo board: Green(TL), Yellow(TR), Red(BL), Blue(BR)
Player order: 0=Red(BL), 1=Green(TL), 2=Yellow(TR), 3=Blue(BR)

Path is now CLOCKWISE:
- Red enters from bottom center going UP
- Green enters from left center going RIGHT
- Yellow enters from top center going DOWN
- Blue enters from right center going LEFT

All positions recalculated from scratch:
- SHARED_PATH: 52 squares going clockwise matching reference image
- HOME_COLUMNS: each player's 6-square run toward center
- HOME_BASES: pieces start in correct corners
- START_SQUARES: [0, 13, 26, 39] with correct global positions
- Center triangles: point toward center from each player's side
- Home zones: correct corner colors
- Panel positions: match board layout spatially
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent c2a2be92
// Ludo Board Map — CORRECT layout matching official Ludo board // Ludo Board Map — Standard layout matching official Ludo board
// 15x15 grid. Colors: Red(TL), Blue(TR), Green(BL), Yellow(BR) // 15x15 grid. GREEN(TL), YELLOW(TR), RED(BL), BLUE(BR)
// Path goes CLOCKWISE starting from Red's entry // Path goes CLOCKWISE. Player order: 0=Red(BL), 1=Green(TL), 2=Yellow(TR), 3=Blue(BR)
// Player order: 0=Red(TL), 1=Green(BL), 2=Yellow(BR), 3=Blue(TR)
// Standard Ludo: clockwise from Red → Green → Yellow → Blue
// The 52 shared squares going CLOCKWISE // The 52 shared squares going CLOCKWISE
// Starting from Red's entry (top-center going down) // Starting from Red's entry (bottom center, going UP)
export const SHARED_PATH = [ export const SHARED_PATH = [
// Red's start: top-center going DOWN — squares 0-4 // Red enters: UP through bottom arm left edge — squares 0-4
[6,1],[6,2],[6,3],[6,4],[6,5], [6,13],[6,12],[6,11],[6,10],[6,9],
// Turn left across top of Green zone — squares 5-10 // LEFT across left arm bottom edge — squares 5-10
[5,6],[4,6],[3,6],[2,6],[1,6],[0,6], [5,8],[4,8],[3,8],[2,8],[1,8],[0,8],
// Left corner — square 11 // Left edge corner — square 11
[0,7], [0,7],
// Left side going down (toward Green's start) — squares 12-13 // RIGHT across left arm top edge — squares 12-17
[0,8],[1,8], [0,6],[1,6],[2,6],[3,6],[4,6],[5,6],
// GREEN'S START (square 13): bottom-left going RIGHT — squares 13-17 // UP through top arm left edge — squares 18-23
[2,8],[3,8],[4,8],[5,8], [6,5],[6,4],[6,3],[6,2],[6,1],[6,0],
// Green's column going DOWN — squares 18-23 // Top edge corner — square 24
[6,9],[6,10],[6,11],[6,12],[6,13],[6,14],
// Bottom corner — square 24
[7,14],
// Bottom going UP (toward Yellow's start) — squares 25-26
[8,14],[8,13],
// YELLOW'S START (square 26): bottom-right going UP — squares 26-30
[8,12],[8,11],[8,10],[8,9],
// Turn right across bottom of Blue zone — squares 31-36
[9,8],[10,8],[11,8],[12,8],[13,8],[14,8],
// Right corner — square 37
[14,7],
// Right side going up (toward Blue's start) — squares 38-39
[14,6],[13,6],
// BLUE'S START (square 39): top-right going LEFT — squares 39-43
[12,6],[11,6],[10,6],[9,6],
// Blue's column going UP — squares 44-49
[8,5],[8,4],[8,3],[8,2],[8,1],[8,0],
// Top corner — square 50
[7,0], [7,0],
// Back to Red's home entry — square 51 // DOWN through top arm right edge — squares 25-30
[6,0], [8,0],[8,1],[8,2],[8,3],[8,4],[8,5],
// RIGHT across right arm top edge — squares 31-36
[9,6],[10,6],[11,6],[12,6],[13,6],[14,6],
// Right edge corner — square 37
[14,7],
// LEFT across right arm bottom edge — squares 38-43
[14,8],[13,8],[12,8],[11,8],[10,8],[9,8],
// DOWN through bottom arm right edge — squares 44-49
[8,9],[8,10],[8,11],[8,12],[8,13],[8,14],
// Bottom edge corner — square 50
[7,14],
// Last square before Red wraps — square 51
[6,14],
]; ];
// Starting squares for each player (where piece enters the shared path) // Starting squares for each player (where piece lands on rolling 6)
// Clockwise: Red=0, Green=13, Yellow=26, Blue=39 // Clockwise: Red=0, Green=13, Yellow=26, Blue=39
export const START_SQUARES = [0, 13, 26, 39]; export const START_SQUARES = [0, 13, 26, 39];
// Home column paths (6 squares each, going toward center) // Home column paths (6 squares each, going toward center)
export const HOME_COLUMNS = [ export const HOME_COLUMNS = [
[[7,1],[7,2],[7,3],[7,4],[7,5],[7,6]], // Red: top-center going down [[7,13],[7,12],[7,11],[7,10],[7,9],[7,8]], // Red: bottom center going UP
[[1,7],[2,7],[3,7],[4,7],[5,7],[6,7]], // Green: left-center going right [[1,7],[2,7],[3,7],[4,7],[5,7],[6,7]], // Green: left center going RIGHT
[[7,13],[7,12],[7,11],[7,10],[7,9],[7,8]], // Yellow: bottom-center going up [[7,1],[7,2],[7,3],[7,4],[7,5],[7,6]], // Yellow: top center going DOWN
[[13,7],[12,7],[11,7],[10,7],[9,7],[8,7]], // Blue: right-center going left [[13,7],[12,7],[11,7],[10,7],[9,7],[8,7]], // Blue: right center going LEFT
]; ];
// The GLOBAL square just before entering home column (local pos 51 → global) // The GLOBAL square at local position 51 (just before entering home column)
// Red (start=0): global 51 [6,0] → home [7,1] // Red: [6,14], Green: [0,6], Yellow: [8,0], Blue: [14,8]
// Green (start=13): global 12 [0,8] → home [1,7]
// Yellow (start=26): global 25 [8,14] → home [7,13]
// Blue (start=39): global 38 [14,6] → home [13,7]
export const HOME_ENTRY = [51, 12, 25, 38]; export const HOME_ENTRY = [51, 12, 25, 38];
// Safe squares — entry squares + midpoints between starts // Safe squares — each player's start + midpoints
export const SAFE_SQUARES = [0, 8, 13, 21, 26, 34, 39, 47]; export const SAFE_SQUARES = [0, 8, 13, 21, 26, 34, 39, 47];
// Home base piece positions (where 4 pieces sit before entering the board) // Home base piece positions (where 4 pieces sit before entering the board)
// Order: Red(TL), Green(BL), Yellow(BR), Blue(TR) // Red(BL), Green(TL), Yellow(TR), Blue(BR)
export const HOME_BASES = [ export const HOME_BASES = [
[[1.5,1.5],[3.5,1.5],[1.5,3.5],[3.5,3.5]], // Red (top-left) [[1.5,10.5],[3.5,10.5],[1.5,12.5],[3.5,12.5]], // Red (bottom-left)
[[1.5,10.5],[3.5,10.5],[1.5,12.5],[3.5,12.5]], // Green (bottom-left) [[1.5,1.5],[3.5,1.5],[1.5,3.5],[3.5,3.5]], // Green (top-left)
[[10.5,10.5],[12.5,10.5],[10.5,12.5],[12.5,12.5]], // Yellow (bottom-right) [[10.5,1.5],[12.5,1.5],[10.5,3.5],[12.5,3.5]], // Yellow (top-right)
[[10.5,1.5],[12.5,1.5],[10.5,3.5],[12.5,3.5]], // Blue (top-right) [[10.5,10.5],[12.5,10.5],[10.5,12.5],[12.5,12.5]],// Blue (bottom-right)
]; ];
// Convert grid [col,row] to pixel center // Convert grid [col,row] to pixel center
...@@ -83,20 +71,17 @@ export function gridToPixel(col, row, cellSize) { ...@@ -83,20 +71,17 @@ export function gridToPixel(col, row, cellSize) {
// Get pixel position for a piece given its local position and player index // Get pixel position for a piece given its local position and player index
export function getPiecePosition(localPos, playerIdx, cellSize) { export function getPiecePosition(localPos, playerIdx, cellSize) {
if (localPos === -1) return null; // In home base if (localPos === -1) return null;
if (localPos >= 52) { if (localPos >= 52) {
// In home column (positions 52-57)
const homeIdx = localPos - 52; const homeIdx = localPos - 52;
if (homeIdx < 6) { if (homeIdx < 6) {
const [col, row] = HOME_COLUMNS[playerIdx][homeIdx]; const [col, row] = HOME_COLUMNS[playerIdx][homeIdx];
return gridToPixel(col, row, cellSize); return gridToPixel(col, row, cellSize);
} }
// Finished — center of board
return gridToPixel(7, 7, cellSize); return gridToPixel(7, 7, cellSize);
} }
// On shared path — convert local position to global
const globalPos = (localPos + START_SQUARES[playerIdx]) % 52; const globalPos = (localPos + START_SQUARES[playerIdx]) % 52;
const [col, row] = SHARED_PATH[globalPos]; const [col, row] = SHARED_PATH[globalPos];
return gridToPixel(col, row, cellSize); return gridToPixel(col, row, cellSize);
......
...@@ -4,6 +4,7 @@ const HOME_STRETCH = 6; ...@@ -4,6 +4,7 @@ const HOME_STRETCH = 6;
const START_POSITIONS = [0, 13, 26, 39]; const START_POSITIONS = [0, 13, 26, 39];
const SAFE_SQUARES = [0, 8, 13, 21, 26, 34, 39, 47]; const SAFE_SQUARES = [0, 8, 13, 21, 26, 34, 39, 47];
// Player order: 0=Red(BL), 1=Green(TL), 2=Yellow(TR), 3=Blue(BR)
export const COLORS = ['red', 'green', 'yellow', 'blue']; export const COLORS = ['red', 'green', 'yellow', 'blue'];
export const COLOR_CSS = ['#E53935', '#43A047', '#FDD835', '#1E88E5']; export const COLOR_CSS = ['#E53935', '#43A047', '#FDD835', '#1E88E5'];
......
...@@ -15,7 +15,7 @@ import * as net from '../../../core/net.js'; ...@@ -15,7 +15,7 @@ import * as net from '../../../core/net.js';
let game, validMoves, ctx, canvas, boardSize, cellSize; let game, validMoves, ctx, canvas, boardSize, cellSize;
let diceAnimating = false; let diceAnimating = false;
// Order: Red(TL), Green(BL), Yellow(BR), Blue(TR) — clockwise from Red // Order: Red(BL), Green(TL), Yellow(TR), Blue(BR) — standard Ludo layout
const COLORS = ['#E53935', '#43A047', '#FDD835', '#1E88E5']; const COLORS = ['#E53935', '#43A047', '#FDD835', '#1E88E5'];
const COLORS_LIGHT = ['#EF9A9A', '#A5D6A7', '#FFF59D', '#90CAF9']; const COLORS_LIGHT = ['#EF9A9A', '#A5D6A7', '#FFF59D', '#90CAF9'];
let PLAYER_NAMES = ['أنت', 'Bot 1', 'Bot 2', 'Bot 3']; let PLAYER_NAMES = ['أنت', 'Bot 1', 'Bot 2', 'Bot 3'];
...@@ -51,13 +51,13 @@ export function mountGame(el, params) { ...@@ -51,13 +51,13 @@ export function mountGame(el, params) {
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;"> <div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;">
<div style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;"> <div style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;">
<div class="pp" id="pp-0" style="--pc:${COLORS[0]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[0]}</span><div class="pp-dice" id="dice-0"></div></div> <div class="pp" id="pp-1" style="--pc:${COLORS[1]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[1]}</span><div class="pp-dice" id="dice-1"></div></div>
<div class="pp" id="pp-3" style="--pc:${COLORS[3]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[3]}</span><div class="pp-dice" id="dice-3"></div></div> <div class="pp" id="pp-2" style="--pc:${COLORS[2]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[2]}</span><div class="pp-dice" id="dice-2"></div></div>
</div> </div>
<div id="ludo-wrap" style="flex:1;display:flex;align-items:center;justify-content:center;padding:4px;min-height:0;"></div> <div id="ludo-wrap" style="flex:1;display:flex;align-items:center;justify-content:center;padding:4px;min-height:0;"></div>
<div style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;"> <div style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;">
<div class="pp" id="pp-1" style="--pc:${COLORS[1]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[1]}</span><div class="pp-dice" id="dice-1"></div></div> <div class="pp" id="pp-0" style="--pc:${COLORS[0]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[0]}</span><div class="pp-dice" id="dice-0"></div></div>
<div class="pp" id="pp-2" style="--pc:${COLORS[2]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[2]}</span><div class="pp-dice" id="dice-2"></div></div> <div class="pp" id="pp-3" style="--pc:${COLORS[3]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[3]}</span><div class="pp-dice" id="dice-3"></div></div>
</div> </div>
<div id="dice-area" style="display:flex;align-items:center;gap:12px;padding:10px 16px;background:#0f0f1e;border-top:1px solid rgba(255,255,255,0.06);justify-content:center;"> <div id="dice-area" style="display:flex;align-items:center;gap:12px;padding:10px 16px;background:#0f0f1e;border-top:1px solid rgba(255,255,255,0.06);justify-content:center;">
<div id="dice-box" style="width:50px;height:50px;background:#f8fafc;border-radius:10px;display:grid;grid-template:repeat(3,1fr)/repeat(3,1fr);padding:6px;box-shadow:0 3px 10px rgba(0,0,0,0.4),inset 0 1px 0 rgba(255,255,255,0.8);transition:transform 0.15s cubic-bezier(0.34,1.56,0.64,1);"> <div id="dice-box" style="width:50px;height:50px;background:#f8fafc;border-radius:10px;display:grid;grid-template:repeat(3,1fr)/repeat(3,1fr);padding:6px;box-shadow:0 3px 10px rgba(0,0,0,0.4),inset 0 1px 0 rgba(255,255,255,0.8);transition:transform 0.15s cubic-bezier(0.34,1.56,0.64,1);">
...@@ -522,8 +522,8 @@ function drawBoard() { ...@@ -522,8 +522,8 @@ function drawBoard() {
ctx.fillRect(0, 0, boardSize, boardSize); ctx.fillRect(0, 0, boardSize, boardSize);
// Home zones — with inner white area // Home zones — with inner white area
// [playerIdx, gridCol, gridRow]: Red(TL), Green(BL), Yellow(BR), Blue(TR) // [playerIdx, gridCol, gridRow]: Red(BL), Green(TL), Yellow(TR), Blue(BR)
[[0,0,0],[1,0,9],[2,9,9],[3,9,0]].forEach(([p,c,r]) => { [[0,0,9],[1,0,0],[2,9,0],[3,9,9]].forEach(([p,c,r]) => {
ctx.fillStyle = COLORS[p]; ctx.fillStyle = COLORS[p];
ctx.fillRect(c*cs, r*cs, 6*cs, 6*cs); ctx.fillRect(c*cs, r*cs, 6*cs, 6*cs);
// Inner white area // Inner white area
...@@ -565,24 +565,24 @@ function drawBoard() { ...@@ -565,24 +565,24 @@ function drawBoard() {
// Center — 4 colored triangles forming a square (finish area) // Center — 4 colored triangles forming a square (finish area)
const cx = 7.5*cs, cy = 7.5*cs, hs = 1.5*cs; const cx = 7.5*cs, cy = 7.5*cs, hs = 1.5*cs;
// Red triangle (top) — player 0 // Red triangle (bottom → points up) — player 0
ctx.fillStyle = COLORS[0]; ctx.beginPath(); ctx.fillStyle = COLORS[0]; ctx.beginPath();
ctx.moveTo(cx-hs, cy-hs); ctx.lineTo(cx+hs, cy-hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill(); ctx.moveTo(cx-hs, cy+hs); ctx.lineTo(cx+hs, cy+hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill();
// Green triangle (left) — player 1 // Green triangle (left → points right) — player 1
ctx.fillStyle = COLORS[1]; ctx.beginPath(); ctx.fillStyle = COLORS[1]; ctx.beginPath();
ctx.moveTo(cx-hs, cy+hs); ctx.lineTo(cx-hs, cy-hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill(); ctx.moveTo(cx-hs, cy-hs); ctx.lineTo(cx-hs, cy+hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill();
// Yellow triangle (bottom) — player 2 // Yellow triangle (top → points down) — player 2
ctx.fillStyle = COLORS[2]; ctx.beginPath(); ctx.fillStyle = COLORS[2]; ctx.beginPath();
ctx.moveTo(cx+hs, cy+hs); ctx.lineTo(cx-hs, cy+hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill(); ctx.moveTo(cx-hs, cy-hs); ctx.lineTo(cx+hs, cy-hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill();
// Blue triangle (right) — player 3 // Blue triangle (right → points left) — player 3
ctx.fillStyle = COLORS[3]; ctx.beginPath(); ctx.fillStyle = COLORS[3]; ctx.beginPath();
ctx.moveTo(cx+hs, cy-hs); ctx.lineTo(cx+hs, cy+hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill(); ctx.moveTo(cx+hs, cy-hs); ctx.lineTo(cx+hs, cy+hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill();
// Center border // Center border
ctx.strokeStyle = '#fff'; ctx.lineWidth = 1.5; ctx.strokeStyle = '#fff'; ctx.lineWidth = 1.5;
ctx.strokeRect(cx-hs, cy-hs, hs*2, hs*2); ctx.strokeRect(cx-hs, cy-hs, hs*2, hs*2);
// Start position colored squares // Start position colored squares — where each player's piece enters the board
const startColors = [[6,1,'#FFCDD2'],[8,13,'#C8E6C9'],[1,8,'#FFF9C4'],[13,6,'#BBDEFB']]; const startColors = [[6,13,'#FFCDD2'],[1,6,'#C8E6C9'],[8,1,'#FFF9C4'],[13,8,'#BBDEFB']];
startColors.forEach(([col,row,color]) => { ctx.fillStyle = color; ctx.fillRect(col*cs+1, row*cs+1, cs-2, cs-2); }); startColors.forEach(([col,row,color]) => { ctx.fillStyle = color; ctx.fillRect(col*cs+1, row*cs+1, cs-2, cs-2); });
// Move preview — draw ghost circles at destination for highlighted pieces // Move preview — draw ghost circles at destination for highlighted pieces
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment