Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

Tetris In ServiceNow!

RobertHayton
Tera Expert

This has been a Project of mine for a while now. I wanted to share it with the community now that it is working.

RobertHayton_3-1715616765272.png

 

I was able to get it to work as a Widget on a service portal page.

The XML and the image/Logo file are attached below

 

Body HTML

<div class="text-center">
<div class="text-muted">${control using keyboard arrow keys}</div>
<body>
<div id="tetris">
<div id="info">
<div id="next_shape"></div>
<p id="level">Level: <span></span></p>
<p id="lines">Lines: <span></span></p>
<p id="score">Score: <span></span></p>
<p id="time">Time: <span></span></p>
<button id="start">Start</button>
<p class="red">Press the Esc button to pause<span></span></p>
</div>
<div id="canvas"></div>
</div>
<script type="text/javascript" src="script.js"></script>
</body>
</div>

 

CSS

 

body {
overflow: hidden;
background: #d7d7d7;
}
#tetris {
width: 360px;
border: 1px solid black;
padding: 20px;
}
#canvas {
width: 200px;
height: 440px;
background-color: #000;
position: relative;
color: #fff;
}
#canvas h1 {
margin: 0;
padding: 0;
text-align: center;
font-size: 30px;
padding-top: 200px;
}
.piece {
border: 1px solid white;
position: absolute;
}

#start{
animation: blink .7s steps(2, start) infinite;
background: #E1FF5F;
border-radius: 2px;
color: #202020;
cursor: pointer;
font-size: 28px;
}

@keyframes blink {
to {
outline: #E1FF5F solid 1px;
}
}

.red{
color: #f00000;
}

.square {
position: absolute;
width: 19px;
height: 19px;
border: 1px solid white;
}
.type0 {
background-color: #a000f0;
}
.type1 {
background-color: #00f0f0;
}
.type2 {
background-color: #f0a000;
}
.type3 {
background-color: #0000f0;
}
.type4 {
background-color: #00f000;
}
.type5 {
background-color: #f00000;
}
.type6 {
background-color: #f0f000;
}
#next_shape {
position: relative;
background-color: #000;
border: 1px solid white;
width: 110px;
height: 110px;
}
#info {
background-color: #000;
color: #fff;
float: right;
width: 110px;
height: 420px;
padding: 10px;
}

 

Controller As c

 

Link 

 

