2012년 3월 7일 수요일

Javascript Tetris


자바스크립트로 테트리스를 만들어 보았다.

Javascript Tetris

← : left, → : right, ↑ : rotate, ↓ down, space: drop
Level:Score:


Javascript code - expand
<script type="text/javascript">
      var VK_LEFT = 37;
      var VK_RIGHT = 39;
      var VK_DOWN = 40;
      var VK_UP = 38;
      var VK_SPACE = 32;
      function get(obj) {
   return (typeof obj == "string") ? document.getElementById(obj) : obj;
      }
      function Tetris(eltId, levelElt, scoreElt, gameoverElt, nextElt) {
   this.init(eltId, levelElt, scoreElt, gameoverElt, nextElt);
      }
      Tetris.prototype.init = function(eltId, levelElt, scoreElt, gameoverElt, nextElt) {

   // map size
   this.MAP_WIDTH = 12;
   this.MAP_HEIGHT = 20;

   // current block
   this.curTet = 0;
   this.curRot = 0;
   this.curPos = {x:0, y:0};

   // tet
   this.tet = new Array(7);
   this.tet[0] = new Array(2);
   this.tet[0][0] = new Array({x:0,y:0},{x:1,y:0},{x:2,y:0},{x:3,y:0});
   this.tet[0][1] = new Array({x:0,y:0},{x:0,y:1},{x:0,y:2},{x:0,y:3});
   this.tet[1] = new Array(2);
   this.tet[1][0] = new Array({x:0,y:0},{x:1,y:0},{x:1,y:1},{x:2,y:1});
   this.tet[1][1] = new Array({x:1,y:0},{x:1,y:1},{x:0,y:1},{x:0,y:2});
   this.tet[2] = new Array(2);
   this.tet[2][0] = new Array({x:0,y:1},{x:1,y:1},{x:1,y:0},{x:2,y:0});
   this.tet[2][1] = new Array({x:0,y:0},{x:0,y:1},{x:1,y:1},{x:1,y:2});
   this.tet[3] = new Array(4);
   this.tet[3][2] = new Array({x:0,y:0},{x:1,y:0},{x:2,y:0},{x:2,y:1});
   this.tet[3][3] = new Array({x:1,y:0},{x:1,y:1},{x:1,y:2},{x:0,y:2});
   this.tet[3][0] = new Array({x:0,y:0},{x:0,y:1},{x:1,y:1},{x:2,y:1});
   this.tet[3][1] = new Array({x:1,y:0},{x:0,y:0},{x:0,y:1},{x:0,y:2});
   this.tet[4] = new Array(4);
   this.tet[4][2] = new Array({x:0,y:0},{x:1,y:0},{x:2,y:0},{x:0,y:1});
   this.tet[4][3] = new Array({x:1,y:0},{x:1,y:1},{x:1,y:2},{x:0,y:0});
   this.tet[4][0] = new Array({x:2,y:0},{x:0,y:1},{x:1,y:1},{x:2,y:1});
   this.tet[4][1] = new Array({x:1,y:2},{x:0,y:0},{x:0,y:1},{x:0,y:2});
   this.tet[5] = new Array(4);
   this.tet[5][0] = new Array({x:0,y:1},{x:1,y:1},{x:2,y:1},{x:1,y:0});
   this.tet[5][1] = new Array({x:0,y:0},{x:0,y:1},{x:0,y:2},{x:1,y:1});
   this.tet[5][2] = new Array({x:0,y:0},{x:1,y:0},{x:2,y:0},{x:1,y:1});
   this.tet[5][3] = new Array({x:1,y:0},{x:1,y:1},{x:1,y:2},{x:0,y:1});
   this.tet[6] = new Array(1);
   this.tet[6][0] = new Array({x:0,y:0},{x:1,y:0},{x:0,y:1},{x:1,y:1});

   // map
   this.map = new Array(this.MAP_HEIGHT);
   for (var i = 0,limit = this.map.length; i < limit; i++) {
       this.map[i] = new Array(this.MAP_WIDTH);
       for (var x = 0, xlimit = this.map[i].length; x < xlimit; x++) {
    this.map[i][x] = 0;
       }
   }
   this.empty_line = new Array(this.MAP_WIDTH);
   for (var x = 0, xlimit = this.MAP_WIDTH; x < xlimit; x++) {this.empty_line[x] = 0;}

   // screen
   this.elt = get(eltId);
   this.inspire(this.elt);

   // level
   this.level_map = new Array(1000, 900, 800, 700, 600, 500, 400, 300, 200, 100);

   this.level = 0;
   this.levelElt = get(levelElt);

   // score
   this.score = 0;
   this.scoreElt = get(scoreElt);

   // game over
   this.gameoverElt = get(gameoverElt);

   // next
   this.next = null;
   this.nextElt = get(nextElt);
   this.inspireNext(this.nextElt);

   // status
   this.state = "ready";
      }
      Tetris.prototype.inspire = function (elt) {
   this.elt = elt;
   this.elt.innerHTML = "";
   for (var y = 0, ylimit = this.map.length; y < ylimit; y++) {
       this.elt.innerHTML += "<div class='line'>";
       for (var x = 0, xlimit = this.map[y].length; x < xlimit; x++) {
    this.elt.innerHTML += "<span id='" + (x + "_" + y)  + "' class='block'>0</span>";
       }
       this.elt.innerHTML += "</div>";
   }
      }
      Tetris.prototype.inspireNext = function(nextElt) {
   this.nextElt = nextElt;
   this.nextElt.innerHTML = "";
   for (var y = 0; y < 4; y++) {
       this.nextElt.innerHTML += "<div class='line'>";
       for (var x = 0; x < 4; x++) {
    this.nextElt.innerHTML += "<span id='next" + (x + "_" + y) + "' class='block'>0</span>";
       }
       this.nextElt.innerHTML += "</div>";
   }
      }
      Tetris.prototype.getMapBlock = function(x, y) {
   return this.map[y][x];
      }
      Tetris.prototype.setMapBlock = function(x, y, type) {
   this.map[y][x] = type;
      }
      Tetris.prototype.clearMap = function() {
   // clear map
   for (var y = 0; y < this.MAP_HEIGHT; y++) {
       for (var x = 0; x < this.MAP_WIDTH; x++) {
    this.setMapBlock(x, y, 0);
       }
   }
      }
      Tetris.prototype.gen = function() {
   this.curPos = {x:(this.MAP_WIDTH / 2) - 2,y:0};
   this.curRot = 0;
   if (this.next == null) {
       this.next = Math.floor(Math.random() * 10) % 7;
   }
   this.curTet = this.next;
   var n = Math.floor(Math.random() * 10) % 7;
   this.next = (n == this.curTet) ? ((n + (Math.floor(Math.random() * 10) % 6) + 1) % 7) : n;
   this.dropDelay(1000);

   if (this.checkCollid(this.curPos, this.curTet, this.curRot)) {
       this.gameOver();
   }
      }
      Tetris.prototype.sideWalk = function(offset) {
   var nextPos = {x:(this.curPos.x + offset), y:this.curPos.y};
   if (!this.checkCollid(nextPos, this.curTet, this.curRot))
       this.curPos = nextPos;
      }
      Tetris.prototype.down = function() {
   var nextPos = {x:this.curPos.x, y:(this.curPos.y + 1)};
   if (!this.checkCollid(nextPos, this.curTet, this.curRot)) {
       this.curPos = nextPos;
       this.dropDelay(this.level_map[this.level]);
       return true;
   } else {
       this.addToMap();
       this.removeFull();
       this.gen();
       return false;
   }
      }
      Tetris.prototype.rotate = function(offset) {
   var cnt = this.tet[this.curTet].length;
   var nextRot = (this.curRot + offset) % cnt;
   if (!this.checkCollid(this.curPos, this.curTet, nextRot))
       this.curRot = nextRot;
      }
      Tetris.prototype.checkCollid = function(pos, type, rot) {
   var b = this.tet[type][rot];
   for (var i = 0, limit = b.length; i < limit; i++) {
       var x = pos.x + b[i].x;
       var y = pos.y + b[i].y;
       if (x < 0 || x >= this.MAP_WIDTH) {
    return true;
       } else  if (y < 0 || y >= this.MAP_HEIGHT) {
    return true;
       } else if (this.getMapBlock(x, y) != 0) {
    return true;
       }
   }
   return false;
      }
      Tetris.prototype.addToMap = function() {
   var elt = this.tet[this.curTet][this.curRot];
   for (var i = 0, limit = elt.length; i < limit; i++) {
       this.setMapBlock(this.curPos.x + elt[i].x, this.curPos.y + elt[i].y, this.curTet + 1);
   }
      }
      Tetris.prototype.removeFull = function() {
   for (var y = this.MAP_HEIGHT - 1; y >= 0; y--) {
       var cnt = 0;
       for (var x = 0; x < this.MAP_WIDTH; x++) {
    if (this.getMapBlock(x, y) == 0)
        break;
    
    cnt++;
       }
       if (cnt == this.MAP_WIDTH) {
    for (var cy = y; cy > 0; cy--) {
        this.map[cy] = this.map[cy-1].slice();
    }
    this.map[0] = this.empty_line.slice();
    this.score += 25;
    this.level = Math.min(Math.floor((this.score / 250)), this.level_map.length - 1);
    y++;
       }
   }
      }
      Tetris.prototype.dropTimer = null;
      Tetris.prototype.drop = function(interval) {

   if (this.state == "start") {
       var ret = this.down();
       this.update();

       if (ret) {
    var me = this;
    clearTimeout(this.dropTimer);
    this.dropTimer = setTimeout(function() {me.drop(interval);}, interval);
       }
   }
      }
      Tetris.prototype.dropDelay = function(interval) {
   var me = this;
   clearTimeout(this.dropTimer);
   this.dropTimer = setTimeout(function() {me.drop(me.level_map[me.level]);}, interval);
      }
      Tetris.prototype.start = function() {

   this.clearMap();
   this.inspire(this.elt);
   this.inspireNext(this.nextElt);
   this.score = 0;
   this.level = 0;
   this.gen();

   this.state = "start";
   this.gameoverElt.innerHTML = "";
   this.update();

   var me = this;
   document.onkeydown = function(e) {me.handleInput(e);}
      }
      Tetris.prototype.gameOver = function() {

   // gray map
   for (var y = 0; y < this.MAP_HEIGHT; y++) {
       for (var x = 0; x < this.MAP_WIDTH; x++) {
    if (this.getMapBlock(x, y) != 0) {
        this.setMapBlock(x, y, 8);
    }
       }
   }
   
   clearTimeout(this.dropTimer);
   document.onkeydown = null;
   this.state = "gameover";
   this.update();
      }
      Tetris.prototype.stop = function() {
   this.clearMap();
   for (var y = 0; y < 4; y++) {
       for (var x = 0; x < 4; x++) {
    get("next" + (x + "_" + y)).className = "block";
       }
   }
   this.next = null;
   clearTimeout(this.dropTimer);
   this.score = 0;
   this.state = "stop";
   document.onkeydown = null;
   this.gameoverElt.innerHTML = "";
   this.update();
      }
      Tetris.prototype.preventDefault = function(e) {
   (e.preventDefault) ? e.preventDefault() : e.returnValue = false;
      }
      Tetris.prototype.handleInput = function(e) {
   var ev = (window.event) ? window.event : e;
   var keycode = (window.event) ? window.event.keyCode : e.which;
   switch (keycode) {
   case VK_LEFT:
       this.sideWalk(-1);
       this.preventDefault(ev);
       break;
   case VK_RIGHT:
       this.sideWalk(1);
       this.preventDefault(ev);
       break;
   case VK_UP:
       this.rotate(1);
       this.preventDefault(ev);
       break;
   case VK_DOWN:
       this.down();
       this.preventDefault(ev);
       break;
   case VK_SPACE:
       while (this.down()){}
       this.preventDefault(ev);
       break;
   default:
       break;
   }
   this.update();
      }
      Tetris.prototype.render = function() {
   for (var y = 0, ylimit = this.map.length; y < ylimit; y++) {
       for (var x = 0, xlimit = this.map[y].length; x < xlimit; x++) {
    var type = this.getMapBlock(x, y);
    get(x + "_" + y).innerHTML = "0";
    get(x + "_" + y).className = "block col" + type;
       }
   }

   if (this.state == "start") {
       this.drawTet(this.curPos, this.curTet, this.curRot);
   } else if (this.state == "gameover") {
       this.gameoverElt.innerHTML = "Game Over!!";
   }
   this.scoreElt.innerHTML = this.score;
   this.levelElt.innerHTML = this.level + 1;
   if (this.next != null)
       this.drawNext(this.next);
      }
      Tetris.prototype.drawBlock = function(x, y, type) {
   get(x + "_" + y).className = "block col" + type;
      }
      Tetris.prototype.drawTet = function(pos, type, rot) {
   var t = this.tet[type][rot];
   for (var i = 0, limit = t.length; i < limit; i++) {
       this.drawBlock(pos.x + t[i].x, pos.y + t[i].y, type + 1);
   }
      }
      Tetris.prototype.drawNext = function(type) {
   for (var y = 0; y < 4; y++) {
       for (var x = 0; x < 4; x++) {
    get("next" + (x + "_" + y)).className = "block";
       }
   }
   var t = this.tet[type][0];
   for (var i = 0, limit = t.length; i < limit; i++) {
       var n = get("next" + (t[i].x + "_" + t[i].y));
       n.innerHTML = "0";
       n.className = "block col" + (type + 1);
   }
      }
      Tetris.prototype.update = function() {
   this.render();
      }
</script>

댓글 1개:

  1. 소스를 통해서 공부를 해볼려고 합니다.
    좋은 글 감사합니다.

    답글삭제