rule.js
author Oleksandr Gavenko <gavenkoa@gmail.com>
Sun, 10 Jan 2016 16:54:12 +0200
changeset 178 ef24ad27c83d
parent 120 653ee00d2601
permissions -rw-r--r--
Add deploy dependency on CSS file.

"use strict";

/** @fileOverview Game/world rules. */

/** @namespace */
var board = {};
board.create = function() {
    var brd = [];
    for (var i = 0; i < 4; i++) {
        brd[i] = [];
        for (var j = 0; j < 4; j++) {
            brd[i][j] = 0;
        }
    }
    return brd;
}
board.copy = function(from, to) {
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            to[i][j] = from[i][j];
        }
    }
}
board.freeCnt = function(brd) {
    var cnt = 0;
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            if (brd[i][j] === 0)
                cnt++;
        }
    }
    return cnt;
}
board.gameOver = function(brd) {
    if (board.freeCnt(brd) > 0)
        return false;
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 3; j++) {
            if (brd[i][j] === brd[i][j+1])
                return false;
        }
    }
    for (var j = 0; j < 4; j++) {
        for (var i = 0; i < 3; i++) {
            if (brd[i][j] === brd[i+1][j])
                return false;
        }
    }
    return true;
}
board.putRandom = function(brd) {
    var cnt = board.freeCnt(brd);
    if (cnt === 0)
        throw Error('You forget detect game over!');
    cnt = Math.floor(Math.random() * cnt)+1;
exit:
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            if (brd[i][j] !== 0)
                continue;
            if (cnt === 1) {
                brd[i][j] = (Math.random() > .9) ? 2 : 1;
                break exit;
            }
            cnt--;
        }
    }
}
/* http://www.reddit.com/r/2048/comments/214njx/highest_possible_score_for_2048_warning_math */
var boardScoreTbl = [0];
for (var i = 1, exp = 2; i < 16; i++, exp *= 2) {
    boardScoreTbl[i] = (i-1)*exp;
}
board.score = function(brd) {
    var score = 0;
    var max = 0;
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            var val = brd[i][j];
            score += boardScoreTbl[val];
            if (max < val)
                max = val;
        }
    }
    return {score: score, max: max};
}

/** @namespace */
board.row = {};
board.row.init = function() {
    return {stack: [], curr: 0};
}
board.row.push = function(state, val) {
    if (val === 0)
        return;
    if (state.curr === 0) {
        state.curr = val;
        return;
    }
    if (state.curr === val) {
        state.stack.push(state.curr+1);
        state.curr = 0;
    } else {
        state.stack.push(state.curr);
        state.curr = val;
    }
}
board.row.finish = function(state) {
    if (state.curr !== 0)
        state.stack.push(state.curr);
}

/** @namespace */
board.move = {};
board.move.up = board.move.upOrig = function(brd) {
    var updated = false;
    for (var j = 0; j < 4; j++) {
        var state = board.row.init();
        for (var i = 0; i < 4; i++) {
            board.row.push(state, brd[i][j]);
        }
        board.row.finish(state);
        for (var i = 0; i < state.stack.length; i++) {
            if (brd[i][j] !== state.stack[i])
                updated = true;
            brd[i][j] = state.stack[i];
        }
        for (; i < 4; i++) {
            if (brd[i][j] !== 0)
                updated = true;
            brd[i][j] = 0;
        }
    }
    return updated;
};
board.move.down = board.move.downOrig = function(brd) {
    var updated = false;
    for (var j = 0; j < 4; j++) {
        var state = board.row.init();
        for (var i = 3; i >= 0; i--) {
            board.row.push(state, brd[i][j]);
        }
        board.row.finish(state);
        for (var i = 0; i < state.stack.length; i++) {
            if (brd[3-i][j] !== state.stack[i])
                updated = true;
            brd[3-i][j] = state.stack[i];
        }
        for (; i < 4; i++) {
            if (brd[3-i][j] !== 0)
                updated = true;
            brd[3-i][j] = 0;
        }
    }
    return updated;
};
board.move.left = board.move.leftOrig = function(brd) {
    var updated = false;
    for (var i = 0; i < 4; i++) {
        var state = board.row.init();
        for (var j = 0; j < 4; j++) {
            board.row.push(state, brd[i][j]);
        }
        board.row.finish(state);
        for (var j = 0; j < state.stack.length; j++) {
            if (brd[i][j] !== state.stack[j])
                updated = true;
            brd[i][j] = state.stack[j];
        }
        for (; j < 4; j++) {
            if (brd[i][j] !== 0)
                updated = true;
            brd[i][j] = 0;
        }
    }
    return updated;
};
board.move.right = board.move.rightOrig = function(brd) {
    var updated = false;
    for (var i = 0; i < 4; i++) {
        var state = board.row.init();
        for (var j = 3; j >= 0; j--) {
            board.row.push(state, brd[i][j]);
        }
        board.row.finish(state);
        for (var j = 0; j < state.stack.length; j++) {
            if (brd[i][3-j] !== state.stack[j])
                updated = true;
            brd[i][3-j] = state.stack[j];
        }
        for (; j < 4; j++) {
            if (brd[i][3-j] !== 0)
                updated = true;
            brd[i][3-j] = 0;
        }
    }
    return updated;
};

/* Restore after testing board.js moves. */
board.move.restore = function() {
    board.move.up = board.move.upOrig;
    board.move.down = board.move.downOrig;
    board.move.left = board.move.leftOrig;
    board.move.right = board.move.rightOrig;
}
/* For testing board.js moves. */
board.move.replaceMoveByBoardJS = function(brdEngine, dir) {
    return function(brd) {
        var engOrigBrd = new brdEngine(brd);
        var engBrd = new brdEngine();
        var changed = engOrigBrd[dir](engBrd);
        var newBrd = engBrd.exportTo();
        var changed2 = board.move[dir+"Orig"](brd);
        for (var i = 0; i < 4; i++) {
            for (var j = 0; j < 4; j++) {
                if (newBrd[i][j] !== brd[i][j]) {
                    console.log("[%d][%d] original: %o, expected: %o, actual: %o", i, j, engOrigBrd.exportTo(), brd, newBrd);
                    return false;
                }
            }
        }
        if (changed2 !== changed) {
            console.log("original: %o, changed: %o, changed2: %o", engOrigBrd.exportTo(), changed, changed2);
            return false;
        }
        return changed;
    }
}
board.move.replaceByBoardJS = function(brdEngine) {
    // console.log("x2: %o", brdEngine);
    board.move.up = board.move.replaceMoveByBoardJS(brdEngine, "up");
    board.move.down = board.move.replaceMoveByBoardJS(brdEngine, "down");
    board.move.left = board.move.replaceMoveByBoardJS(brdEngine, "left");
    board.move.right = board.move.replaceMoveByBoardJS(brdEngine, "right");
}