(function () {
  var isStart = false;
  var tetris = {
    board: [],
    boardDiv: null,
    canvas: null,
    pSize: 20,
    canvasHeight: 440,
    canvasWidth: 200,
    boardHeight: 0,
    boardWidth: 0,
    spawnX: 4,
    spawnY: 1,
    shapes: [
      [
        [-1, 1],
        [0, 1],
        [1, 1],
        [0, 0], //TEE
      ],
      [
        [-1, 0],
        [0, 0],
        [1, 0],
        [2, 0], //line
      ],
      [
        [-1, -1],
        [-1, 0],
        [0, 0],
        [1, 0], //L EL
      ],
      [
        [1, -1],
        [-1, 0],
        [0, 0],
        [1, 0], //R EL
      ],
      [
        [0, -1],
        [1, -1],
        [-1, 0],
        [0, 0], // R ess
      ],
      [
        [-1, -1],
        [0, -1],
        [0, 0],
        [1, 0], //L ess
      ],
      [
        [0, -1],
        [1, -1],
        [0, 0],
        [1, 0], //square
      ],
    ],
    tempShapes: null,
    curShape: null,
    curShapeIndex: null,
    curX: 0,
    curY: 0,
    curSqs: [],
    nextShape: null,
    nextShapeDisplay: null,
    nextShapeIndex: null,
    sqs: [],
    score: 0,
    scoreDisplay: null,
    level: 1,
    levelDisplay: null,
    numLevels: 10,
    time: 0,
    maxTime: 1000,
    timeDisplay: null,
    isActive: 0,
    curComplete: false,
    timer: null,
    sTimer: null,
    speed: 700,
    lines: 0,

    init: function () {
      isStart = true;
      this.canvas = document.getElementById('canvas');
      this.initBoard();
      this.initInfo();
      this.initLevelScores();
      this.initShapes();
      this.bindKeyEvents();
      this.play();
    },
    initBoard: function () {
      this.boardHeight = this.canvasHeight / this.pSize;
      this.boardWidth = this.canvasWidth / this.pSize;
      var s = this.boardHeight * this.boardWidth;
      for (var i = 0; i < s; i++) {
        this.board.push(0);
      }
      //this.boardDiv = document.getElementById('board); //for debugging
    },
    initInfo: function () {
      this.nextShapeDisplay = document.getElementById('next_shape');
      this.levelDisplay = document
        .getElementById('level')
        .getElementsByTagName('span')[0];
      this.timeDisplay = document
        .getElementById('time')
        .getElementsByTagName('span')[0];
      this.scoreDisplay = document
        .getElementById('score')
        .getElementsByTagName('span')[0];
      this.linesDisplay = document
        .getElementById('lines')
        .getElementsByTagName('span')[0];
      this.setInfo('time');
      this.setInfo('score');
      this.setInfo('level');
      this.setInfo('lines');
    },
    initShapes: function () {
      this.curSqs = [];
      this.curComplete = false;
      this.shiftTempShapes();
      this.curShapeIndex = this.tempShapes[0];
      this.curShape = this.shapes[this.curShapeIndex];
      this.initNextShape();
      this.setCurCoords(this.spawnX, this.spawnY);
      this.drawShape(this.curX, this.curY, this.curShape);
    },
    initNextShape: function () {
      if (typeof this.tempShapes[1] === 'undefined') {
        this.initTempShapes();
      }
      try {
        this.nextShapeIndex = this.tempShapes[1];
        this.nextShape = this.shapes[this.nextShapeIndex];
        this.drawNextShape();
      } catch (e) {
        throw new Error('Could not create next shape. ' + e);
      }
    },
    initTempShapes: function () {
      this.tempShapes = [];
      for (var i = 0; i < this.shapes.length; i++) {
        this.tempShapes.push(i);
      }
      var k = this.tempShapes.length;
      while (--k) {
        //Fisher Yates Shuffle
        var j = Math.floor(Math.random() * (k + 1));
        var tempk = this.tempShapes[k];
        var tempj = this.tempShapes[j];
        this.tempShapes[k] = tempj;
        this.tempShapes[j] = tempk;
      }
    },
    shiftTempShapes: function () {
      try {
        if (
          typeof this.tempShapes === 'undefined' ||
          this.tempShapes === null
        ) {
          this.initTempShapes();
        } else {
          this.tempShapes.shift();
        }
      } catch (e) {
        throw new Error('Could not shift or init tempShapes: ' + e);
      }
    },
    initTimer: function () {
      var me = this;
      var tLoop = function () {
        me.incTime();
        me.timer = setTimeout(tLoop, 2000);
      };
      this.timer = setTimeout(tLoop, 2000);
    },
    initLevelScores: function () {
      var c = 1;
      for (var i = 1; i <= this.numLevels; i++) {
        this['level' + i] = [c * 1000, 40 * i, 5 * i]; //for nxt level, row score, p sore,
        c = c + c;
      }
    },
    setInfo: function (el) {
      this[el + 'Display'].innerHTML = this[el];
    },
    drawNextShape: function () {
      var ns = [];
      for (var i = 0; i < this.nextShape.length; i++) {
        ns[i] = this.createSquare(
          this.nextShape[i][0] + 2,
          this.nextShape[i][1] + 2,
          this.nextShapeIndex
        );
      }
      this.nextShapeDisplay.innerHTML = '';
      for (var k = 0; k < ns.length; k++) {
        this.nextShapeDisplay.appendChild(ns[k]);
      }
    },
    drawShape: function (x, y, p) {
      for (var i = 0; i < p.length; i++) {
        var newX = p[i][0] + x;
        var newY = p[i][1] + y;
        this.curSqs[i] = this.createSquare(newX, newY, this.curShapeIndex);
      }
      for (var k = 0; k < this.curSqs.length; k++) {
        this.canvas.appendChild(this.curSqs[k]);
      }
    },
    createSquare: function (x, y, type) {
      var el = document.createElement('div');
      el.className = 'square type' + type;
      el.style.left = x * this.pSize + 'px';
      el.style.top = y * this.pSize + 'px';
      return el;
    },
    removeCur: function () {
      var me = this;
      this.curSqs.eachdo(function () {
        me.canvas.removeChild(this);
      });
      this.curSqs = [];
    },
    setCurCoords: function (x, y) {
      this.curX = x;
      this.curY = y;
    },
    bindKeyEvents: function () {
      var me = this;
      var event = 'keypress';
      if (this.isSafari() || this.isIE()) {
        event = 'keydown';
      }
      var cb = function (e) {
        me.handleKey(e);
      };
      if (window.addEventListener) {
        document.addEventListener(event, cb, false);
      } else {
        document.attachEvent('on' + event, cb);
      }
    },
    handleKey: function (e) {
      var c = this.whichKey(e);
      var dir = '';
      switch (c) {
        case 37:
          this.move('L');
          break;
        case 38:
          this.move('RT');
          break;
        case 39:
          this.move('R');
          break;
        case 40:
          this.move('D');
          break;
        case 27: //esc: pause
          this.togglePause();
          break;
        default:
          break;
      }
    },
    whichKey: function (e) {
      var c;
      if (window.event) {
        c = window.event.keyCode;
      } else if (e) {
        c = e.keyCode;
      }
      return c;
    },
    incTime: function () {
      this.time++;
      this.setInfo('time');
    },
    incScore: function (amount) {
      this.score = this.score + amount;
      this.setInfo('score');
    },
    incLevel: function () {
      this.level++;
      this.speed = this.speed - 75;
      this.setInfo('level');
    },
    incLines: function (num) {
      this.lines += num;
      this.setInfo('lines');
    },
    calcScore: function (args) {
      var lines = args.lines || 0;
      var shape = args.shape || false;
      var speed = args.speed || 0;
      var score = 0;

      if (lines > 0) {
        score += lines * this['level' + this.level][1];
        this.incLines(lines);
      }
      if (shape === true) {
        score += shape * this['level' + this.level][2];
      }
      /*if (speed > 0){ score += speed * this["level" +this .level[3]];}*/
      this.incScore(score);
    },
    checkScore: function () {
      if (this.score >= this['level' + this.level][0]) {
        this.incLevel();
      }
    },
    gameOver: function () {
      this.clearTimers();
      isStart = false;
      this.canvas.innerHTML = '<h1>GAME OVER</h1>';
    },
    play: function () {
      var me = this;
      if (this.timer === null) {
        this.initTimer();
      }
      var gameLoop = function () {
        me.move('D');
        if (me.curComplete) {
          me.markBoardShape(me.curX, me.curY, me.curShape);
          me.curSqs.eachdo(function () {
            me.sqs.push(this);
          });
          me.calcScore({ shape: true });
          me.checkRows();
          me.checkScore();
          me.initShapes();
          me.play();
        } else {
          me.pTimer = setTimeout(gameLoop, me.speed);
        }
      };
      this.pTimer = setTimeout(gameLoop, me.speed);
      this.isActive = 1;
    },
    togglePause: function () {
      if (this.isActive === 1) {
        this.clearTimers();
        this.isActive = 0;
      } else {
        this.play();
      }
    },
    clearTimers: function () {
      clearTimeout(this.timer);
      clearTimeout(this.pTimer);
      this.timer = null;
      this.pTimer = null;
    },
    move: function (dir) {
      var s = '';
      var me = this;
      var tempX = this.curX;
      var tempY = this.curY;
      switch (dir) {
        case 'L':
          s = 'left';
          tempX -= 1;
          break;
        case 'R':
          s = 'left';
          tempX += 1;
          break;
        case 'D':
          s = 'top';
          tempY += 1;
          break;
        case 'RT':
          this.rotate();
          return true;
          break;
        default:
          throw new Error('wtf');
          break;
      }
      if (this.checkMove(tempX, tempY, this.curShape)) {
        this.curSqs.eachdo(function (i) {
          var l = parseInt(this.style[s], 10);
          dir === 'L' ? (l -= me.pSize) : (l += me.pSize);
          this.style[s] = l + 'px';
        });
        this.curX = tempX;
        this.curY = tempY;
      } else if (dir === 'D') {
        if (this.curY === 1 || this.time === this.maxTime) {
          this.gameOver();
          return false;
        }
        this.curComplete = true;
      }
    },
    rotate: function () {
      if (this.curShapeIndex !== 6) {
        //square
        var temp = [];
        this.curShape.eachdo(function () {
          temp.push([this[1] * -1, this[0]]);
        });
        if (this.checkMove(this.curX, this.curY, temp)) {
          this.curShape = temp;
          this.removeCur();
          this.drawShape(this.curX, this.curY, this.curShape);
        } else {
          throw new Error('Could not rotate!');
        }
      }
    },
    checkMove: function (x, y, p) {
      if (this.isOB(x, y, p) || this.isCollision(x, y, p)) {
        return false;
      }
      return true;
    },
    isCollision: function (x, y, p) {
      var me = this;
      var bool = false;
      p.eachdo(function () {
        var newX = this[0] + x;
        var newY = this[1] + y;
        if (me.boardPos(newX, newY) === 1) {
          bool = true;
        }
      });
      return bool;
    },
    isOB: function (x, y, p) {
      var w = this.boardWidth - 1;
      var h = this.boardHeight - 1;
      var bool = false;
      p.eachdo(function () {
        var newX = this[0] + x;
        var newY = this[1] + y;
        if (newX < 0 || newX > w || newY < 0 || newY > h) {
          bool = true;
        }
      });
      return bool;
    },
    getRowState: function (y) {
      var c = 0;
      for (var x = 0; x < this.boardWidth; x++) {
        if (this.boardPos(x, y) === 1) {
          c = c + 1;
        }
      }
      if (c === 0) {
        return 'E';
      }
      if (c === this.boardWidth) {
        return 'F';
      }
      return 'U';
    },
    checkRows: function () {
      var me = this;
      var start = this.boardHeight;
      this.curShape.eachdo(function () {
        var n = this[1] + me.curY;
        console.log(n);
        if (n < start) {
          start = n;
        }
      });
      console.log(start);

      var c = 0;
      var stopCheck = false;
      for (var y = this.boardHeight - 1; y >= 0; y--) {
        switch (this.getRowState(y)) {
          case 'F':
            this.removeRow(y);
            c++;
            break;
          case 'E':
            if (c === 0) {
              stopCheck = true;
            }
            break;
          case 'U':
            if (c > 0) {
              this.shiftRow(y, c);
            }
            break;
          default:
            break;
        }
        if (stopCheck === true) {
          break;
        }
      }
      if (c > 0) {
        this.calcScore({ lines: c });
      }
    },
    shiftRow: function (y, amount) {
      var me = this;
      for (var x = 0; x < this.boardWidth; x++) {
        this.sqs.eachdo(function () {
          if (me.isAt(x, y, this)) {
            me.setBlock(x, y + amount, this);
          }
        });
      }
      me.emptyBoardRow(y);
    },
    emptyBoardRow: function (y) {
      for (var x = 0; x < this.boardWidth; x++) {
        this.markBoardAt(x, y, 0);
      }
    },
    removeRow: function (y) {
      for (var x = 0; x < this.boardWidth; x++) {
        this.removeBlock(x, y);
      }
    },
    removeBlock: function (x, y) {
      var me = this;
      this.markBoardAt(x, y, 0);
      this.sqs.eachdo(function (i) {
        if (me.getPos(this)[0] === x && me.getPos(this)[1] === y) {
          me.canvas.removeChild(this);
          me.sqs.splice(i, 1);
        }
      });
    },
    setBlock: function (x, y, block) {
      this.markBoardAt(x, y, 1);
      var newX = x * this.pSize;
      var newY = y * this.pSize;
      block.style.left = newX + 'px';
      block.style.top = newY + 'px';
    },
    isAt: function (x, y, block) {
      if (this.getPos(block)[0] === x && this.getPos(block)[1] === y) {
        return true;
      }
      return false;
    },
    getPos: function (block) {
      var p = [];
      p.push(parseInt(block.style.left, 10) / this.pSize);
      p.push(parseInt(block.style.top, 10) / this.pSize);
      return p;
    },
    getBoardIdx: function (x, y) {
      return x + y * this.boardWidth;
    },
    boardPos: function (x, y) {
      return this.board[x + y * this.boardWidth];
    },
    markBoardAt: function (x, y, val) {
      this.board[this.getBoardIdx(x, y)] = val;
    },
    markBoardShape: function (x, y, p) {
      var me = this;
      p.eachdo(function (i) {
        var newX = p[i][0] + x;
        var newY = p[i][1] + y;
        me.markBoardAt(newX, newY, 1);
      });
    },
    isIE: function () {
      return this.bTest(/IE/);
    },
    isFirefox: function () {
      return this.bTest(/Firefox/);
    },
    isSafari: function () {
      return this.bTest(/Safari/);
    },
    bTest: function (rgx) {
      return rgx.test(navigator.userAgent);
    },
  };
  const btn = document.querySelector('#start');
  btn.addEventListener('click', function () {
    btn.style.display = 'none';
    if (!isStart) {
      tetris.init();
    }
  });
})();

