2048 Tilt

Sample Project using horizontal and vertical tilt

See the full list of sample projects.

This sample file generates a page with the game 2048. Tilt up, down, left, or right to join numbers and get to the 2048 tile.


/* global MotionStack */

function MotionAd () {
  this.tiltVertical = new MotionStack.Tilt({orientation: "vertical"});
  this.tiltHorizontal = new MotionStack.Tilt({orientation: "horizontal"});
  this.board = [[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0]];
  this.score = 0;

  this.updateBoard(newBoard=true);
}

MotionAd.prototype.getRandomValue = function () {
  return Math.random() < 0.9 ? 2 : 4;
};

MotionAd.prototype.getRandomIndex = function () {
  return Math.floor(Math.random() * 4);
};

MotionAd.prototype.addRandomTile = function () {
  var row = this.getRandomIndex(),
      column = this.getRandomIndex();

  if (this.board[row][column] === 0) {
    this.board[row][column] = this.getRandomValue();
  } else {
    this.addRandomTile();
  }
};

MotionAd.prototype.clearBoard = function() {
  var tiles = document.querySelectorAll(".tile");

  function reset(textContent, className) {
    return function(tile) {
      tile.textContent = textContent;
      tile.className = className;
    };
  }

  [].forEach.call(tiles, reset("", "tile"));
};

MotionAd.prototype.reduceBoard = function(board) {
  return board.reduce(function(a, b) {
    return a.concat(b);
  });
}

MotionAd.prototype.displayTiles = function () {
  var tile = document.getElementsByClassName('tile');
  var values = this.reduceBoard(this.board);

  this.clearBoard();

  for (var i = 0; i < tile.length; i++) {
    if (values[i] > 0) {
      tile[i].textContent = values[i];
      tile[i].className = 'tile tile-' + tile[i].textContent;
    }
  }
};

MotionAd.prototype.printScore = function () {
  var score = document.querySelector('#score');
  score.textContent = this.score;
};

MotionAd.prototype.updateBoard = function(newBoard) {
  if (newBoard) this.addRandomTile();
  this.addRandomTile();
  this.displayTiles();
  this.printScore();
}

MotionAd.prototype.isDifferent = function(previous, current) {
  var previous = this.reduceBoard(previous),
      current = this.reduceBoard(current);

  for (var i = 0; i < current.length; i++) {
    if (previous[i] !== current[i]) {
      return true;
    }
  }
};

MotionAd.prototype.invertBoard = function(board, direction) {
  if (direction === 1 || direction === 2) {
    this.board = board[0].map(function(col, i) {
      return board.map(function(row) {
      return row[i];
      });
    });
  }
};

MotionAd.prototype.getPrevious = function(board) {
  return board.map(function(row) {
    return row;
  });
};

MotionAd.prototype.shift = function(direction) {
  var previous = this.getPrevious(this.board);
  var self = this;

  function biggerThanZero(value) {
    return value > 0;
  }

  function isEmpty(array) {
    var empty = true;
    array.forEach(function(value) {
      if(value > 0) empty = false;
    });
    return empty;
  }

  function padZeroes(array) {
    while(array.length < 4) {
      if (direction === self.tiltVertical.DIRECTION_DOWN || direction === self.tiltVertical.DIRECTION_LEFT) {
        array.push(0);
      } else {
        array.unshift(0);
      }
      updateView = true;
    }
    return array;
  }

  function merge(array) {
    if (direction === self.tiltVertical.DIRECTION_DOWN || direction === self.tiltVertical.DIRECTION_LEFT) {
      for (var i = 0; i < array.length-1; i++) {
        if (array[i] !== 0 && array[i] === array[i+1]) {
          array[i] *= 2;
          self.score += array[i];
          array[i+1] = 0;
        }
      }
    } else {
      for (var j = array.length; j > 0; j--) {
        if (array[j] !== 0 && array[j] === array[j-1]) {
          array[j] *= 2;
          self.score += array[j];
          array[j-1] = 0;
        }
      }
    }
    return array.filter(biggerThanZero);
  }

  this.invertBoard(this.board, direction);

  for (var i = 0; i < this.board.length; i++) {
    var row = this.board[i];
    if (!isEmpty(row)) {
      this.board[i] = padZeroes(merge(row.filter(biggerThanZero)));
    }
  }

  this.invertBoard(this.board, direction);

  if (this.isDifferent(previous, this.board)) this.updateBoard();
};

MotionAd.prototype.checkIfWon = function () {
  var div = document.querySelector('#won');

  this.board.forEach(function(row){
    if (row.indexOf(2048) !== -1) {
      div.style.display = 'block';
      this.stop();
    }
  });
};

MotionAd.prototype.checkIfLost = function () {
  var i,
      j,
      lost = true,
      div = document.getElementById('lost');

  i = 0;
  while (i < this.board.length - 1) {
    j = 0;
    while (j < this.board.length) {
      if (this.board[i][j] === 0 || this.board[i][j] === this.board[i+1][j] || this.board[i+1][j] === 0) {
        lost = false;
      }
      j += 1;
    }
    i += 1;
  }

  i = 0;
  while (i < this.board.length) {
    j = 0;
    while (j < this.board[i].length - 1) {
      if (this.board[i][j] === 0 || this.board[i][j] === this.board[i][j+1] || this.board[i][j+1] === 0) {
        lost = false;
      }
      j += 1;
    }
    i += 1;
  }

  if (lost) {
    div.style.display = 'block';
    this.stop();
  }
};

MotionAd.prototype._handleTilt = function(e) {
  this._previousTilt = this._previousTilt || Date.now();
  var now = e.timeStamp;
  var threshold = 300;

  if(now > this._previousTilt + threshold) {
    this.shift(e.direction);
    this.checkIfLost();
    this.checkIfWon();
  }
  this._previousTilt = now;
};

MotionAd.prototype.start = function() {
  this.tiltVertical.start(this._handleTilt, this);
  this.tiltHorizontal.start(this._handleTilt, this);
};

MotionAd.prototype.stop = function() {
  this.tiltVertical.stop();
  this.tiltHorizontal.stop()
};

var motionAd = new MotionAd();
motionAd.start();