Skip to content

Instantly share code, notes, and snippets.

@grindars
Last active December 20, 2015 08:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grindars/6098560 to your computer and use it in GitHub Desktop.
Save grindars/6098560 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<script>
!function(){this.Shape=function(){function Shape(){}Shape.prototype.rotated=null;Shape.$shapeTemplates=[{rotated:7,blocks:["TL","TC","MR"],color:"red"},{rotated:8,blocks:["TC","TR","ML"],color:"lime"},{rotated:9,blocks:["ML","MR","BC"],color:"#aa00ff"},{rotated:3,blocks:["TL","TC","ML"],color:"yellow"},{rotated:12,blocks:["ML","BL","MR"],color:"orange"},{rotated:15,blocks:["ML","BR","MR"],color:"blue"},{rotated:18,blocks:["ML","MR","MSR"],color:"cyan"},{rotated:0,blocks:["TC","ML","BL"],color:"red"},{rotated:1,blocks:["TC","MR","BR"],color:"lime"},{rotated:10,blocks:["TC","MR","BC"],color:"#aa00ff"},{rotated:11,blocks:["TC","ML","MR"],color:"#aa00ff"},{rotated:2,blocks:["TC","ML","BC"],color:"#aa00ff"},{rotated:13,blocks:["TC","BC","BR"],color:"orange"},{rotated:14,blocks:["TR","ML","MR"],color:"orange"},{rotated:4,blocks:["TL","TC","BC"],color:"orange"},{rotated:16,blocks:["TR","TC","BC"],color:"blue"},{rotated:17,blocks:["TL","ML","MR"],color:"blue"},{rotated:5,blocks:["TC","BC","BL"],color:"blue"},{rotated:6,blocks:["TC","BC","BSC"],color:"cyan"}];Shape.prototype.initializeBlocksFromTemplate=function(template,cols,rows){var offset;this.blocks=function(){var _i,_len,_results;_results=[];for(_i=0,_len=template.length;_i<_len;_i++){offset=template[_i];switch(offset){case"TL":_results.push(-cols-1);break;case"TC":_results.push(-cols);break;case"TR":_results.push(-cols+1);break;case"ML":_results.push(-1);break;case"MR":_results.push(1);break;case"MSR":_results.push(2);break;case"BL":_results.push(cols-1);break;case"BC":_results.push(cols);break;case"BR":_results.push(cols+1);break;case"BSC":_results.push(2*cols);break;default:_results.push(void 0)}}return _results}();return this.blocks.unshift(0)};Shape.forBoard=function(cols,rows){var index,shape,shapes,template,_i,_j,_len,_ref;shapes=new Array(this.$shapeTemplates.length);for(shape=_i=0,_ref=shapes.length;0<=_ref?_i<_ref:_i>_ref;shape=0<=_ref?++_i:--_i){shapes[shape]=new Shape}for(index=_j=0,_len=shapes.length;_j<_len;index=++_j){shape=shapes[index];template=this.$shapeTemplates[index];shape.rotated=shapes[template.rotated];shape.color=template.color;shape.initializeBlocksFromTemplate(template.blocks,cols,rows)}return shapes};return Shape}()}.call(this);!function(){this.Board=function(){Board.prototype.cols=12;Board.prototype.rows=23;function Board(){var col,row,_i,_j,_ref,_ref1;this.$board=new Array(this.cols*this.rows);for(col=_i=0,_ref=this.cols;0<=_ref?_i<_ref:_i>_ref;col=0<=_ref?++_i:--_i){for(row=_j=0,_ref1=this.rows;0<=_ref1?_j<_ref1:_j>_ref1;row=0<=_ref1?++_j:--_j){if(col===0||col===this.cols-1||row>=this.rows-2){this.$board[col+row*this.cols]="gray"}else{this.$board[col+row*this.cols]=null}}}this.shapes=Shape.forBoard(this.cols,this.rows)}Board.prototype.fits=function(pos,shape){var block,_i,_len,_ref;_ref=shape.blocks;for(_i=0,_len=_ref.length;_i<_len;_i++){block=_ref[_i];if(this.$board[pos+block]!==null){return false}}return true};Board.prototype.place=function(pos,shape,state){var block,_i,_len,_ref;_ref=shape.blocks;for(_i=0,_len=_ref.length;_i<_len;_i++){block=_ref[_i];if(state){this.$board[pos+block]=shape.color}else{this.$board[pos+block]=null}}return true};Board.prototype.color=function(pos){return this.$board[pos]};Board.prototype.elide=function(){var col,row,_i,_j,_ref,_ref1;for(row=_i=1,_ref=this.rows-2;1<=_ref?_i<_ref:_i>_ref;row=1<=_ref?++_i:--_i){if(this.$rowIsFilled(row)){for(col=_j=1,_ref1=this.cols-1;1<=_ref1?_j<_ref1:_j>_ref1;col=1<=_ref1?++_j:--_j){this.$board[row*this.cols+col]=null}return function(){var copyaddr,_k,_ref2,_results;_results=[];for(copyaddr=_k=_ref2=row*this.cols;_ref2<=0?_k<=0:_k>=0;copyaddr=_ref2<=0?++_k:--_k){_results.push(this.$board[copyaddr+this.cols]=this.$board[copyaddr])}return _results}}}return null};Board.prototype.$rowIsFilled=function(row){var col,_i,_ref;for(col=_i=1,_ref=this.cols-1;1<=_ref?_i<_ref:_i>_ref;col=1<=_ref?++_i:--_i){if(this.$board[row*this.cols+col]===null){return false}}return true};return Board}()}.call(this);!function(){this.UserInterface=function(){UserInterface.prototype.blockSize=24;UserInterface.prototype.previewMargin=8;UserInterface.prototype.fontSize=16;UserInterface.prototype.largeFontSize=24;function UserInterface(container,inputReceiver){this.$canvas=document.createElement("canvas");this.$canvas.width=480;this.$canvas.height=640;this.$canvas.tabIndex=1;container.appendChild(this.$canvas);this.$canvas.focus();this.$canvas.addEventListener("keydown",this.$keydown.bind(this),false);this.$context=this.$canvas.getContext("2d");this.$inputReceiver=inputReceiver}UserInterface.prototype.$keydown=function(ev){switch(ev.keyCode){case 37:return this.$inputReceiver.pushKey("left");case 39:return this.$inputReceiver.pushKey("right");case 38:return this.$inputReceiver.pushKey("rotate");case 40:return this.$inputReceiver.pushKey("drop")}};UserInterface.prototype.updateScreen=function(){var block,board,col,color,pos,row,uiOffset,_i,_j,_k,_len,_ref,_ref1,_ref2;board=this.$inputReceiver.board;this.$context.fillStyle="#fff";this.$context.strokeStyle="rgba(0,0,0,0.5)";this.$context.fillRect(0,0,this.$canvas.width,this.$canvas.height);for(col=_i=0,_ref=board.cols;0<=_ref?_i<_ref:_i>_ref;col=0<=_ref?++_i:--_i){for(row=_j=0,_ref1=board.rows;0<=_ref1?_j<_ref1:_j>_ref1;row=0<=_ref1?++_j:--_j){pos=row*board.cols+col;color=board.color(pos);if(color!==null){this.$context.fillStyle=color;this.$context.fillRect(col*this.blockSize,row*this.blockSize,this.blockSize,this.blockSize);this.$context.strokeRect(col*this.blockSize,row*this.blockSize,this.blockSize,this.blockSize)}}}uiOffset=this.blockSize*(board.cols+1);this.$context.fillStyle="gray";this.$context.font=this.fontSize+"px monospace";this.$context.fillRect(uiOffset,this.blockSize,this.blockSize*6,this.blockSize*6);this.$context.fillStyle="white";this.$context.fillRect(uiOffset+this.blockSize-this.previewMargin,2*this.blockSize-this.previewMargin,this.blockSize*4+this.previewMargin*2,this.blockSize*4+this.previewMargin*2);this.$context.fillStyle=this.$inputReceiver.nextShape.color;_ref2=this.$inputReceiver.nextShape.blocks;for(_k=0,_len=_ref2.length;_k<_len;_k++){block=_ref2[_k];block+=board.cols+1;col=block%board.cols;row=block/board.cols<<0;this.$context.fillRect(uiOffset+this.blockSize+col*this.blockSize,2*this.blockSize+row*this.blockSize,this.blockSize,this.blockSize);this.$context.strokeRect(uiOffset+this.blockSize+col*this.blockSize,2*this.blockSize+row*this.blockSize,this.blockSize,this.blockSize)}this.$context.fillStyle="black";this.$context.fillText("Score: "+this.$inputReceiver.score,uiOffset,this.blockSize*8+this.fontSize);return this.$context.fillText("Level: "+this.$inputReceiver.level,uiOffset,this.blockSize*8+2*this.fontSize)};UserInterface.prototype.gameOver=function(){var line,lines,metrics,totalHeight,y_offset,_i,_j,_len,_len1,_results;this.$context.fillStyle="#fff";this.$context.fillRect(0,0,this.$canvas.width,this.$canvas.height);this.$context.fillStyle="#000";lines=[{height:this.largeFontSize,text:"Game over"},{height:this.largeFontSize,text:""},{height:this.fontSize,text:"Score: "+this.$inputReceiver.score},{height:this.fontSize,text:"Level: "+this.$inputReceiver.level}];totalHeight=0;for(_i=0,_len=lines.length;_i<_len;_i++){line=lines[_i];totalHeight+=line.height}y_offset=(this.$canvas.height-totalHeight)/2;_results=[];for(_j=0,_len1=lines.length;_j<_len1;_j++){line=lines[_j];this.$context.font=line.height+"px monospace";metrics=this.$context.measureText(line.text);this.$context.fillText(line.text,(this.$canvas.width-metrics.width)/2,y_offset);_results.push(y_offset+=line.height)}return _results};return UserInterface}()}.call(this);!function(){this.Tetris=function(){function Tetris(container,level){this.board=new Board;this.$ui=new UserInterface(container,this);this.$pendingInputQueue=[];this.score=0;this.level=level;this.$fallRate=1e3/level;this.nextShape=this.$randomShape();this.currentShape=this.$randomShape();this.$pos=1*this.board.cols+this.board.cols/2-1;this.$gameTick()}Tetris.prototype.$randomShape=function(){var index;index=Math.floor(Math.random()*this.board.shapes.length);return this.board.shapes[index]};Tetris.prototype.$gameTick=function(){this.board.place(this.$pos,this.currentShape,true);this.$ui.updateScreen();this.board.place(this.$pos,this.currentShape,false);return this.$receiveInput(function(ch){switch(ch){case null:if(this.board.fits(this.$pos+this.board.cols,this.currentShape)){this.$pos+=this.board.cols;return this.$gameTick()}else{this.board.place(this.$pos,this.currentShape,true);this.score++;return this.$elide(function(){this.currentShape=this.nextShape;this.nextShape=this.$randomShape();this.$pos=1*this.board.cols+this.board.cols/2-1;if(this.board.fits(this.$pos,this.currentShape)){return this.$gameTick()}else{return this.$ui.gameOver()}})}break;case"left":if(this.board.fits(this.$pos-1,this.currentShape)){this.$pos--}return this.$gameTick();case"right":if(this.board.fits(this.$pos+1,this.currentShape)){this.$pos++}return this.$gameTick();case"rotate":if(this.board.fits(this.$pos,this.currentShape.rotated)){this.currentShape=this.currentShape.rotated}return this.$gameTick();case"drop":while(this.board.fits(this.$pos+this.board.cols,this.currentShape)){this.$pos+=this.board.cols;this.score++}return this.$gameTick()}})};Tetris.prototype.$elide=function(callback){var boardCallback,forwardCallback,shiftCallback,_this=this;boardCallback=this.board.elide();if(boardCallback!=null){forwardCallback=function(){return _this.$elide(callback)};shiftCallback=function(){boardCallback.call(_this.board);_this.$ui.updateScreen();return setTimeout(forwardCallback,100)};this.$ui.updateScreen();return setTimeout(shiftCallback,100)}else{return callback.call(this)}};Tetris.prototype.$receiveInput=function(callback){var timeout,timeoutHandler,_this=this;if(this.$pendingInputQueue.length>0){callback.call(this,this.$pendingInputQueue.shift());return}if(this.$tickRemaining!=null){timeout=this.$tickRemaining;delete this.$tickRemaining}else{timeout=this.$fallRate}this.$inputReceiver=callback;this.$inputStarted=(new Date).valueOf();timeoutHandler=function(){delete _this.$inputTimeout;delete _this.$inputReceiver;delete _this.$inputStarted;return callback.call(_this,null)};return this.$inputTimeout=setTimeout(timeoutHandler,timeout)};Tetris.prototype.pushKey=function(code){var receiver;if(this.$inputTimeout!=null){receiver=this.$inputReceiver;this.$tickRemaining=this.$fallRate-((new Date).valueOf()-this.$inputStarted);clearTimeout(this.$inputTimeout);delete this.$inputTimeout;delete this.$inputStarted;delete this.$inputReceiver;return receiver.call(this,code)}else{return this.$pendingInputQueue.push(code)}};return Tetris}()}.call(this); </script>
<script>
window.addEventListener('load', function() {
var tetris = new Tetris(document.getElementById('container'), 2);
});
</script>
<style>
#container > canvas {
border: solid 1px #000;
}
</style>
</head>
<body>
<div id='container'></div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment