Javascript Tetris – A Simple Implementation


<!DOCTYPE html>
<html lang='en'>
 
<head>
    <meta charset='UTF-8'>
    <style>
        canvas {
            position: absolute;
            top: 45%;
            left: 50%;
            width: 640px;
            height: 640px;
            margin: -320px 0 0 -320px;
        }
    </style>
</head>
 
<body>
    <canvas></canvas>
    <script>
        'use strict';
        var canvas = document.querySelector('canvas');
        canvas.width = 640;
        canvas.height = 640;
 
        var g = canvas.getContext('2d');
 
        var right = { x: 1, y: 0 };
        var down = { x: 0, y: 1 };
        var left = { x: -1, y: 0 };
 
        var EMPTY = -1;
        var BORDER = -2;
 
        var fallingShape;
        var nextShape;
        var dim = 640;
        var nRows = 18;
        var nCols = 12;
        var blockSize = 30;
        var topMargin = 50;
        var leftMargin = 20;
        var scoreX = 400;
        var scoreY = 330;
        var titleX = 130;
        var titleY = 160;
        var clickX = 120;
        var clickY = 400;
        var previewCenterX = 467;
        var previewCenterY = 97;
        var mainFont = 'bold 48px monospace';
        var smallFont = 'bold 18px monospace';
        var colors = ['green', 'red', 'blue', 'purple', 'orange', 'blueviolet', 'magenta'];
        var gridRect = { x: 46, y: 47, w: 308, h: 517 };
        var previewRect = { x: 387, y: 47, w: 200, h: 200 };
        var titleRect = { x: 100, y: 95, w: 252, h: 100 };
        var clickRect = { x: 50, y: 375, w: 252, h: 40 };
        var outerRect = { x: 5, y: 5, w: 630, h: 630 };
        var squareBorder = 'white';
        var titlebgColor = 'white';
        var textColor = 'black';
        var bgColor = '#DDEEFF';
        var gridColor = '#BECFEA';
        var gridBorderColor = '#7788AA';
        var largeStroke = 5;
        var smallStroke = 2;
 
        // position of falling shape
        var fallingShapeRow;
        var fallingShapeCol;
 
        var keyDown = false;
        var fastDown = false;
 
        var grid = [];
        var scoreboard = new Scoreboard();
 
        addEventListener('keydown', function (event) {
            if (!keyDown) {
                keyDown = true;
 
                if (scoreboard.isGameOver())
                    return;
 
                switch (event.key) {
 
                    case 'w':
                    case 'ArrowUp':
                        if (canRotate(fallingShape))
                            rotate(fallingShape);
                        break;
 
                    case 'a':
                    case 'ArrowLeft':
                        if (canMove(fallingShape, left))
                            move(left);
                        break;
 
                    case 'd':
                    case 'ArrowRight':
                        if (canMove(fallingShape, right))
                            move(right);
                        break;
 
                    case 's':
                    case 'ArrowDown':
                        if (!fastDown) {
                            fastDown = true;
                            while (canMove(fallingShape, down)) {
                                move(down);
                                draw();
                            }
                            shapeHasLanded();
                        }
                }
                draw();
            }
        });
 
        addEventListener('click', function () {
            startNewGame();
        });
 
        addEventListener('keyup', function () {
            keyDown = false;
            fastDown = false;
        });
 
        function canRotate(s) {
            if (s === Shapes.Square)
                return false;
 
            var pos = new Array(4);
            for (var i = 0; i < pos.length; i++) {
                pos[i] = s.pos[i].slice();
            }
 
            pos.forEach(function (row) {
                var tmp = row[0];
                row[0] = row[1];
                row[1] = -tmp;
            });
 
            return pos.every(function (p) {
                var newCol = fallingShapeCol + p[0];
                var newRow = fallingShapeRow + p[1];
                return grid[newRow][newCol] === EMPTY;
            });
        }
 
        function rotate(s) {
            if (s === Shapes.Square)
                return;
 
            s.pos.forEach(function (row) {
                var tmp = row[0];
                row[0] = row[1];
                row[1] = -tmp;
            });
        }
 
        function move(dir) {
            fallingShapeRow += dir.y;
            fallingShapeCol += dir.x;
        }
 
        function canMove(s, dir) {
            return s.pos.every(function (p) {
                var newCol = fallingShapeCol + dir.x + p[0];
                var newRow = fallingShapeRow + dir.y + p[1];
                return grid[newRow][newCol] === EMPTY;
            });
        }
 
        function shapeHasLanded() {
            addShape(fallingShape);
            if (fallingShapeRow < 2) {
                scoreboard.setGameOver();
                scoreboard.setTopscore();
            } else {
                scoreboard.addLines(removeLines());
            }
            selectShape();
        }
 
        function removeLines() {
            var count = 0;
            for (var r = 0; r < nRows - 1; r++) {
                for (var c = 1; c < nCols - 1; c++) {
                    if (grid[r] === EMPTY)
                        break;
                    if (c === nCols - 2) {
                        count++;
                        removeLine(r);
                    }
                }
            }
            return count;
        }
 
        function removeLine(line) {
            for (var c = 0; c < nCols; c++)
                grid[line] = EMPTY;
 
            for (var c = 0; c < nCols; c++) {
                for (var r = line; r > 0; r--)
                    grid[r] = grid[r - 1];
            }
        }
 
        function addShape(s) {
            s.pos.forEach(function (p) {
                grid[fallingShapeRow + p[1]][fallingShapeCol + p[0]] = s.ordinal;
            });
        }
 
        function Shape(shape, o) {
            this.shape = shape;
            this.pos = this.reset();
            this.ordinal = o;
        }
 
        var Shapes = {
            ZShape: [[0, -1], [0, 0], [-1, 0], [-1, 1]],
            SShape: [[0, -1], [0, 0], [1, 0], [1, 1]],
            IShape: [[0, -1], [0, 0], [0, 1], [0, 2]],
            TShape: [[-1, 0], [0, 0], [1, 0], [0, 1]],
            Square: [[0, 0], [1, 0], [0, 1], [1, 1]],
            LShape: [[-1, -1], [0, -1], [0, 0], [0, 1]],
            JShape: [[1, -1], [0, -1], [0, 0], [0, 1]]
        };
 
        function getRandomShape() {
            var keys = Object.keys(Shapes);
            var ord = Math.floor(Math.random() * keys.length);
            var shape = Shapes[keys[ord]];
            return new Shape(shape, ord);
        }
 
        Shape.prototype.reset = function () {
            this.pos = new Array(4);
            for (var i = 0; i < this.pos.length; i++) {
                this.pos[i] = this.shape[i].slice();
            }
            return this.pos;
        }
 
        function selectShape() {
            fallingShapeRow = 1;
            fallingShapeCol = 5;
            fallingShape = nextShape;
            nextShape = getRandomShape();
            if (fallingShape != null) {
                fallingShape.reset();
            }
        }
 
        function Scoreboard() {
            this.MAXLEVEL = 9;
 
            var level = 0;
            var lines = 0;
            var score = 0;
            var topscore = 0;
            var gameOver = true;
 
            this.reset = function () {
                this.setTopscore();
                level = lines = score = 0;
                gameOver = false;
            }
 
            this.setGameOver = function () {
                gameOver = true;
            }
 
            this.isGameOver = function () {
                return gameOver;
            }
 
            this.setTopscore = function () {
                if (score > topscore) {
                    topscore = score;
                }
            }
 
            this.getTopscore = function () {
                return topscore;
            }
 
            this.getSpeed = function () {
 
                switch (level) {
                    case 0: return 700;
                    case 1: return 600;
                    case 2: return 500;
                    case 3: return 400;
                    case 4: return 350;
                    case 5: return 300;
                    case 6: return 250;
                    case 7: return 200;
                    case 8: return 150;
                    case 9: return 100;
                    default: return 100;
                }
            }
 
            this.addScore = function (sc) {
                score += sc;
            }
 
            this.addLines = function (line) {
 
                switch (line) {
                    case 1:
                        this.addScore(10);
                        break;
                    case 2:
                        this.addScore(20);
                        break;
                    case 3:
                        this.addScore(30);
                        break;
                    case 4:
                        this.addScore(40);
                        break;
                    default:
                        return;
                }
 
                lines += line;
                if (lines > 10) {
                    this.addLevel();
                }
            }
 
            this.addLevel = function () {
                lines %= 10;
                if (level < this.MAXLEVEL) {
                    level++;
                }
            }
 
            this.getLevel = function () {
                return level;
            }
 
            this.getLines = function () {
                return lines;
            }
 
            this.getScore = function () {
                return score;
            }
        }
 
        function draw() {
            g.clearRect(0, 0, canvas.width, canvas.height);
 
            drawUI();
 
            if (scoreboard.isGameOver()) {
                drawStartScreen();
            } else {
                drawFallingShape();
            }
        }
 
        function drawStartScreen() {
            g.font = mainFont;
 
            fillRect(titleRect, titlebgColor);
            fillRect(clickRect, titlebgColor);
 
            g.fillStyle = textColor;
            g.fillText('Tetris', titleX, titleY);
 
            g.font = smallFont;
            g.fillText('click to start', clickX, clickY);
        }
 
        function fillRect(r, color) {
            g.fillStyle = color;
            g.fillRect(r.x, r.y, r.w, r.h);
        }
 
        function drawRect(r, color) {
            g.strokeStyle = color;
            g.strokeRect(r.x, r.y, r.w, r.h);
        }
 
        function drawSquare(colorIndex, r, c) {
            var bs = blockSize;
            g.fillStyle = colors[colorIndex];
            g.fillRect(leftMargin + c * bs, topMargin + r * bs, bs, bs);
 
            g.lineWidth = smallStroke;
            g.strokeStyle = squareBorder;
            g.strokeRect(leftMargin + c * bs, topMargin + r * bs, bs, bs);
        }
 
        function drawUI() {
 
            // background
            fillRect(outerRect, bgColor);
            fillRect(gridRect, gridColor);
 
            // the blocks dropped in the grid
            for (var r = 0; r < nRows; r++) {
                for (var c = 0; c < nCols; c++) {
                    var idx = grid[r];
                    if (idx > EMPTY)
                        drawSquare(idx, r, c);
                }
            }
 
            // the borders of grid and preview panel
            g.lineWidth = largeStroke;
            drawRect(gridRect, gridBorderColor);
            drawRect(previewRect, gridBorderColor);
            drawRect(outerRect, gridBorderColor);
 
            // scoreboard
            g.fillStyle = textColor;
            g.font = smallFont;
            g.fillText('hiscore    ' + scoreboard.getTopscore(), scoreX, scoreY);
            g.fillText('level      ' + scoreboard.getLevel(), scoreX, scoreY + 30);
            g.fillText('lines      ' + scoreboard.getLines(), scoreX, scoreY + 60);
            g.fillText('score      ' + scoreboard.getScore(), scoreX, scoreY + 90);
 
            // preview
            var minX = 5, minY = 5, maxX = 0, maxY = 0;
            nextShape.pos.forEach(function (p) {
                minX = Math.min(minX, p[0]);
                minY = Math.min(minY, p[1]);
                maxX = Math.max(maxX, p[0]);
                maxY = Math.max(maxY, p[1]);
            });
            var cx = previewCenterX - ((minX + maxX + 1) / 2.0 * blockSize);
            var cy = previewCenterY - ((minY + maxY + 1) / 2.0 * blockSize);
 
            g.translate(cx, cy);
            nextShape.shape.forEach(function (p) {
                drawSquare(nextShape.ordinal, p[1], p[0]);
            });
            g.translate(-cx, -cy);
        }
 
        function drawFallingShape() {
            var idx = fallingShape.ordinal;
            fallingShape.pos.forEach(function (p) {
                drawSquare(idx, fallingShapeRow + p[1], fallingShapeCol + p[0]);
            });
        }
 
       function animate(lastFrameTime) {
            var requestId = requestAnimationFrame(function () {
                animate(lastFrameTime);
            });
 
            var time = new Date().getTime();
            var delay = scoreboard.getSpeed();
 
            if (lastFrameTime + delay < time) {
 
                if (!scoreboard.isGameOver()) {
 
                    if (canMove(fallingShape, down)) {
                        move(down);
                    } else {
                        shapeHasLanded();
                    }
                    draw();
                    lastFrameTime = time;
 
                } else {
                    cancelAnimationFrame(requestId);
                }
            }
        }
 
        function startNewGame() {
            initGrid();
            selectShape();
            scoreboard.reset();
            animate(-1);
        }
 
        function initGrid() {
            function fill(arr, value) {
                for (var i = 0; i < arr.length; i++) {
                    arr[i] = value;
                }
            }
            for (var r = 0; r < nRows; r++) {
                grid[r] = new Array(nCols);
                fill(grid[r], EMPTY);
                for (var c = 0; c < nCols; c++) {
                    if (c === 0 || c === nCols - 1 || r === nRows - 1)
                        grid[r] = BORDER;
                }
            }
        }
 
        function init() {
            initGrid();
            selectShape();
            draw();
        }
 
        init();
    </script>
 
