diff --git a/dist/_tetris.js b/dist/_tetris.js deleted file mode 100644 index dd30e84..0000000 --- a/dist/_tetris.js +++ /dev/null @@ -1,1103 +0,0 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=0;i--){ - for(var j = 0;j0){ - matrix[i][j] = matrix[i-1][j]; - }else{ - matrix[i][j] = 0 ; - } - } - } -}; -/** - Remove rows from game matrix by row numbers. -*/ -var removeRows = function(matrix,rows){ - for(var i in rows){ - removeOneRow(matrix,rows[i]); - } -}; - -/** - Check game data to determin wether the game is over -*/ -var checkGameOver = function(matrix){ - var firstRow = matrix[0]; - for(var i = 0;i1){ - return Math.pow(2,rows.length - 1)*100; - } - return 0; -}; - -/** - Calculate game score -*/ -var calcScore = function(rows){ - if (rows&&rows.length){ - return rows.length*100; - } - return 0; -}; - -/** - Calculate time interval by level, the higher the level,the faster shape moves -*/ -var calcIntervalByLevel = function(level){ - return consts.DEFAULT_INTERVAL - (level-1)*60; -}; - - -// Default max scene size -var defaults = { - maxHeight:700, - maxWidth:600 -}; - -/** - Tetris main object defination -*/ -function Tetris(id){ - this.id = id; - this.init(); -} - -Tetris.prototype = { - - init:function(options){ - - var cfg = this.config = utils.extend(options,defaults); - this.interval = consts.DEFAULT_INTERVAL; - - - views.init(this.id, cfg.maxWidth,cfg.maxHeight); - - canvas.init(views.scene,views.preview); - - this.matrix = initMatrix(consts.ROW_COUNT,consts.COLUMN_COUNT); - this.reset(); - - this._initEvents(); - this._fireShape(); - - - }, - //Reset game - reset:function(){ - this.running = false; - this.isGameOver = false; - this.level = 1; - this.score = 0; - this.startTime = new Date().getTime(); - this.currentTime = this.startTime; - this.prevTime = this.startTime; - this.levelTime = this.startTime; - clearMatrix(this.matrix); - views.setLevel(this.level); - views.setScore(this.score); - views.setGameOver(this.isGameOver); - this._draw(); - }, - //Start game - start:function(){ - this.running = true; - window.requestAnimationFrame(utils.proxy(this._refresh,this)); - }, - //Pause game - pause:function(){ - this.running = false; - this.currentTime = new Date().getTime(); - this.prevTime = this.currentTime; - }, - //Game over - gamveOver:function(){ - - }, - // All key event handlers - _keydownHandler:function(e){ - - var matrix = this.matrix; - - if(!e) { - var e = window.event; - } - if (this.isGameOver||!this.shape){ - return; - } - - switch(e.keyCode){ - case 37:{this.shape.goLeft(matrix);this._draw();} - break; - - case 39:{this.shape.goRight(matrix);this._draw();} - break; - - case 38:{this.shape.rotate(matrix);this._draw();} - break; - - case 40:{this.shape.goDown(matrix);this._draw();} - break; - - case 32:{this.shape.goBottom(matrix);this._update();} - break; - } - }, - // Restart game - _restartHandler:function(){ - this.reset(); - this.start(); - }, - // Bind game events - _initEvents:function(){ - window.addEventListener('keydown',utils.proxy(this._keydownHandler,this),false); - views.btnRestart.addEventListener('click',utils.proxy(this._restartHandler,this),false); - }, - - // Fire a new random shape - _fireShape:function(){ - this.shape = this.preparedShape||shapes.randomShape(); - this.preparedShape = shapes.randomShape(); - this._draw(); - canvas.drawPreviewShape(this.preparedShape); - }, - - // Draw game data - _draw:function(){ - canvas.drawScene(); - canvas.drawShape(this.shape); - canvas.drawMatrix(this.matrix); - }, - // Refresh game canvas - _refresh:function(){ - if (!this.running){ - return; - } - this.currentTime = new Date().getTime(); - if (this.currentTime - this.prevTime > this.interval ){ - this._update(); - this.prevTime = this.currentTime; - this._checkLevel(); - } - if (!this.isGameOver){ - window.requestAnimationFrame(utils.proxy(this._refresh,this)); - } - }, - // Update game data - _update:function(){ - if (this.shape.canDown(this.matrix)){ - this.shape.goDown(this.matrix); - }else{ - this.shape.copyTo(this.matrix); - this._check(); - this._fireShape(); - } - this._draw(); - this.isGameOver = checkGameOver(this.matrix); - views.setGameOver(this.isGameOver); - if (this.isGameOver){ - views.setFinalScore(this.score); - } - }, - // Check and update game data - _check:function(){ - var rows = checkFullRows(this.matrix); - if (rows.length){ - removeRows(this.matrix,rows); - - var score = calcScore(rows); - var reward = calcRewards(rows); - this.score += score + reward; - - views.setScore(this.score); - views.setReward(reward); - - } - }, - // Check and update game level - _checkLevel:function(){ - var currentTime = new Date().getTime(); - if (currentTime - this.levelTime > consts.LEVEL_INTERVAL){ - this.level+=1; - this.interval = calcIntervalByLevel(this.level); - views.setLevel(this.level); - this.levelTime = currentTime; - } - } -} - - -window.Tetris = Tetris; - - - - - - -},{"./canvas.js":1,"./consts.js":2,"./shapes.js":4,"./utils.js":5,"./views.js":6}],4:[function(require,module,exports){ -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.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.flag = 'LR'; -} - -function ShapeO() -{ - - var state1 = [ [1, 1], - [1, 1] ]; - - - this.states = [ state1 ]; - this.x = 4; - this.y = -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.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.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.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.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 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=0 && x= 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=0&&x=0&&ydh){ - size.height = Math.min(maxH,dh); - size.width = Math.min(size.height /2 + SIDE_WIDTH,maxW); - }else{ - size.width = Math.min(maxW,dw); - size.height = Math.min(maxH,dh); - } - return size; - -}; - - -/** - Layout game elements -*/ -var layoutView = function(container,maxW,maxH){ - var size = getContainerSize(maxW,maxH); - var st = container.style; - st.height = size.height + 'px'; - st.width = size.width + 'px'; - st.marginTop = (-(size.height/2)) + 'px'; - st.marginLeft = (-(size.width/2)) + 'px'; - - //layout scene - scene.height = size.height; - scene.width = scene.height / 2; - - var sideW = size.width - scene.width; - side.style.width = sideW+ 'px'; - if (sideW< SIDE_WIDTH ){ - info.style.width = side.style.width; - } - preview.width = 80; - preview.height = 80; - - gameOver.style.width = scene.width +'px'; - -} - -/** - Main tetris game view -*/ -var tetrisView = { - - - init:function(id, maxW,maxH){ - this.container = $(id); - this.scene = scene; - this.preview = preview; - this.btnRestart = btnRestart; - layoutView(this.container,maxW,maxH); - this.scene.focus(); - - rewardInfo.addEventListener('animationEnd',function(e){ - rewardInfo.className = 'invisible'; - }); - }, - // Update the score - setScore:function(scoreNumber){ - score.innerHTML = scoreNumber; - }, - // Update the finnal score - setFinalScore:function(scoreNumber){ - finalScore.innerHTML = scoreNumber; - }, - // Update the level - setLevel:function(levelNumber){ - level.innerHTML = levelNumber; - }, - // Update the extra reward score - setReward:function(rewardScore){ - if (rewardScore>0){ - reward.innerHTML = rewardScore; - rewardInfo.className = 'fadeOutUp animated'; - }else{ - rewardInfo.className = 'invisible'; - } - }, - // Set game over view - setGameOver:function(isGameOver){ - gameOver.style.display = isGameOver?'block':'none'; - } -}; - -module.exports = tetrisView; -},{"./consts.js":2,"./utils.js":5}]},{},[3]); diff --git a/dist/tetrion.js b/dist/tetrion.js index 6c50754..d711740 100644 --- a/dist/tetrion.js +++ b/dist/tetrion.js @@ -2,7 +2,8 @@ var utils = require('./utils.js'); var consts = require('./consts.js'); - +// import * as utils from './utils.js'; +// import * as consts from './const.js'; var lineColor = consts.GRID_LINE_COLOR; @@ -256,6 +257,8 @@ var tetrisCanvas = { module.exports = tetrisCanvas; +// export tetrisCanvas; + },{"./consts.js":2,"./utils.js":8}],2:[function(require,module,exports){ //colors for shapes L, O, Z, T, J, S, I @@ -417,8 +420,10 @@ window.addEventListener("gamepadconnected", gamepadAPI.connect); window.addEventListener("gamepaddisconnected", gamepadAPI.disconnect); module.exports = gamepadAPI; +// export gamepadAPI; },{}],4:[function(require,module,exports){ var gamepad = require('./gamepad.js'); +// import * as gamepad from './gamepad.js'; var UserInputs = { init() { @@ -501,7 +506,7 @@ var UserInputs = { // Direction Pad gamepadDPadDown(finds) { - var DAS = 80.0; + var DAS = 65.0; var ARR = 20.0; var isContained = this.gpButtons.includes(finds); var isPrevContained = this.prevGpButtons.includes(finds); @@ -581,7 +586,7 @@ var UserInputs = { }, // Direction arrows processKeyboardArrowKeys(key) { - var DAS = 80.0; + var DAS = 65.0; var ARR = 20.0; @@ -661,6 +666,7 @@ var UserInputs = { }; module.exports = UserInputs; +// export UserInputs; },{"./gamepad.js":3}],5:[function(require,module,exports){ var utils = require('./utils.js'); var consts = require('./consts.js'); @@ -669,7 +675,14 @@ var views = require('./views.js'); var canvas = require('./canvas.js'); var inputs = require('./input.js'); var openers = require('./openers.js'); - +// import * as utils from './utils.js'; +// import * as consts from './const.js'; +// import * as shapes from './shapes.js'; +// import * as views from './views.js'; +// import * as canvas from './canvas.js'; +// import * as inputs from './input.js'; +// import * as openers from './openers.js'; +//import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r125/build/three.module.js'; /** Init game matrix @@ -805,9 +818,6 @@ function Tetris(id) { Tetris.prototype = { init: function(options) { - - // Initialize function calls - //document.getElementById("TKIFonzieVar").onclick = function() { this._restartHandler() }; var cfg = this.config = utils.extend(options, defaults); this.interval = consts.DEFAULT_INTERVAL; @@ -817,30 +827,40 @@ Tetris.prototype = { canvas.init(views.scene, views.preview, views.hold); inputs.init(); + // if true no openers. just random tetrinos + this.isFreePlay = true; this.currentOpener = 0; this.matrix = initMatrix(consts.ROW_COUNT, consts.COLUMN_COUNT); this.reset(); - setInterval(() => {this._processInput();}, 1); + setInterval(() => {this._processTick();}, 1); this._initEvents(); this._fireShape(); }, + setFreePlay: function() + { + this.isFreePlay = true; + this.hintQueue = []; + this.shapeQueue = []; + this.hintMino = 0; + this._restartHandler(); + this.currentOpener = 0; + this._fireShape(); + }, setTKIFonzieVar: function() { + this.isFreePlay = false; this._restartHandler(); this.currentOpener = 1; this._fireShape(); - - //this._update(); }, setDTCannonVar: function() { + this.isFreePlay = false; this._restartHandler(); this.currentOpener = 2; this._fireShape(); - - //this._update(); }, //Reset game reset: function() { @@ -849,16 +869,24 @@ Tetris.prototype = { this.level = 1; this.score = 0; this.lines = 0; + // beginning of frame this.startTime = new Date().getTime(); this.currentTime = this.startTime; this.prevTime = this.startTime; + //todo:get rid of extra this.levelTime = this.startTime; this.prevInputTime = this.startTime; + // current tetrino index gets set to 0 at the end of opener sequence this.currentMinoInx = 0; this.shapeQueue = []; this.hintQueue = []; this.holdQueue = []; + // gets set to false after mino has been popped from hold stack; set back to true on mino dropped this.canPullFromHoldQueue = false; + // rotation counter for srs extended piece lockdown + this.rotationCounter = 0; + // timer for srs extened piece lockdown + this.pieceTimer = 0; clearMatrix(this.matrix); views.setLevel(this.level); views.setScore(this.score); @@ -902,14 +930,7 @@ Tetris.prototype = { this._draw(); } }, - //Game over - gamveOver: function() { - }, - - // All key event handlers - _keydownHandler: function(e) { - }, // Restart game _restartHandler: function() { this.reset(); @@ -917,38 +938,45 @@ Tetris.prototype = { }, // Bind game events _initEvents: function() { - //window.addEventListener('keydown', utils.proxy(this._keydownHandler, this), false); views.btnRestart.addEventListener('click', utils.proxy(this._restartHandler, this), false); }, // Fire a new random shape _fireShape: function() { - //this.shape = this.shapeQueue.shift() || shapes.randomShape(); - - - - - while(this.shapeQueue.length <= 4) - { - this.preparedShape = openers.getNextMino(this.currentOpener); - this.shapeQueue.push(this.preparedShape); - } - while(this.hintQueue.length <= 4) - { - this.preparedShape = openers.getNextHint(this.currentOpener); - this.hintQueue.push(this.preparedShape); - } - this.hintMino = this.hintQueue.shift(); - this.shape = this.shapeQueue.shift();// shapes.randomShape(); - - this.currentMinoInx++; - - if(this.currentMinoInx > openers.getLength()) { - this.hintQueue = []; - this.shapeQueue = []; - this._restartHandler(); - this._fireShape(); + + if(this.isFreePlay == false) { + while(this.shapeQueue.length <= 4) + { + this.preparedShape = openers.getNextMino(this.currentOpener); + this.shapeQueue.push(this.preparedShape); + } + while(this.hintQueue.length <= 4) + { + this.preparedShape = openers.getNextHint(this.currentOpener); + this.hintQueue.push(this.preparedShape); + } + + this.hintMino = this.hintQueue.shift(); + this.shape = this.shapeQueue.shift();// shapes.randomShape(); + + this.currentMinoInx++; + + if(this.currentMinoInx > openers.getLength()) { + this.hintQueue = []; + this.shapeQueue = []; + this._restartHandler(); + this._fireShape(); + } + } else { + while(this.shapeQueue.length <= 4) + { + this.preparedShape = shapes.randomShape(); + this.shapeQueue.push(this.preparedShape); + } + + this.shape = this.shapeQueue.shift() || randomShape(); + this.currentMinoInx++; } // Reset matrix at successful end of opener @@ -977,12 +1005,8 @@ Tetris.prototype = { } canvas.drawMatrix(this.matrix); }, - // Render Shape - _renderShape: function() - { - this._draw(); - }, - _processInput: async function() { + + _processTick: async function() { var deltaTime = 1.0; // 1 millisecond var tenthOfFrame = 1.0//1;//1.6; // 1.6ms = 1 fram @@ -1007,26 +1031,25 @@ Tetris.prototype = { var curkey = inputs.gamepadQueue.shift(); if(curkey == "DPad-Left") { this.shape.goLeft(this.matrix); - this._renderShape(); + this._draw(); } if(curkey == "DPad-Right") { this.shape.goRight(this.matrix); - this._renderShape(); + this._draw(); } if(curkey == "A") { + this.rotationCounter++; this.shape.rotate(this.matrix); - this._renderShape(); - //this._draw(); + this._draw(); } if(curkey == "B") { + this.rotationCounter++; this.shape.rotateClockwise(this.matrix); - this._renderShape(); - //this._draw(); + this._draw(); } if(curkey == "DPad-Down") { this.shape.goDown(this.matrix); - this._renderShape(); - //this._draw(); + this._draw(); } if(curkey == "RB") { this.shape.goBottom(this.matrix); @@ -1034,17 +1057,13 @@ Tetris.prototype = { } if(curkey == "LB") { this.pushHoldStack(); - //this._renderShape(); this._update(); } if(curkey == "DPad-Up") { this.popHoldStack(); - //this._renderShape(); this._update(); } if(curkey == "Back") { - // this.hintMino = [] ? - // this.shape = [] this._restartHandler(); return; } @@ -1067,47 +1086,45 @@ Tetris.prototype = { var curkey = inputs.inputqueue.shift(); if(curkey == 37) { this.shape.goLeft(this.matrix); - this._renderShape(); + this._draw(); } if(curkey == 39){ this.shape.goRight(this.matrix); - this._renderShape(); + this._draw(); } if(curkey == 40) { this.shape.goDown(this.matrix); - this._renderShape(); + this._draw(); } if(curkey == 90) { + this.rotationCounter++; this.shape.rotate(this.matrix); - this._renderShape(); + this._draw(); } if(curkey == 88){ + this.rotationCounter++; this.shape.rotateClockwise(this.matrix);; - this._renderShape(); + this._draw(); } if(curkey == 32) { this.shape.goBottom(this.matrix); - //this._renderShape(); this._update(); } if(curkey == 16) { this.pushHoldStack(); - //this._renderShape(); this._update(); } if(curkey == 17) { this.popHoldStack(); - //this._renderShape(); this._update(); } if(curkey == 81) { - if(document.getElementById("bg").style.display == "none") - document.getElementById("bg").style.display = "initial"; + if(document.getElementById("divbg").style.display == "none") + document.getElementById("divbg").style.display = "initial"; else - document.getElementById("bg").style.display="none"; + document.getElementById("divbg").style.display="none"; } if(curkey == 82) { - views.setGameOver(true); this._restartHandler(); return; } @@ -1156,6 +1173,8 @@ Tetris.prototype = { }, _checkHint: function() { + if(this.isFreePlay) + return; if(!this.shape.isSameSRS(this.hintMino)) { new Audio('./dist/Failed.ogg').play(); @@ -1230,8 +1249,8 @@ Tetris.prototype = { var rows = checkFullRows(this.matrix); if (rows.length) { var tspinType; - if(rows.length >= 4) - new Audio('./dist/Tetris.ogg').play(); + // if(rows.length >= 4) + // new Audio('./dist/Tetris.ogg').play(); if(this.shape.flag === 'T') tspinType = this._tSpinType(this.shape, this.matrix); @@ -1263,8 +1282,10 @@ Tetris.prototype = { window.Tetris = Tetris; +// export {Tetris}; },{"./canvas.js":1,"./consts.js":2,"./input.js":4,"./openers.js":6,"./shapes.js":7,"./utils.js":8,"./views.js":9}],6:[function(require,module,exports){ var shapes = require("./shapes.js"); +// import * as shapes from './shapes.js'; var OpenerGenerator = { shapeQueue: [], @@ -1346,7 +1367,6 @@ var OpenerGenerator = { shapes.getShape(2), shapes.getShape(4), shapes.getShape(3)); - // L this.hintQueue[0].x = -1; this.hintQueue[0].y = 17; @@ -1502,10 +1522,16 @@ module.exports.getNextMino = getNextMino; module.exports.getNextHint = getNextHint; module.exports.getLength = getLength; module.exports.reset = reset; +// export getNextMino; +// export getNextHint; +// export getLength; +// export reset; + },{"./shapes.js":7}],7:[function(require,module,exports){ var consts = require('./consts.js'); +// import * as consts from './const.js'; var COLORS = consts.COLORS; var COLUMN_COUNT = consts.COLUMN_COUNT; @@ -1635,7 +1661,7 @@ function ShapeLR() { -1, 0, -1, 0, -1, -1, -1, -1, 0, 2, 0, 2, - -1, 2, -1, 2, + -1, 2, -1, 2 ]; this.rotationPoints = [state1RotationPointsOffset, state2RotationPointsOffset, state3RotationPointsOffset, state4RotationPointsOffset]; @@ -1743,7 +1769,7 @@ function ShapeI() { [0, 0, 0, 0] ]; - var state1RotationPointsOffset = [ + var state1RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, -2, 0, 2, 0, 1, 0, @@ -2412,9 +2438,11 @@ function getShape(shapei) { shape.init(result); return shape; } + module.exports.randomShape = randomShape; module.exports.getShape = getShape; -//module.exports.newOpenerShapeQueue = newOpenerShapeQueue; queue.push(new ShapeL()); +// export randomShape; +// export getShape; },{"./consts.js":2}],8:[function(require,module,exports){ @@ -2528,12 +2556,18 @@ exports.$ = $; exports.extend = extend; exports.proxy = proxy; +// export $; +// export extend; +// export proxy; + },{}],9:[function(require,module,exports){ /** All dom definitions and actions */ var utils = require('./utils.js'); var consts = require('./consts.js'); +// import * as utils from './utils.js'; +// import * as consts from './const.js'; var $ = utils.$; @@ -2673,4 +2707,5 @@ var tetrisView = { }; module.exports = tetrisView; +// export tetrisView; },{"./consts.js":2,"./utils.js":8}]},{},[5]); diff --git a/index.html b/index.html index 82799c3..736c6af 100644 --- a/index.html +++ b/index.html @@ -56,27 +56,33 @@ document.getElementById("divbg").removeAttribute("tabIndex");
+
    Openers: - -
      T-Spin Openers: -
        Early I Piece: +
      • No Opener
      • +
          T-Spin Openers: + -
            Early L or J Piece: + -
              Early O Piece: + -
                Early S, Z, or T Piece: +
                  Early S, Z, or T Piece:
                • Pelican
                • DT Cannon (TSZ base)
                • BT Cannon
                • @@ -132,7 +138,7 @@ document.getElementById("divbg").removeAttribute("tabIndex");