var consts = require('./consts.js'); var COLORS = consts.COLORS; var COLUMN_COUNT = consts.COLUMN_COUNT; /** Defined all shapes used in Tetris game. You can add more shapes if you wish. */ function ShapeL() { var state1 = [ [1, 0], [1, 0], [1, 1] ]; var state2 = [ [0, 0, 1], [1, 1, 1] ]; var state3 = [ [1, 1], [0, 1], [0, 1] ]; var state4 = [ [1, 1, 1], [1, 0, 0] ]; this.states = [state1, state2, state3, state4]; this.x = 4; this.y = -3; this.originY = -3; this.flag = 'L'; } function ShapeLR() { var state1 = [ [0, 1], [0, 1], [1, 1] ]; var state2 = [ [1, 1, 1], [0, 0, 1] ]; var state3 = [ [1, 1], [1, 0], [1, 0] ]; var state4 = [ [1, 0, 0], [1, 1, 1] ]; this.states = [state1, state2, state3, state4]; this.x = 4; this.y = -3; this.originY = -3; this.flag = 'LR'; } function ShapeO() { var state1 = [ [1, 1], [1, 1] ]; this.states = [state1]; this.x = 4; this.y = -2; this.originY = -2; this.flag = 'O'; } function ShapeI() { var state1 = [ [1], [1], [1], [1] ]; var state2 = [ [1, 1, 1, 1] ]; this.states = [state1, state2]; this.x = 5; this.y = -4; this.originY = -4; this.flag = 'I'; } function ShapeT() { var state1 = [ [1, 1, 1], [0, 1, 0] ]; var state2 = [ [1, 0], [1, 1], [1, 0] ]; var state3 = [ [0, 1, 0], [1, 1, 1] ]; var state4 = [ [0, 1], [1, 1], [0, 1] ]; this.states = [state1, state2, state3, state4]; this.x = 4; this.y = -2; this.originY = -2; this.flag = 'T'; } function ShapeZ() { var state1 = [ [1, 1, 0], [0, 1, 1] ]; var state2 = [ [0, 1], [1, 1], [1, 0] ]; this.states = [state1, state2]; this.x = 4; this.y = -2; this.originY = -2; this.flag = 'Z'; } function ShapeZR() { var state1 = [ [0, 1, 1], [1, 1, 0] ]; var state2 = [ [1, 0], [1, 1], [0, 1] ]; this.states = [state1, state2]; this.x = 4; this.y = -2 this.originY = -2; this.flag = 'ZR'; } /** Is shape can move @param shape: tetris shape @param matrix: game matrix @param action: 'left','right','down','rotate' */ var isShapeCanMove = function(shape, matrix, action) { var rows = matrix.length; var cols = matrix[0].length; var rotationDirection = 0; var isBoxCanMove = function(box) { var x = shape.x + box.x; var y = shape.y + box.y; if (y < 0) { return true; } if (action === 'left') { x -= 1; return x >= 0 && x < cols && matrix[y][x] == 0; } else if (action === 'right') { x += 1; return x >= 0 && x < cols && matrix[y][x] == 0; } else if (action === 'down') { y += 1; return y < rows && matrix[y][x] == 0; } else if (action === 'rotate') { rotationDirection = 1; return y < rows && !matrix[y][x]; } else if (action === 'rotateclockwise') { rotationDirection = -1; return y < rows && !matrix[y][x]; } }; //var boxes = action === 'rotate'?shape.getBoxes(shape.nextState()) : shape.getBoxes(shape.state); var boxes; if(rotationDirection != 0) boxes = shape.getBoxes(shape.nextState(rotationDirection)); else boxes = shape.getBoxes(shape.state); for (var i in boxes) { if (!isBoxCanMove(boxes[i])) { return false; } } return true; }; /** All shapes shares the same method, use prototype for memory optimized */ ShapeL.prototype = ShapeLR.prototype = ShapeO.prototype = ShapeI.prototype = ShapeT.prototype = ShapeZ.prototype = ShapeZR.prototype = { init: function(result) { this.color = COLORS[result]; this.state = 0; this.allBoxes = {}; this.y = 0; }, // Get boxes matrix which composite the shape getBoxes: function(state) { var boxes = this.allBoxes[state] || []; if (boxes.length) { return boxes; } var matrix = this.matrix(state); for (var i = 0; i < matrix.length; i++) { var row = matrix[i]; for (var j = 0; j < row.length; j++) { if (row[j] === 1) { boxes.push({ x: j, y: i }); } } } this.allBoxes[state] = boxes; return boxes; }, //Get matrix for specified state matrix: function(state) { var st = state !== undefined ? state : this.state; return this.states[st]; }, //Rotate shape rotate: function(matrix) { if (isShapeCanMove(this,matrix,'rotate')){ this.state = this.nextState(1); //fix position if shape is out of right border var right = this.getRight(); if ( right >= COLUMN_COUNT){ this.x -= right - COLUMN_COUNT + 1; } /*var left = this.getLeft(); if(left <= 0) this.x += 1;*/ } }, //Rotate shape clockwise rotateClockwise: function(matrix) { if (isShapeCanMove(this, matrix, 'rotateclockwise')) { this.state = this.nextState(-1); //fix position if shape is out of right border var right = this.getRight(); if (right >= COLUMN_COUNT) { this.x -= right - COLUMN_COUNT + 1; } } }, //Caculate the max column of the shape getColumnCount: function() { var mtx = this.matrix(); var colCount = 0; for (var i = 0; i < mtx.length; i++) { colCount = Math.max(colCount, mtx[i].length); } return colCount; }, //Caculate the max row of the shape getRowCount: function() { return this.matrix().length; }, //Get the right pos of the shape getRight: function() { var boxes = this.getBoxes(this.state); var right = 0; for (var i in boxes) { right = Math.max(boxes[i].x, right); } return this.x + right; }, //Return the next state of the shape nextState: function(direction) { var rotate = this.state; rotate += direction; if(rotate < 0) return this.states.length - 1; return rotate % this.states.length; }, //Check if the shape can move down canDown: function(matrix) { return isShapeCanMove(this, matrix, 'down'); }, //Move the shape down goDown: function(matrix) { if (isShapeCanMove(this, matrix, 'down')) { this.y += 1; } }, //Move the shape to the Bottommost bottomAt: function(matrix) { var save = this.y; var ret; while (isShapeCanMove(this, matrix, 'down')) { this.y += 1; } ret = this.y; this.y = save; return ret; }, //Move the shape to the Bottommost goBottom: function(matrix) { while (isShapeCanMove(this, matrix, 'down')) { this.y += 1; } }, //Move the shape to the left goLeft: function(matrix) { if (isShapeCanMove(this, matrix, 'left')) { this.x -= 1; } }, //Move the shape to the right goRight: function(matrix) { if (isShapeCanMove(this, matrix, 'right')) { this.x += 1; } }, //Copy the shape data to the game data copyTo: function(matrix) { var smatrix = this.matrix(); for (var i = 0; i < smatrix.length; i++) { var row = smatrix[i]; for (var j = 0; j < row.length; j++) { if (row[j] === 1) { var x = this.x + j; var y = this.y + i; if (x >= 0 && x < matrix[0].length && y >= 0 && y < matrix.length) { matrix[y][x] = this.color; } } } } }, resetOrigin: function() { this.y = this.originY + 1; } } /** Create a random shape for game */ // Handles randomly generating and returning a tetromino var RandomGenerator = { bag: [], getTetrimino() { if (this.bag.length === 0) { this.bag = this.generateNewBag(); } return this.bag.shift(); }, generateNewBag() { //var tetrominoes = ['I', 'J', 'L', 'O', 'S', 'T', 'Z']; var tetrominoes = ['0', '1', '2', '3', '4', '5', '6']; //var tetrominoes = ['L','L','L','L','L','L','L',]; var bag = []; for (var i = 7; i > 0; i--) { var tetrominoIndex = Math.floor(Math.random() * i); bag.push(tetrominoes[tetrominoIndex]); tetrominoes.splice(tetrominoIndex, 1); } return bag; } }; function randomShape() { var result = parseInt(RandomGenerator.getTetrimino(),10);//Math.floor(Math.random() * 7); var shape; switch (result) { case 0: shape = new ShapeL(); break; case 1: shape = new ShapeO(); break; case 2: shape = new ShapeZ(); break; case 3: shape = new ShapeT(); break; case 4: shape = new ShapeLR(); break; case 5: shape = new ShapeZR(); break; case 6: shape = new ShapeI(); break; } shape.init(result); return shape; } module.exports.randomShape = randomShape;