</body>
 
</html>

Source.

Blob Game with Collision Detection

<!DOCTYPE html>
<html>
<head>
<style>
body {background-color: Plum;}
canvas{ border: 2px solid black;}
</style>
</head>
<body>
  <img id="link1"  src="link1 (2).png" alt="Link1" style="display:none">
  <img id="link2"  src="link2 (2).png" alt="Link2" style="display:none">
  <img id="grass1"  src="grass1.png" alt="Link2" style="display:none">
  <img id="grass2"  src="grass2.png" alt="Link2" style="display:none">
  <img id="grass3"  src="grass3.png" alt="Link2" style="display:none">
  <img id="grass4"  src="grass4.png" alt="Link2" style="display:none">
  <img id="rock1"  src="rock1.png" alt="Link2" style="display:none">
  <canvas id = "game"></canvas>
<script>
  var Key = {  
    _pressed: {},
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    DOWN: 40,

    isDown: function(keyCode){
      return this._pressed[keyCode];
    },
 
    onKeydown: function(event) {
      this._pressed[event.keyCode] = true;
    },
 
    onKeyup: function(event) {
      delete this._pressed[event.keyCode];
    }
  };
 
  window.addEventListener('keyup', function(event) {Key.onKeyup(event); }, false);
  window.addEventListener('keydown', function(event) {Key.onKeydown(event); }, false);
  
  var OBSTACLES = [
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,1,0,1,0,0,1,0,0,0,0,0,0,0],
    [0,1,1,1,0,1,0,1,0,0,0,0,0,0],
    [0,1,0,1,0,1,1,1,0,0,0,0,0,0],
    [0,1,1,1,0,1,0,1,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,1]
    ];
  
  var canvas = document.getElementById('game');
  canvas.width = 1400; //window.innerWidth;
  canvas.height =900; //window.innerHeight;
  var grid = 100;
  var player_width = document.getElementById("link1").width;
  var player_height = document.getElementById("link1").height;
  //var player_width = document.getElementById("rock1").width;
  //var player_height = document.getElementById("rock1").height;
  console.log(player_width, player_height);
  
  function isCollidedOb(x,y,width,height,obarray){
    upperleft = [[Math.floor(y/grid)],[Math.floor(x/grid)]];
    lowerleft =  [[Math.floor((y+height)/grid)],[Math.floor((x)/grid)]];
    upperright = [[Math.floor((y)/grid)],[Math.floor((x+width)/grid)]];
    lowerright = [[Math.floor((y+height)/grid)],[Math.floor((x+width)/grid)]];
    console.log(upperleft[0],lowerleft,upperright[0],lowerright);
    if(obarray[upperleft[0]][upperleft[1]] || obarray[lowerleft[0]][lowerleft[1]] || obarray[upperright[0]][upperright[1]] || obarray[lowerright[0]][lowerright[1]]){
      return true;
    }
    return false;
  }
  
   function drawObs(obarray,grid){
      for (let i = 0; i < obarray.length; i++) {
          for (let j = 0; j <  obarray[i].length; j++) {
              if (obarray[i][j]){
                ctx.drawImage(rock1,j*grid,i*grid);
              }
          }
      }
   }
  
  var x = 50;
  var y = 50;
  var toggle = 1;
 
  var grass1 = document.getElementById("grass1");
  var rock1 = document.getElementById("rock1");
  var ctx = canvas.getContext('2d');
 
  var goalX = Math.random() * window.innerWidth;
  var goalY = Math.random() * window.innerHeight;
  var playerSize = 50;
  var goalSize = 15;

  var speed = 3;
  function draw() {
   
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "rgb(24, 255, 0)";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    drawObs(OBSTACLES,grid);
    ctx.drawImage(grass1, 200, 200);
    
    //MOVE UP
    if(Key.isDown(Key.UP)){
      toggle += 1;
      if(y < 0){
        y= canvas.height-player_height;
      }
      if(!isCollidedOb(x,Math.abs(y-speed),player_width,player_height,OBSTACLES)){
        y-=speed;
      }
    }
    
    //MOVE DOWN
    if(Key.isDown(Key.DOWN)){
      toggle += 1;
      if(y > canvas.height){
        y= y%canvas.height;
      }
      if(!isCollidedOb(x,y+speed,player_width,player_height,OBSTACLES)){
        y+=speed;
      } 
    }
    
    //MOVE LEFT
    if(Key.isDown(Key.LEFT)){
      toggle += 1;
      if(x < 0){
        x = canvas.width-player_width;
      }
      if(!isCollidedOb(x-speed,y,player_width,player_height,OBSTACLES)){
        x-=speed;
      }
    }
    
    //MOVE RIGHT
    if(Key.isDown(Key.RIGHT)){
      toggle += 1;
      if(x > canvas.width){
        x= x%canvas.width;
      }
      if(!isCollidedOb(x+speed,y,player_width,player_height,OBSTACLES)){
        x+=speed;
      }
    }
   
    if ((Math.abs(x-goalX))**2 + (Math.abs(y-goalY))**2 < (playerSize+goalSize)**2){
      //playerSize += 5;
      goalX = Math.random() * canvas.width;
      goalY = Math.random() * canvas.height;
    }
    //var character = new Path2D();
    ////////////////////////////////////////////////////////////////////
     //var character = new Image();
    //toggle += 1;
    toggle %= 100;
    if(toggle > 50){
    var character = document.getElementById("link1");
      //character.src = document.getElementById("link1");
    } else {
    //character.src = document.getElementById("link2");}
    var character = document.getElementById("link2");}
    //character.addEventListener('load', function() {
     // execute drawImage statements here
    ctx.drawImage(character, x, y);
    
    var goal = new Path2D();
    goal.arc(goalX, goalY, goalSize, 0, 2 * Math.PI);
   
    ctx.fillStyle = "#FF0000"
    //ctx.fill(character);
   
    ctx.fill(goal)
  }
  setInterval(draw, 10);
 
</script>
</body>
</html>