if (!Array.prototype.eachdo) {
  Array.prototype.eachdo = function (fn) {
    for (var i = 0; i < this.length; i++) {
      fn.call(this[i], i);
    }
  };
}
if (!Array.prototype.remDup) {
  Array.prototype.remDup = function () {
    var temp = [];
    for (var i = 0; i < this.length; i++) {
      var bool = true;
      for (var j = i + 1; j < this.length; j++) {
        if (this[i] === this[j]) {
          bool = false;
        }
      }
      if (bool === true) {
        temp.push(this[i]);
      }
    }
    return temp;
  };
}
 
Has Preview = True
 
RobertHayton_0-1715616557927.png

 

RobertHayton_1-1715616574426.pngRobertHayton_2-1715616591689.png

 

 

 

 

 

 

5 REPLIES 5

Naomi5
Giga Guru

Whoaa! This is amazing!

Ishaan Shoor
Mega Sage
Mega Sage

Nice, instead of having the widget code here, I'd suggest you submit this as a contribution on the ServiceNow share so that others can download and play around 😀

Link to ServiceNow Share: My Projects - Share | ServiceNow Developers

Regards,

Ishaan 

Hope this helps!
BR.
Ishaan Shoor

shibasou
Kilo Sage

Great!!

TiagoTSilva
Tera Contributor

I was not being able to use my keyboards to move pieces. So made a small change to the script:

(function () {
var isStart = false;
var tetris = {
board: [],
boardDiv: null,
canvas: null,
pSize: 20,
canvasHeight: 440,
canvasWidth: 200,
boardHeight: 0,
boardWidth: 0,
spawnX: 4,
spawnY: 1,
shapes: [
[
[-1, 1],
[0, 1],
[1, 1],
[0, 0], //TEE
],
[
[-1, 0],
[0, 0],
[1, 0],
[2, 0], //line
],
[
[-1, -1],
[-1, 0],
[0, 0],
[1, 0], //L EL
],
[
[1, -1],
[-1, 0],
[0, 0],
[1, 0], //R EL
],
[
[0, -1],
[1, -1],
[-1, 0],
[0, 0], // R ess
],
[
[-1, -1],
[0, -1],
[0, 0],
[1, 0], //L ess
],
[
[0, -1],
[1, -1],
[0, 0],
[1, 0], //square
],
],
tempShapes: null,
curShape: null,
curShapeIndex: null,
curX: 0,
curY: 0,
curSqs: [],
nextShape: null,
nextShapeDisplay: null,
nextShapeIndex: null,
sqs: [],
score: 0,
scoreDisplay: null,
level: 1,
levelDisplay: null,
numLevels: 10,
time: 0,
maxTime: 1000,
timeDisplay: null,
isActive: 0,
curComplete: false,
timer: null,
sTimer: null,
speed: 700,
lines: 0,

init: function () {
isStart = true;
this.canvas = document.getElementById('canvas');
this.initBoard();
this.initInfo();
this.initLevelScores();
this.initShapes();
this.bindKeyEvents();
this.play();
},
initBoard: function () {
this.boardHeight = this.canvasHeight / this.pSize;
this.boardWidth = this.canvasWidth / this.pSize;
var s = this.boardHeight * this.boardWidth;
for (var i = 0; i < s; i++) {
this.board.push(0);
}
//this.boardDiv = document.getElementById('board); //for debugging
},
initInfo: function () {
this.nextShapeDisplay = document.getElementById('next_shape');
this.levelDisplay = document
.getElementById('level')
.getElementsByTagName('span')[0];
this.timeDisplay = document
.getElementById('time')
.getElementsByTagName('span')[0];
this.scoreDisplay = document
.getElementById('score')
.getElementsByTagName('span')[0];
this.linesDisplay = document
.getElementById('lines')
.getElementsByTagName('span')[0];
this.setInfo('time');
this.setInfo('score');
this.setInfo('level');
this.setInfo('lines');
},
initShapes: function () {
this.curSqs = [];
this.curComplete = false;
this.shiftTempShapes();
this.curShapeIndex = this.tempShapes[0];
this.curShape = this.shapes[this.curShapeIndex];
this.initNextShape();
this.setCurCoords(this.spawnX, this.spawnY);
this.drawShape(this.curX, this.curY, this.curShape);
},
initNextShape: function () {
if (typeof this.tempShapes[1] === 'undefined') {
this.initTempShapes();
}
try {
this.nextShapeIndex = this.tempShapes[1];
this.nextShape = this.shapes[this.nextShapeIndex];
this.drawNextShape();
} catch (e) {
throw new Error('Could not create next shape. ' + e);
}
},
initTempShapes: function () {
this.tempShapes = [];
for (var i = 0; i < this.shapes.length; i++) {
this.tempShapes.push(i);
}
var k = this.tempShapes.length;
while (--k) {
//Fisher Yates Shuffle
var j = Math.floor(Math.random() * (k + 1));
var tempk = this.tempShapes[k];
var tempj = this.tempShapes[j];
this.tempShapes[k] = tempj;
this.tempShapes[j] = tempk;
}
},
shiftTempShapes: function () {
try {
if (
typeof this.tempShapes === 'undefined' ||
this.tempShapes === null
) {
this.initTempShapes();
} else {
this.tempShapes.shift();
}
} catch (e) {
throw new Error('Could not shift or init tempShapes: ' + e);
}
},
initTimer: function () {
var me = this;
var tLoop = function () {
me.incTime();
me.timer = setTimeout(tLoop, 2000);
};
this.timer = setTimeout(tLoop, 2000);
},
initLevelScores: function () {
var c = 1;
for (var i = 1; i <= this.numLevels; i++) {
this['level' + i] = [c * 1000, 40 * i, 5 * i]; //for nxt level, row score, p sore,
c = c + c;
}
},
setInfo: function (el) {
this[el + 'Display'].innerHTML = this[el];
},
drawNextShape: function () {
var ns = [];
for (var i = 0; i < this.nextShape.length; i++) {
ns[i] = this.createSquare(
this.nextShape[i][0] + 2,
this.nextShape[i][1] + 2,
this.nextShapeIndex
);
}
this.nextShapeDisplay.innerHTML = '';
for (var k = 0; k < ns.length; k++) {
this.nextShapeDisplay.appendChild(ns[k]);
}
},
drawShape: function (x, y, p) {
for (var i = 0; i < p.length; i++) {
var newX = p[i][0] + x;
var newY = p[i][1] + y;
this.curSqs[i] = this.createSquare(newX, newY, this.curShapeIndex);
}
for (var k = 0; k < this.curSqs.length; k++) {
this.canvas.appendChild(this.curSqs[k]);
}
},
createSquare: function (x, y, type) {
var el = document.createElement('div');
el.className = 'square type' + type;
el.style.left = x * this.pSize + 'px';
el.style.top = y * this.pSize + 'px';
return el;
},
removeCur: function () {
var me = this;
this.curSqs.eachdo(function () {
me.canvas.removeChild(this);
});
this.curSqs = [];
},
setCurCoords: function (x, y) {
this.curX = x;
this.curY = y;
},
bindKeyEvents: function () {
var me = this;
var event = 'keydown'; // Changed from 'keypress' to 'keydown'
var cb = function (e) {
me.handleKey(e);
};
if (window.addEventListener) {
document.addEventListener(event, cb, false);
} else {
document.attachEvent('on' + event, cb);
}
},
handleKey: function (e) {
var c = this.whichKey(e);
switch (c) {
case 37: // Left arrow
this.move('L');
break;
case 38: // Up arrow
this.move('RT');
break;
case 39: // Right arrow
this.move('R');
break;
case 40: // Down arrow
this.move('D');
break;
case 27: // Esc
this.togglePause();
break;
default:
break;
}
},
whichKey: function (e) {
return e.keyCode || e.which;
},
incTime: function () {
this.time++;
this.setInfo('time');
},
incScore: function (amount) {
this.score = this.score + amount;
this.setInfo('score');
},
incLevel: function () {
this.level++;
this.speed = this.speed - 75;
this.setInfo('level');
},
incLines: function (num) {
this.lines += num;
this.setInfo('lines');
},
calcScore: function (args) {
var lines = args.lines || 0;
var shape = args.shape || false;
var speed = args.speed || 0;
var score = 0;

if (lines > 0) {
score += lines * this['level' + this.level][1];
this.incLines(lines);
}
if (shape === true) {
score += shape * this['level' + this.level][2];
}
/*if (speed > 0){ score += speed * this["level" +this .level[3]];}*/
this.incScore(score);
},
checkScore: function () {
if (this.score >= this['level' + this.level][0]) {
this.incLevel();
}
},
gameOver: function () {
this.clearTimers();
isStart = false;
this.canvas.innerHTML = '<h1>GAME OVER</h1>';
},
play: function () {
var me = this;
if (this.timer === null) {
this.initTimer();
}
var gameLoop = function () {
me.move('D');
if (me.curComplete) {
me.markBoardShape(me.curX, me.curY, me.curShape);
me.curSqs.eachdo(function () {
me.sqs.push(this);
});
me.calcScore({ shape: true });
me.checkRows();
me.checkScore();
me.initShapes();
me.play();
} else {
me.pTimer = setTimeout(gameLoop, me.speed);
}
};
this.pTimer = setTimeout(gameLoop, me.speed);
this.isActive = 1;
},
togglePause: function () {
if (this.isActive === 1) {
this.clearTimers();
this.isActive = 0;
} else {
this.play();
}
},
clearTimers: function () {
clearTimeout(this.timer);
clearTimeout(this.pTimer);
this.timer = null;
this.pTimer = null;
},
move: function (dir) {
var s = '';
var me = this;
var tempX = this.curX;
var tempY = this.curY;
switch (dir) {
case 'L':
s = 'left';
tempX -= 1;
break;
case 'R':
s = 'left';
tempX += 1;
break;
case 'D':
s = 'top';
tempY += 1;
break;
case 'RT':
this.rotate();
return true;
break;
default:
throw new Error('wtf');
break;
}
if (this.checkMove(tempX, tempY, this.curShape)) {
this.curSqs.eachdo(function (i) {
var l = parseInt(this.style[s], 10);
dir === 'L' ? (l -= me.pSize) : (l += me.pSize);
this.style[s] = l + 'px';
});
this.curX = tempX;
this.curY = tempY;
} else if (dir === 'D') {
if (this.curY === 1 || this.time === this.maxTime) {
this.gameOver();
return false;
}
this.curComplete = true;
}
},
rotate: function () {
if (this.curShapeIndex !== 6) {
//square
var temp = [];
this.curShape.eachdo(function () {
temp.push([this[1] * -1, this[0]]);
});
if (this.checkMove(this.curX, this.curY, temp)) {
this.curShape = temp;
this.removeCur();
this.drawShape(this.curX, this.curY, this.curShape);
} else {
throw new Error('Could not rotate!');
}
}
},
checkMove: function (x, y, p) {
if (this.isOB(x, y, p) || this.isCollision(x, y, p)) {
return false;
}
return true;
},
isCollision: function (x, y, p) {
var me = this;
var bool = false;
p.eachdo(function () {
var newX = this[0] + x;
var newY = this[1] + y;
if (me.boardPos(newX, newY) === 1) {
bool = true;
}
});
return bool;
},
isOB: function (x, y, p) {
var w = this.boardWidth - 1;
var h = this.boardHeight - 1;
var bool = false;
p.eachdo(function () {
var newX = this[0] + x;
var newY = this[1] + y;
if (newX < 0 || newX > w || newY < 0 || newY > h) {
bool = true;
}
});
return bool;
},
getRowState: function (y) {
var c = 0;
for (var x = 0; x < this.boardWidth; x++) {
if (this.boardPos(x, y) === 1) {
c = c + 1;
}
}
if (c === 0) {
return 'E';
}
if (c === this.boardWidth) {
return 'F';
}
return 'U';
},
checkRows: function () {
var me = this;
var start = this.boardHeight;
this.curShape.eachdo(function () {
var n = this[1] + me.curY;
console.log(n);
if (n < start) {
start = n;
}
});
console.log(start);

var c = 0;
var stopCheck = false;
for (var y = this.boardHeight - 1; y >= 0; y--) {
switch (this.getRowState(y)) {
case 'F':
this.removeRow(y);
c++;
break;
case 'E':
if (c === 0) {
stopCheck = true;
}
break;
case 'U':
if (c > 0) {
this.shiftRow(y, c);
}
break;
default:
break;
}
if (stopCheck === true) {
break;
}
}
if (c > 0) {
this.calcScore({ lines: c });
}
},
shiftRow: function (y, amount) {
var me = this;
for (var x = 0; x < this.boardWidth; x++) {
this.sqs.eachdo(function () {
if (me.isAt(x, y, this)) {
me.setBlock(x, y + amount, this);
}
});
}
me.emptyBoardRow(y);
},
emptyBoardRow: function (y) {
for (var x = 0; x < this.boardWidth; x++) {
this.markBoardAt(x, y, 0);
}
},
removeRow: function (y) {
for (var x = 0; x < this.boardWidth; x++) {
this.removeBlock(x, y);
}
},
removeBlock: function (x, y) {
var me = this;
this.markBoardAt(x, y, 0);
this.sqs.eachdo(function (i) {
if (me.getPos(this)[0] === x && me.getPos(this)[1] === y) {
me.canvas.removeChild(this);
me.sqs.splice(i, 1);
}
});
},
setBlock: function (x, y, block) {
this.markBoardAt(x, y, 1);
var newX = x * this.pSize;
var newY = y * this.pSize;
block.style.left = newX + 'px';
block.style.top = newY + 'px';
},
isAt: function (x, y, block) {
if (this.getPos(block)[0] === x && this.getPos(block)[1] === y) {
return true;
}
return false;
},
getPos: function (block) {
var p = [];
p.push(parseInt(block.style.left, 10) / this.pSize);
p.push(parseInt(block.style.top, 10) / this.pSize);
return p;
},
getBoardIdx: function (x, y) {
return x + y * this.boardWidth;
},
boardPos: function (x, y) {
return this.board[x + y * this.boardWidth];
},
markBoardAt: function (x, y, val) {
this.board[this.getBoardIdx(x, y)] = val;
},
markBoardShape: function (x, y, p) {
var me = this;
p.eachdo(function (i) {
var newX = p[i][0] + x;
var newY = p[i][1] + y;
me.markBoardAt(newX, newY, 1);
});
},
isIE: function () {
return this.bTest(/IE/);
},
isFirefox: function () {
return this.bTest(/Firefox/);
},
isSafari: function () {
return this.bTest(/Safari/);
},
bTest: function (rgx) {
return rgx.test(navigator.userAgent);
},
};
const btn = document.querySelector('#start');
btn.addEventListener('click', function () {
btn.style.display = 'none';
if (!isStart) {
tetris.init();
}
});

if (!Array.prototype.eachdo) {
Array.prototype.eachdo = function (fn) {
for (var i = 0; i < this.length; i++) {
fn.call(this[i], i);
}
};
}
if (!Array.prototype.remDup) {
Array.prototype.remDup = function () {
var temp = [];
for (var i = 0; i < this.length; i++) {
var bool = true;
for (var j = i + 1; j < this.length; j++) {
if (this[i] === this[j]) {
bool = false;
}
}
if (bool === true) {
temp.push(this[i]);
}
}
return temp;
};
}
})();