// Legend of grid case const legend = { empty: 0, laser: 1, coloredLaser: 2, mirror: 3, door: 4, button: 5, wall: 6, target: 7, ligthLaser: 8, demiWallCornerUpLeft: 9, demiWallCornerUpRight: 10, demiWallCornerDownLeft: 11, demiWallCornerDownRight: 12, doorOpen: 13, } const laserColors = { white: "white", red: "red", blue: "blue", yellow: "yellow", }; const glassOptions = [ laserColors.red, laserColors.blue, laserColors.yellow, ]; // Grid test let levels = [ [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 11, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 4, 0, 10, 6, 0, 0, 0, 0], [0, 0, 0, 0, 7, 6, 6, 5, 6, 0, 6, 0, 0, 0, 0], [0, 0, 0, 0, 3, 0, 0, 0, 0, 12, 6, 0, 0, 0, 0], [0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 9, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ] ]; let currentLevelIndex = 0; // Function to save initial orientation of mirrors let laserDirection = { dx: 0, dy: 0 }; let laserSegments = {}; let mirrorOrientations = {}; let activatedButtons = {}; let openedDoors = {}; function normalizeLaserDirection(dx, dy) { const epsilon = 0.0001; const normalizedDx = Math.abs(dx) < epsilon ? 0 : Math.sign(dx); const normalizedDy = Math.abs(dy) < epsilon ? 0 : Math.sign(dy); return { dx: normalizedDx, dy: normalizedDy }; } function reflectLaser(direction, mirrorAngle) { const mirrorRadians = mirrorAngle * (Math.PI / 180); const mirrorVectorX = Math.cos(mirrorRadians); const mirrorVectorY = Math.sin(mirrorRadians); const dotProduct = (direction.dx * mirrorVectorX) + (direction.dy * mirrorVectorY); const reflectedDx = (2 * dotProduct * mirrorVectorX) - direction.dx; const reflectedDy = (2 * dotProduct * mirrorVectorY) - direction.dy; return normalizeLaserDirection(reflectedDx, reflectedDy); } function getLaserSegmentClass(segmentDirection) { if (!segmentDirection) { return "laser-horizontal"; } if (segmentDirection.dx === 0) { return "laser-vertical"; } if (segmentDirection.dy === 0) { return "laser-horizontal"; } if (segmentDirection.dx === segmentDirection.dy) { return "laser-diagonal-down"; } return "laser-diagonal-up"; } function openAdjacentDoors(x, y) { // Open adjacent doors (up, down, left, right) const directions = [ { dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 } ]; for (let dir of directions) { const newX = x + dir.dx; const newY = y + dir.dy; if (newX >= 0 && newX < levels[currentLevelIndex][0].length && newY >= 0 && newY < levels[currentLevelIndex].length) { if (levels[currentLevelIndex][newY][newX] === legend.door) { openedDoors[`${newY},${newX}`] = true; levels[currentLevelIndex][newY][newX] = legend.doorOpen; // Change state to open } } } loadGrid(); // Refresh grid to show opened doors } function initializeMirrorOrientations() { mirrorOrientations = {}; // Reset for (let y = 0; y < levels[currentLevelIndex].length; y++) { for (let x = 0; x < levels[currentLevelIndex][y].length; x++) { if (levels[currentLevelIndex][y][x] === legend.mirror) { mirrorOrientations[`${y},${x}`] = 0; // Default angle } } } } // Function to print grid let mirrorCoordinates = []; function loadGrid() { const mapDiv = document.getElementById("map"); // Div with map in DOM mapDiv.innerHTML = ""; for (let y = 0; y < levels[currentLevelIndex].length; y++) { const lign = document.createElement("div"); lign.classList.add("lign"); for (let x = 0; x < levels[currentLevelIndex][y].length; x++) { const cell = document.createElement("div"); cell.classList.add("cell"); switch (levels[currentLevelIndex][y][x]) { case legend.empty: cell.classList.add("empty"); break; case legend.laser: cell.classList.add("laser"); break; case legend.coloredLaser: cell.classList.add("colored-laser"); break; case legend.mirror: const currentAngle = mirrorOrientations[`${y},${x}`] || 0; const btnMirror = document.createElement("button"); btnMirror.classList.add("btn-mirror"); btnMirror.type = "button"; btnMirror.style.transform = `rotate(${currentAngle}deg)`; const img = document.createElement("img"); img.src = "../../assets/img/tiles/Mirror.svg"; img.style.rotate = `${currentAngle}deg`; img.className = "mirror-img"; btnMirror.appendChild(img); btnMirror.onmousedown = (e) => { e.preventDefault(); e.stopPropagation(); rotateMirror(x, y, e.button === 2); }; btnMirror.oncontextmenu = (e) => e.preventDefault(); cell.appendChild(btnMirror); cell.classList.add("mirror"); break; case legend.door: cell.classList.add("door"); if (openedDoors[`${y},${x}`]) { cell.classList.add("door-open"); } break; case legend.button: cell.classList.add("button"); if (activatedButtons[`${y},${x}`]) { cell.classList.add("button-active"); } let button = document.createElement("button"); button.classList.add("button-door"); button.type = "button"; button.onclick = () => { activatedButtons[`${y},${x}`] = !activatedButtons[`${y},${x}`]; if (activatedButtons[`${y},${x}`]) { openAdjacentDoors(x, y); } traceLaser(); }; cell.appendChild(button); break; case legend.wall: cell.classList.add("wall"); break; case legend.target: cell.classList.add("target"); break; case legend.ligthLaser: cell.classList.add("light-laser"); const segmentDirection = laserSegments[`${y},${x}`]; cell.classList.add(getLaserSegmentClass(segmentDirection)); break; case legend.demiWallCornerUpLeft: cell.classList.add("demi-wall-corner-up-left"); break; case legend.demiWallCornerUpRight: cell.classList.add("demi-wall-corner-up-right"); break; case legend.demiWallCornerDownLeft: cell.classList.add("demi-wall-corner-down-left"); break; case legend.demiWallCornerDownRight: cell.classList.add("demi-wall-corner-down-right"); break; case legend.doorOpen: cell.classList.add("door-open"); break; } lign.appendChild(cell); } mapDiv.appendChild(lign); } } loadGrid(); // Function to rotate mirror function rotateMirror(x, y, isRightClick) { const coordKey = `${y},${x}`; if (levels[currentLevelIndex][y][x] !== legend.mirror) { return; } let currentAngle = mirrorOrientations[coordKey] || 0; // Rotation and normalize negative angles to [0, 360) currentAngle = (currentAngle + (isRightClick ? 22.5 : -22.5)) % 360; if (currentAngle < 0) { currentAngle += 360; } // Save mirrorOrientations[coordKey] = currentAngle; // Print laser light traceLaser(true); } // Function to trace let isLevelFinished = false; function traceLaser() { // Reset light laser from previous trace laserSegments = {}; // Ne pas réinitialiser activatedButtons et openedDoors pour préserver l'état des boutons manuels for (let y = 0; y < levels[currentLevelIndex].length; y++) { for (let x = 0; x < levels[currentLevelIndex][y].length; x++) { if (levels[currentLevelIndex][y][x] === legend.ligthLaser) { levels[currentLevelIndex][y][x] = legend.empty; } } } let startLaserX; let startLaserY; // Search laser for (let y = 0; y < levels[currentLevelIndex].length; y++) { for (let x = 0; x < levels[currentLevelIndex][y].length; x++) { if (levels[currentLevelIndex][y][x] === legend.laser) { startLaserX = x; startLaserY = y; laserDirection = { dx: 1, dy: 0 }; break; } } if (startLaserX !== undefined) break; } // If laser not found -> return if (startLaserX === undefined) { return; } let currentX = startLaserX; let currentY = startLaserY; let laserActive = true; const maxIterations = 1000; // Prevent infinite loops let iterations = 0; while (laserActive && iterations < maxIterations) { iterations++; currentX += laserDirection.dx; currentY += laserDirection.dy; // Out of bounds if (currentX < 0 || currentX >= levels[currentLevelIndex][0].length || currentY < 0 || currentY >= levels[currentLevelIndex].length) { laserActive = false; break; } const cellType = levels[currentLevelIndex][currentY][currentX]; switch (cellType) { case legend.laser: case legend.coloredLaser: laserActive = false; break; case legend.empty: levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser; laserSegments[`${currentY},${currentX}`] = { ...laserDirection }; break; case legend.target: levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser; laserSegments[`${currentY},${currentX}`] = { ...laserDirection }; laserActive = false; isLevelFinished = true; break; case legend.mirror: const mirrorAngle = mirrorOrientations[`${currentY},${currentX}`] || 0; laserDirection = reflectLaser(laserDirection, mirrorAngle); break; case legend.wall: laserActive = false; break; case legend.door: if (openedDoors[`${currentY},${currentX}`]) { // La porte est ouverte, le laser la traverse levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser; laserSegments[`${currentY},${currentX}`] = { ...laserDirection }; } else { // La porte est fermée, le laser s'arrête laserActive = false; } break; case legend.doorOpen: // Porte ouverte - le laser la traverse levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser; laserSegments[`${currentY},${currentX}`] = { ...laserDirection }; break; case legend.button: // Activer le bouton et ouvrir les portes adjacentes activatedButtons[`${currentY},${currentX}`] = true; openAdjacentDoors(currentX, currentY); levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser; laserSegments[`${currentY},${currentX}`] = { ...laserDirection }; laserActive = false; break; case legend.demiWallCornerUpLeft: laserDirection = reflectLaser(laserDirection, 135); break; case legend.demiWallCornerUpRight: laserDirection = reflectLaser(laserDirection, 45); break; case legend.demiWallCornerDownLeft: laserDirection = reflectLaser(laserDirection, 45); break; case legend.demiWallCornerDownRight: laserDirection = reflectLaser(laserDirection, 315); break; default: levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser; laserSegments[`${currentY},${currentX}`] = { ...laserDirection }; break; } } loadGrid(); if (isLevelFinished) { finish(); } } traceLaser(); // Reset level state function resetLevel() { activatedButtons = {}; openedDoors = {}; laserSegments = {}; laserDirection = { dx: 0, dy: 0 }; isLevelFinished = false; initializeMirrorOrientations(); traceLaser(); } // If level finishh -> call this function function finish() { setTimeout(() => { alert("Réussi !"); }, 100); }