GOAL = 2048;
CHANCE_OF_FOUR_TILE = .125;

function Game() {

    this.grid = [0, 0, 0, 0,
                 0, 0, 0, 0,
                 0, 0, 0, 0,
                 0, 0, 0, 0];

    this.score = 0;

    this.won = false;

    this.shift = function(groups, object) {
        return groups.map(function(group) {
            group = Util.compact(group);
            var retArray = [];
            while (group.length > 0) {
                if (group.length == 1) {
                    retArray.push(group.shift());
                } else if (group[0] == group[1]) {
                    var newTileValue = group[0] * 2;
                    object.score += newTileValue;
                    object.won = (newTileValue == GOAL);
                    retArray.push(group.shift() + group.shift());
                } else {
                    retArray.push(group.shift());
                }
            }
            return Util.pad(retArray);
        });
    };

}

Game.prototype.up = function() {
    var cols = Util.columnize(this.grid);
    cols = this.shift(cols, this);
    var newGrid = Util.decolumnize(cols);
    if (Util.eq(this.grid, newGrid)) return false;
    this.grid = newGrid;
    return true;
};

Game.prototype.down = function() {
    var cols = Util.columnize(this.grid.reverse());
    cols = this.shift(cols, this);
    var newGrid = Util.decolumnize(cols).reverse();
    if (Util.eq(this.grid.reverse(), newGrid)) return false;
    this.grid = newGrid;
    return true;
};

Game.prototype.left = function() {
    var rows = Util.rowify(this.grid);
    rows = this.shift(rows, this);
    var newGrid = Util.derowify(rows);
    if (Util.eq(this.grid, newGrid)) return false;
    this.grid = newGrid;
    return true;
};

Game.prototype.right = function() {
    var rows = Util.rowify(this.grid.reverse());
    rows = this.shift(rows, this);
    var newGrid = Util.derowify(rows).reverse();
    if (Util.eq(this.grid.reverse(), newGrid)) return false;
    this.grid = newGrid;
    return true;
};

Game.prototype.addTile = function() {
    var empties = [];
    for (var i = 0; i < 16; i++) empties.push(i);
    empties = empties.filter(function(index) {
        return this.grid[index] === 0;
    }, this);
    var newTileIndex = empties[Math.floor(Math.random() * empties.length)];
    var newTile = Math.random() >= CHANCE_OF_FOUR_TILE ? 2 : 4;
    this.grid[newTileIndex] = newTile;
};

Game.prototype.moveAvailable = function() {
    if (this.grid.some(function(tile) { return tile === 0; })) return true;
    var groups = [];
    groups = groups.concat(Util.rowify(this.grid), Util.columnize(this.grid));
    return Util.hasDouble(groups);
};

Game.prototype.toString = function() {
    var retString = "";
    for (var i = 0; i < 16; i++) {
        retString += Util.center(this.grid[i].toString(), 6, "&nbsp;");
        retString += (i % 4 == 3) ? "<br/>" : " ";
    }
    return retString;
};

@@ -71,37 +71,17 @@
 grid = %5B
-0, 0, 0, 0,%0A         
+%0A
         
@@ -91,32 +91,49 @@
 0, 0, 0,%0A   
+     0, 0, 0, 0,%0A
         
       0, 0, 
@@ -112,38 +112,32 @@
 , 0, 0,%0A        
-      
 0, 0, 0, 0,%0A    
@@ -140,25 +140,16 @@
         
-    
-     
 0, 0, 0,
@@ -150,16 +150,21 @@
  0, 0, 0
+%0A    
 %5D;%0A%0A    
@@ -1215,32 +1215,72 @@
  = function() %7B%0A
+    var oldGrid = %5B%5D.concat(this.grid);%0A
     var cols = U
@@ -1293,22 +1293,20 @@
 lumnize(
-this.g
+oldG
 rid.reve
@@ -1421,34 +1421,24 @@
 eq(this.grid
-.reverse()
 , newGrid)) 
@@ -1770,32 +1770,72 @@
  = function() %7B%0A
+    var oldGrid = %5B%5D.concat(this.grid);%0A
     var rows = U
@@ -1845,22 +1845,20 @@
 .rowify(
-this.g
+oldG
 rid.reve
@@ -1974,26 +1974,16 @@
 his.grid
-.reverse()
 , newGri
@@ -2555,24 +2555,24 @@
 eturn true;%0A
+
     var grou
@@ -2582,29 +2582,8 @@
 = %5B%5D
-;%0A    groups = groups
 .con

Improve game logic