<!DOCTYPE html><html><head><title></title><style>
html, body {height:100%;margin:0;}
body {background: black;display: flex;align-items: center;justify-content: center;}
canvas {background: forestgreen;}</style></head><body><canvaswidth="960"height="832"id="game"></canvas><script>const canvas = document.getElementById('game');const context = canvas.getContext('2d');const grid =64;const numRows =13;const numCols =15;// create a new canvas and draw the soft wall image. then we can use this// canvas to draw the images later onconst softWallCanvas = document.createElement('canvas');const softWallCtx = softWallCanvas.getContext('2d');
softWallCanvas.width = softWallCanvas.height = grid;
softWallCtx.fillStyle ='black';
softWallCtx.fillRect(0,0, grid, grid);
softWallCtx.fillStyle ='#a9a9a9';// 1st row brick
softWallCtx.fillRect(1,1, grid -2,20);// 2nd row bricks
softWallCtx.fillRect(0,23,20,18);
softWallCtx.fillRect(22,23,42,18);// 3rd row bricks
softWallCtx.fillRect(0,43,42,20);
softWallCtx.fillRect(44,43,20,20);// create a new canvas and draw the soft wall image. then we can use this// canvas to draw the images later onconst wallCanvas = document.createElement('canvas');const wallCtx = wallCanvas.getContext('2d');
wallCanvas.width = wallCanvas.height = grid;
wallCtx.fillStyle ='black';
wallCtx.fillRect(0,0, grid, grid);
wallCtx.fillStyle ='white';
wallCtx.fillRect(0,0, grid -2, grid -2);
wallCtx.fillStyle ='#a9a9a9';
wallCtx.fillRect(2,2, grid -4, grid -4);// create a mapping of object typesconst types ={
wall:'▉',
softWall:1,
bomb:2};// keep track of all entitieslet entities =[];// keep track of what is in every cell of the game using a 2d array. the// template is used to note where walls are and where soft walls cannot spawn.// '▉' represents a wall// 'x' represents a cell that cannot have a soft wall (player start zone)let cells =[];const template =[['▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉'],['▉','x','x',,,,,,,,,,'x','x','▉'],['▉','x','▉',,'▉',,'▉',,'▉',,'▉',,'▉','x','▉'],['▉','x',,,,,,,,,,,,'x','▉'],['▉',,'▉',,'▉',,'▉',,'▉',,'▉',,'▉',,'▉'],['▉',,,,,,,,,,,,,,'▉'],['▉',,'▉',,'▉',,'▉',,'▉',,'▉',,'▉',,'▉'],['▉',,,,,,,,,,,,,,'▉'],['▉',,'▉',,'▉',,'▉',,'▉',,'▉',,'▉',,'▉'],['▉','x',,,,,,,,,,,,'x','▉'],['▉','x','▉',,'▉',,'▉',,'▉',,'▉',,'▉','x','▉'],['▉','x','x',,,,,,,,,,'x','x','▉'],['▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉']];// populate the level with walls and soft wallsfunction generateLevel(){
cells =[];for(let row =0; row < numRows; row++){
cells[row]=[];for(let col =0; col < numCols; col++){// 90% chance cells will contain a soft wallif(!template[row][col]&&Math.random()<0.90){
cells[row][col]= types.softWall;}elseif(template[row][col]=== types.wall){
cells[row][col]= types.wall;}}}}// blow up a bomb and its surrounding tilesfunction blowUpBomb(bomb){// bomb has already exploded so don't blow up againif(!bomb.alive)return;
bomb.alive =false;// remove bomb from grid
cells[bomb.row][bomb.col]=null;// explode bomb outward by sizeconst dirs =[{// up
row:-1,
col:0},{// down
row:1,
col:0},{// left
row:0,
col:-1},{// right
row:0,
col:1}];
dirs.forEach((dir)=>{for(let i =0; i < bomb.size; i++){const row = bomb.row + dir.row * i;const col = bomb.col + dir.col * i;const cell = cells[row][col];// stop the explosion if it hit a wallif(cell === types.wall){return;}// center of the explosion is the first iteration of the loop
entities.push(newExplosion(row, col, dir, i ===0?true:false));
cells[row][col]=null;// bomb hit another bomb so blow that one up tooif(cell === types.bomb){// find the bomb that was hit by comparing positionsconst nextBomb = entities.find((entity)=>{return(
entity.type === types.bomb &&
entity.row === row && entity.col === col
);});
blowUpBomb(nextBomb);}// stop the explosion if hit anythingif(cell){return;}}});}// bomb constructor functionfunctionBomb(row, col, size, owner){this.row = row;this.col = col;this.radius = grid *0.4;this.size = size;// the size of the explosionthis.owner = owner;// which player placed this bombthis.alive =true;this.type = types.bomb;// bomb blows up after 3 secondsthis.timer =3000;// update the bomb each framethis.update =function(dt){this.timer -= dt;// blow up bomb if timer is doneif(this.timer <=0){return blowUpBomb(this);}// change the size of the bomb every half second. we can determine the size// by dividing by 500 (half a second) and taking the ceiling of the result.// then we can check if the result is even or odd and change the sizeconst interval =Math.ceil(this.timer /500);if(interval %2===0){this.radius = grid *0.4;}else{this.radius = grid *0.5;}};// render the bomb each framethis.render =function(){const x =(this.col +0.5)* grid;const y =(this.row +0.5)* grid;// draw bomb
context.fillStyle ='black';
context.beginPath();
context.arc(x, y,this.radius,0,2*Math.PI);
context.fill();// draw bomb fuse moving up and down with the bomb sizeconst fuseY =(this.radius === grid *0.5? grid *0.15:0);
context.strokeStyle ='white';
context.lineWidth =5;
context.beginPath();
context.arc((this.col +0.75)* grid,(this.row +0.25)* grid - fuseY,10,Math.PI,-Math.PI /2);
context.stroke();};}// explosion constructor functionfunctionExplosion(row, col, dir, center){this.row = row;this.col = col;this.dir = dir;this.alive =true;// show explosion for 0.3 secondsthis.timer =300;// update the explosion each framethis.update =function(dt){this.timer -= dt;if(this.timer <=0){this.alive =false;}};// render the explosion each framethis.render =function(){const x =this.col * grid;const y =this.row * grid;const horizontal =this.dir.col;const vertical =this.dir.row;// create a fire effect by stacking red, orange, and yellow on top of// each other using progressively smaller rectangles
context.fillStyle ='#D72B16';// red
context.fillRect(x, y, grid, grid);
context.fillStyle ='#F39642';// orange// determine how to draw based on if it's vertical or horizontal// center draws both waysif(center || horizontal){
context.fillRect(x, y +6, grid, grid -12);}if(center || vertical){
context.fillRect(x +6, y, grid -12, grid);}
context.fillStyle ='#FFE5A8';// yellowif(center || horizontal){
context.fillRect(x, y +12, grid, grid -24);}if(center || vertical){
context.fillRect(x +12, y, grid -24, grid);}};}// player character (just a simple circle)const player ={
row:1,
col:1,
numBombs:1,
bombSize:3,
radius: grid *0.35,
render(){const x =(this.col +0.5)* grid;const y =(this.row +0.5)* grid;
context.save();
context.fillStyle ='white';
context.beginPath();
context.arc(x, y,this.radius,0,2*Math.PI);
context.fill();}}// game looplet last;let dt;function loop(timestamp){
requestAnimationFrame(loop);
context.clearRect(0,0,canvas.width,canvas.height);// calculate the time difference since the last update. requestAnimationFrame// passes the current timestamp as a parameter to the loopif(!last){
last = timestamp;}
dt = timestamp - last;
last = timestamp;// update and render everything in the gridfor(let row =0; row < numRows; row++){for(let col =0; col < numCols; col++){switch(cells[row][col]){case types.wall:
context.drawImage(wallCanvas, col * grid, row * grid);break;case types.softWall:
context.drawImage(softWallCanvas, col * grid, row * grid);break;}}}// update and render all entities
entities.forEach((entity)=>{
entity.update(dt);
entity.render();});// remove dead entities
entities = entities.filter((entity)=> entity.alive);
player.render();}// listen to keyboard events to move the snake
document.addEventListener('keydown',function(e){let row = player.row;let col = player.col;// left arrow keyif(e.which ===37){
col--;}// up arrow keyelseif(e.which ===38){
row--;}// right arrow keyelseif(e.which ===39){
col++;}// down arrow keyelseif(e.which ===40){
row++;}// space key (bomb)elseif(
e.which ===32&&!cells[row][col]&&// count the number of bombs the player has placed
entities.filter((entity)=>{return entity.type === types.bomb && entity.owner === player
}).length < player.numBombs
){// place bombconst bomb =newBomb(row, col, player.bombSize, player);
entities.push(bomb);
cells[row][col]= types.bomb;}// don't move the player if something is already at that positionif(!cells[row][col]){
player.row = row;
player.col = col;}});// start the game
generateLevel();
requestAnimationFrame(loop);</script></body></html>
Bomberman