first commit
This commit is contained in:
Executable
+160
@@ -0,0 +1,160 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Labyrinthe avec niveaux</title>
|
||||
<style>
|
||||
body { margin:0; background:#000; color:#fff; font-family:Courier, monospace; }
|
||||
#wrap { display:flex; flex-direction:column; align-items:center; gap:8px; padding:12px; }
|
||||
canvas { background:#000; border:2px solid #0ff; box-shadow:0 0 12px #0ff; }
|
||||
.hud { text-align:center; }
|
||||
.hint { color:#aaa; font-size:12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrap">
|
||||
<canvas id="game" width="960" height="720"></canvas>
|
||||
<div id="hud" class="hud"></div>
|
||||
<div class="hint">Flèches pour bouger • R: restart</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const canvas = document.getElementById('game');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const W = canvas.width, H = canvas.height;
|
||||
|
||||
// ----- State -----
|
||||
let COLS, ROWS, CELL, OFFSET_X, OFFSET_Y;
|
||||
const grid = [];
|
||||
const player = { c:0, r:0, steps:0 };
|
||||
const exit = { c:0, r:0 };
|
||||
const state = { level:1, win:false, startTime:0, best:0 };
|
||||
|
||||
// ----- Génération du labyrinthe -----
|
||||
function index(c,r){ return r*COLS+c; }
|
||||
function neighbors(c,r){
|
||||
const n=[];
|
||||
if(r>0) n.push([c,r-1,0]);
|
||||
if(c<COLS-1) n.push([c+1,r,1]);
|
||||
if(r<ROWS-1) n.push([c,r+1,2]);
|
||||
if(c>0) n.push([c-1,r,3]);
|
||||
return n;
|
||||
}
|
||||
function carveMaze(startC=0,startR=0){
|
||||
grid.length=0;
|
||||
for(let r=0;r<ROWS;r++){
|
||||
for(let c=0;c<COLS;c++){
|
||||
grid.push({c,r,visited:false,walls:[true,true,true,true]});
|
||||
}
|
||||
}
|
||||
const stack=[];
|
||||
let current=grid[index(startC,startR)];
|
||||
current.visited=true; stack.push(current);
|
||||
while(stack.length){
|
||||
const cell=stack[stack.length-1];
|
||||
const cand=neighbors(cell.c,cell.r)
|
||||
.map(([nc,nr,dir])=>({nc,nr,dir,cell:grid[index(nc,nr)]}))
|
||||
.filter(o=>!o.cell.visited);
|
||||
if(cand.length===0){ stack.pop(); }
|
||||
else{
|
||||
const pick=cand[(Math.random()*cand.length)|0];
|
||||
cell.walls[pick.dir]=false;
|
||||
pick.cell.walls[(pick.dir+2)%4]=false;
|
||||
pick.cell.visited=true;
|
||||
stack.push(pick.cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Reset niveau -----
|
||||
function reset(level=1){
|
||||
state.level=level;
|
||||
COLS=20+level*2; ROWS=15+level*2;
|
||||
CELL=Math.floor(Math.min(W/COLS,H/ROWS));
|
||||
OFFSET_X=Math.floor((W-COLS*CELL)/2);
|
||||
OFFSET_Y=Math.floor((H-ROWS*CELL)/2);
|
||||
carveMaze(0,0);
|
||||
player.c=0; player.r=0; player.steps=0;
|
||||
exit.c=COLS-1; exit.r=ROWS-1;
|
||||
state.startTime=performance.now();
|
||||
state.win=false;
|
||||
}
|
||||
|
||||
// ----- Input -----
|
||||
const keys={ArrowUp:false,ArrowRight:false,ArrowDown:false,ArrowLeft:false};
|
||||
let cooldown=0;
|
||||
window.addEventListener('keydown',e=>{
|
||||
if(e.code in keys) keys[e.code]=true;
|
||||
if(e.code==='KeyR') reset(1);
|
||||
});
|
||||
window.addEventListener('keyup',e=>{
|
||||
if(e.code in keys) keys[e.code]=false;
|
||||
});
|
||||
|
||||
function tryMove(dc,dr,wallIndex){
|
||||
if(state.win) return;
|
||||
const cell=grid[index(player.c,player.r)];
|
||||
if(!cell.walls[wallIndex]){
|
||||
player.c+=dc; player.r+=dr; player.steps++;
|
||||
if(player.c===exit.c && player.r===exit.r){
|
||||
state.win=true;
|
||||
const elapsed=((performance.now()-state.startTime)/1000)|0;
|
||||
if(state.level>state.best) state.best=state.level;
|
||||
setTimeout(()=>reset(state.level+1),1500); // passe au niveau suivant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Update -----
|
||||
function update(dt){
|
||||
cooldown-=dt*1000;
|
||||
if(cooldown<=0){
|
||||
if(keys.ArrowUp){ tryMove(0,-1,0); cooldown=100; }
|
||||
else if(keys.ArrowRight){ tryMove(1,0,1); cooldown=100; }
|
||||
else if(keys.ArrowDown){ tryMove(0,1,2); cooldown=100; }
|
||||
else if(keys.ArrowLeft){ tryMove(-1,0,3); cooldown=100; }
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Draw -----
|
||||
function draw(){
|
||||
ctx.fillStyle='#000'; ctx.fillRect(0,0,W,H);
|
||||
ctx.strokeStyle='#0ff'; ctx.lineWidth=2;
|
||||
for(const cell of grid){
|
||||
const x=OFFSET_X+cell.c*CELL, y=OFFSET_Y+cell.r*CELL;
|
||||
if(cell.walls[0]){ ctx.beginPath(); ctx.moveTo(x,y); ctx.lineTo(x+CELL,y); ctx.stroke(); }
|
||||
if(cell.walls[1]){ ctx.beginPath(); ctx.moveTo(x+CELL,y); ctx.lineTo(x+CELL,y+CELL); ctx.stroke(); }
|
||||
if(cell.walls[2]){ ctx.beginPath(); ctx.moveTo(x,y+CELL); ctx.lineTo(x+CELL,y+CELL); ctx.stroke(); }
|
||||
if(cell.walls[3]){ ctx.beginPath(); ctx.moveTo(x,y); ctx.lineTo(x,y+CELL); ctx.stroke(); }
|
||||
}
|
||||
// exit
|
||||
ctx.fillStyle='#39FF14';
|
||||
ctx.beginPath();
|
||||
ctx.arc(OFFSET_X+exit.c*CELL+CELL/2,OFFSET_Y+exit.r*CELL+CELL/2,CELL*0.3,0,Math.PI*2);
|
||||
ctx.fill();
|
||||
// player
|
||||
ctx.fillStyle='#00FFFF';
|
||||
ctx.beginPath();
|
||||
ctx.arc(OFFSET_X+player.c*CELL+CELL/2,OFFSET_Y+player.r*CELL+CELL/2,CELL*0.3,0,Math.PI*2);
|
||||
ctx.fill();
|
||||
// HUD
|
||||
const elapsed=((performance.now()-state.startTime)/1000)|0;
|
||||
document.getElementById('hud').innerHTML=
|
||||
`Niveau: ${state.level} Pas: ${player.steps} Temps: ${elapsed}s Meilleur: ${state.best}`;
|
||||
}
|
||||
|
||||
// ----- Loop -----
|
||||
let last=performance.now();
|
||||
function frame(t){
|
||||
const dt=(t-last)/1000; last=t;
|
||||
update(dt); draw();
|
||||
requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
reset(1);
|
||||
requestAnimationFrame(frame);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user