board.js
author Oleksandr Gavenko <gavenkoa@gmail.com>
Mon, 08 Sep 2014 17:43:10 +0300
changeset 20 ab294e8db00c
parent 14 9b49e710f5a7
child 22 b041338d7e88
permissions -rw-r--r--
Add detecting value at edge or corner, free cell count. Added AI that count max value at edges or corner and free cell count.


////////////////////////////////////////////////////////////////
// Board as linear array.
////////////////////////////////////////////////////////////////

/* Create board on linear array.
 * Extract data from 'brd' (which is 2d array) if present. */
function BoardArr(brd) {
    this.brd = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    if (brd) {
        for (var i = 0; i < 4; i++)
            for (var j = 0; j < 4; j++)
                this.brd[4*i + j] = brd[i][j];
    }
}
/* Doesn't designed to be efficient. */
BoardArr.prototype.get = function(i, j) {
    return this.brd[4*i + j];
}
/* Doesn't designed to be efficient. */
BoardArr.prototype.set = function(i, j, val) {
    this.brd[4*i + j] = val;
}
/* Return and optionally fill 2d board.
 * Doesn't designed to be efficient. */
BoardArr.prototype.exportTo = function(brd) {
    brd = brd || [[],[],[],[]];
    for (var i = 0; i < 4; i++)
        for (var j = 0; j < 4; j++)
            brd[i][j] = this.brd[4*i + j];
    return brd;
}
BoardArr.prototype.copy = function(brd) {
    brd = brd || new BoardArr();
    for (var i = 0; i < 16; i++)
        brd.brd[i] = this.brd[i];
    return brd;
}



////////////////////////////////////////////////////////////////
// Board as 2d array.
////////////////////////////////////////////////////////////////

/* Create board on 2d array.
 * Extract data from brd (which is 2d array) if present. */
function BoardArr2d(brd) {
    this.brd = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];
    if (brd) {
        for (var i = 0; i < 4; i++)
            for (var j = 0; j < 4; j++)
                this.brd[i][j] = brd[i][j];
    }
}
BoardArr2d.prototype.get = function(i, j) {
    return this.brd[i][j];
}
BoardArr2d.prototype.set = function(i, j, val) {
    this.brd[i][j] = val;
}
BoardArr2d.prototype.equals = function(brd) {
    for (var i = 0; i < 4; i++)
        for (var j = 0; j < 4; j++)
            if (this.brd[i][j] !== brd.brd[i][j])
                return false;
    return true;
}
/* Return and optionally fill 2d board. */
BoardArr2d.prototype.exportTo = function(brd) {
    brd = brd || [[],[],[],[]];
    for (var i = 0; i < 4; i++)
        for (var j = 0; j < 4; j++)
            brd[i][j] = this.brd[i][j];
    return brd;
}
BoardArr2d.prototype.copy = function(brd) {
    brd = brd || new BoardArr2d();
    for (var i = 0; i < 4; i++)
        for (var j = 0; j < 4; j++)
            brd.brd[i][j] = this.brd[i][j];
    return brd;
}
BoardArr2d.prototype.free = function() {
    var cnt = 0;
    for (var i = 0; i < 4; i++)
        for (var j = 0; j < 4; j++)
            if (this.brd[i][j] === 0)
                cnt++;
    return cnt;
}
BoardArr2d.prototype.score = function() {
    var score = 0;
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            var v = this.brd[i][j];
            if (v > 0)
                score += (v-1)*Math.pow(2, v);
        }
    }
    return score;
}
BoardArr2d.prototype.max = function() {
    var max = 0;
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            max = Math.max(max, this.brd[i][j]);
        }
    }
    return max;
}
BoardArr2d.prototype.find = function(val) {
    var xy = [];
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            if (this.brd[i][j] === val)
            xy.push([i,j]);
        }
    }
    return xy;
}
BoardArr2d.prototype.atEdge = function(val) {
    var brd = this.brd;
    return (brd[0][0] === val) || (brd[0][1] === val) || (brd[0][2] === val) || (brd[0][3] === val)
        || (brd[1][0] === val) || (brd[1][3] === val)
        || (brd[2][0] === val) || (brd[2][3] === val)
        || (brd[3][0] === val) || (brd[3][1] === val) || (brd[3][2] === val) || (brd[3][3] === val);
}
BoardArr2d.prototype.atCorner = function(val) {
    var brd = this.brd;
    return (brd[0][0] === val) || (brd[0][3] === val) || (brd[3][0] === val) || (brd[3][3] === val);
}

BoardArr2d.prototype.canRight = function() {
    for (var i = 0; i < 4; i++) {
        var f0 = this.brd[i][0], f1 = this.brd[i][1], f2 = this.brd[i][2], f3 = this.brd[i][3];
        if (f2 > 0 && (f2 === f3 || f3 === 0))
            return true;
        if (f1 > 0 && (f1 === f2 || f2 === 0))
            return true;
        if (f0 > 0 && (f0 === f1 || f1 === 0))
            return true;
    }
    return false;
}
BoardArr2d.prototype.canLeft = function() {
    for (var i = 0; i < 4; i++) {
        var f0 = this.brd[i][0], f1 = this.brd[i][1], f2 = this.brd[i][2], f3 = this.brd[i][3];
        if (f1 > 0 && (f1 === f0 || f0 === 0))
            return true;
        if (f2 > 0 && (f2 === f1 || f1 === 0))
            return true;
        if (f3 > 0 && (f3 === f2 || f2 === 0))
            return true;
    }
    return false;
}
BoardArr2d.prototype.canUp = function() {
    for (var j = 0; j < 4; j++) {
        var f0 = this.brd[0][j], f1 = this.brd[1][j], f2 = this.brd[2][j], f3 = this.brd[3][j];
        if (f1 > 0 && (f1 === f0 || f0 === 0))
            return true;
        if (f2 > 0 && (f2 === f1 || f1 === 0))
            return true;
        if (f3 > 0 && (f3 === f2 || f2 === 0))
            return true;
    }
    return false;
}
BoardArr2d.prototype.canDown = function() {
    for (var j = 0; j < 4; j++) {
        var f0 = this.brd[0][j], f1 = this.brd[1][j], f2 = this.brd[2][j], f3 = this.brd[3][j];
        if (f2 > 0 && (f2 === f3 || f3 === 0))
            return true;
        if (f1 > 0 && (f1 === f2 || f2 === 0))
            return true;
        if (f0 > 0 && (f0 === f1 || f1 === 0))
            return true;
    }
    return false;
}

BoardArr2d.prototype.shiftLeft_unrolled = function(brd) {
    var from = this.brd, to = brd.brd;
    for (var i = 3; i >= 0; i--) {
        var f0 = from[i][0], f1 = from[i][1], f2 = from[i][2], f3 = from[i][3];
        if (f3 === 0) {
            to[i][0] = 0;
            if (f2 === 0) {
                to[i][1] = 0;
                if (f1 === 0) {      // a 0 0 0
                    to[i][2] = 0;
                    to[i][3] = f0;
                } else {
                    if (f0 === f1) {   // a a 0 0
                        to[i][2] = 0;
                        to[i][3] = f0 + 1;
                    } else {           // a b 0 0
                        to[i][2] = f0;
                        to[i][3] = f1;
                    }
                }
            } else {               // f2 !== 0
                if (f1 === 0) {
                    to[i][1] = 0;
                    if (f0 === f2) {   // a 0 a 0
                        to[i][3] = f2 + 1;
                        to[i][2] = 0;
                    } else {           // a 0 b 0
                        to[i][3] = f2;
                        to[i][2] = f0;
                    }
                } else {             // f1 !== 0
                    if (f1 === f2) {   // a b b 0
                        to[i][3] = f2 + 1;
                        to[i][2] = f0;
                        to[i][1] = 0;
                    } else {           // f1 !== f2
                        to[i][3] = f2;
                        if (f0 === f1) { // a a b 0
                            to[i][2] = f1 + 1;
                            to[i][1] = 0;
                        } else {         // a b c 0
                            to[i][2] = f1;
                            to[i][1] = f0;
                        }
                    }
                }
            }
        } else {                 // f3 !== 0
            if (f2 === f3) {
                to[i][3] = f2 + 1;
                to[i][0] = 0;
                if (f1 === 0) { // a 0 b b
                    to[i][2] = f0;
                    to[i][1] = 0;
                } else {
                    if (f0 === f1) { // a a b b
                        to[i][2] = f1 + 1;
                        to[i][1] = 0;
                    } else { // a b c c
                        to[i][2] = f1;
                        to[i][1] = f0;
                    }
                }
            } else { // f2 !== f3
                to[i][3] = f3;
                if (f2 === 0) {
                    to[i][0] = 0;
                    if (f1 === 0) { // a 0 0 b
                        to[i][1] = 0;
                        to[i][2] = f0;
                    } else {
                        if (f0 === f1) { // a a 0 b
                            to[i][1] = 0;
                            to[i][2] = f1 + 1;
                        } else { // a b 0 c
                            to[i][1] = f0;
                            to[i][2] = f1;
                        }
                    }
                } else { // f2 !== 0 && f2 !== f3
                    if (f1 === 0) { // a 0 b c
                        to[i][0] = 0;
                        to[i][1] = f0;
                        to[i][2] = f2;
                    } else {
                        if (f0 === f1) { // a a b c
                            to[i][0] = 0;
                            to[i][1] = f1 + 1;
                            to[i][2] = f2;
                        } else { // a b c d
                            to[i][0] = f0;
                            to[i][1] = f1;
                            to[i][2] = f2;
                        }
                    }
                }
            }
        }
    }
}

BoardArr2d.prototype.shiftRight_mostly_unrolled = function(brd) {
    var updated = false;
    var from = this.brd, to = brd.brd;
    for (var i = 0; i < 4; i++) {
        var moved = true;
        var f0 = from[i][0], f1 = from[i][1], f2 = from[i][2], f3 = from[i][3];
        if (f3 === 0) {
            if (f2 === 0) {
                if (f1 === 0) {  // a 0 0 0
                    if (f0 === 0)
                        moved = false;
                    f3 = f0;
                    f2 = 0;
                } else {         // a b 0 0
                    f3 = f1;
                    f2 = f0;
                }
                f1 = 0;
            } else {             // f2 !== 0 && f3 === 0
                if (f1 === 0) {  // a 0 b 0
                    f3 = f2;
                    f2 = f0;
                } else {         // a b c 0
                    f3 = f2;
                    f2 = f1;
                    f1 = f0;
                }
            }
            f0 = 0;
        } else {                 // f3 !== 0
            if (f2 === 0) {
                if (f1 === 0) {  // a 0 0 b
                    if (f0 === 0)
                        moved = false;
                    f2 = f0;
                } else {         // a b 0 c
                    f2 = f1;
                    f1 = f0;
                }
                f0 = 0;
            } else {             // f2 !== 0 && f3 !== 0
                if (f1 === 0) {  // a 0 b c
                    if (f0 === 0)
                        moved = false;
                    f1 = f0;
                    f0 = 0;
                } else {         // else: a b c d
                    moved = false;
                }
            }
        }
        updated = updated || moved;
        if (f2 === 0) {
            to[i][0] = 0;
            to[i][1] = 0;
            to[i][2] = 0;
            to[i][3] = f3;
            continue;
        }
        if (f1 === 0) {
            to[i][0] = 0;
            to[i][1] = 0;
            if (f2 === f3) {
                updated = true;
                to[i][2] = 0;
                to[i][3] = f3 + 1;
            } else {
                to[i][2] = f2;
                to[i][3] = f3;
            }
            continue;
        }
        if (f2 === f3) {
            updated = true;
            to[i][0] = 0;
            to[i][3] = f3 + 1;
            if (f0 === f1) {
                to[i][1] = 0;
                to[i][2] = f1 + 1;
            } else {
                to[i][1] = f0;
                to[i][2] = f1;
            }
        } else {
            to[i][3] = f3;
            if (f1 === f2) {
                updated = true;
                to[i][0] = 0;
                to[i][1] = f0;
                to[i][2] = f2 + 1;
            } else {
                if (f0 === f1) {
                    updated = true;
                    to[i][0] = 0;
                    to[i][1] = f1+1;
                    to[i][2] = f2;
                } else {
                    to[i][0] = f0;
                    to[i][1] = f1;
                    to[i][2] = f2;
                }
            }
        }
    }
    return updated;
}
BoardArr2d.prototype.shiftDown_mostly_unrolled = function(brd) {
    var updated = false;
    var from = this.brd, to = brd.brd;
    for (var j = 0; j < 4; j++) {
        var moved = true;
        var f0 = from[0][j], f1 = from[1][j], f2 = from[2][j], f3 = from[3][j];
        if (f3 === 0) {
            if (f2 === 0) {
                if (f1 === 0) {  // a 0 0 0
                    if (f0 === 0)
                        moved = false;
                    f3 = f0;
                    f2 = 0;
                } else {         // a b 0 0
                    f3 = f1;
                    f2 = f0;
                }
                f1 = 0;
            } else {             // f2 !== 0 && f3 === 0
                if (f1 === 0) {  // a 0 b 0
                    f3 = f2;
                    f2 = f0;
                } else {         // a b c 0
                    f3 = f2;
                    f2 = f1;
                    f1 = f0;
                }
            }
            f0 = 0;
        } else {                 // f3 !== 0
            if (f2 === 0) {
                if (f1 === 0) {  // a 0 0 b
                    if (f0 === 0)
                        moved = false;
                    f2 = f0;
                } else {         // a b 0 c
                    f2 = f1;
                    f1 = f0;
                }
                f0 = 0;
            } else {             // f2 !== 0 && f3 !== 0
                if (f1 === 0) {  // a 0 b c
                    if (f0 === 0)
                        moved = false;
                    f1 = f0;
                    f0 = 0;
                } else {         // else: a b c d
                    moved = false;
                }
            }
        }
        updated = updated || moved;
        if (f2 === 0) {
            to[0][j] = 0;
            to[1][j] = 0;
            to[2][j] = 0;
            to[3][j] = f3;
            continue;
        }
        if (f1 === 0) {
            to[0][j] = 0;
            to[1][j] = 0;
            if (f2 === f3) {
                to[2][j] = 0;
                to[3][j] = f3 + 1;
                updated = true;
            } else {
                to[2][j] = f2;
                to[3][j] = f3;
            }
            continue;
        }
        if (f2 === f3) {
            updated = true;
            to[0][j] = 0;
            to[3][j] = f3 + 1;
            if (f0 === f1) {
                to[1][j] = 0;
                to[2][j] = f1 + 1;
            } else {
                to[1][j] = f0;
                to[2][j] = f1;
            }
        } else {
            to[3][j] = f3;
            if (f1 === f2) {
                updated = true;
                to[0][j] = 0;
                to[1][j] = f0;
                to[2][j] = f2 + 1;
            } else {
                if (f0 === f1) {
                    updated = true;
                    to[0][j] = 0;
                    to[1][j] = f1+1;
                    to[2][j] = f2;
                } else {
                    to[0][j] = f0;
                    to[1][j] = f1;
                    to[2][j] = f2;
                }
            }
        }
    }
    return updated;
}
BoardArr2d.prototype.shiftLeft_mostly_unrolled = function(brd) {
    var updated = false;
    var from = this.brd, to = brd.brd;
    for (var i = 0; i < 4; i++) {
        var moved = true;
        var f0 = from[i][0], f1 = from[i][1], f2 = from[i][2], f3 = from[i][3];
        if (f0 === 0) {
            if (f1 === 0) {
                if (f2 === 0) {  // 0 0 0 a
                    if (f3 === 0)
                        moved = false;
                    f0 = f3;
                    f1 = 0;
                } else {         // 0 0 a b
                    f0 = f2;
                    f1 = f3;
                }
                f2 = 0;
            } else {             // f1 !== 0 && f0 === 0
                if (f2 === 0) {  // 0 a 0 b
                    f0 = f1;
                    f1 = f3;
                } else {         // 0 a b c
                    f0 = f1;
                    f1 = f2;
                    f2 = f3;
                }
            }
            f3 = 0;
        } else {                 // f0 !== 0
            if (f1 === 0) {
                if (f2 === 0) {  // a 0 0 b
                    if (f3 === 0)
                        moved = false;
                    f1 = f3;
                } else {         // a 0 b c
                    f1 = f2;
                    f2 = f3;
                }
                f3 = 0;
            } else {             // f1 !== 0 && f0 !== 0
                if (f2 === 0) {  // a b 0 c
                    if (f3 === 0)
                        moved = false;
                    f2 = f3;
                    f3 = 0;
                } else {         // else: a b c d
                    moved = false;
                }
            }
        }
        updated = updated || moved;
        if (f1 === 0) {          // a 0 0 0
            to[i][3] = 0;
            to[i][2] = 0;
            to[i][1] = 0;
            to[i][0] = f0;
            continue;
        }
        if (f2 === 0) {
            to[i][3] = 0;
            to[i][2] = 0;
            if (f1 === f0) {     // a a 0 0
                updated = true;
                to[i][1] = 0;
                to[i][0] = f0 + 1;
            } else {             // a b 0 0
                to[i][1] = f1;
                to[i][0] = f0;
            }
            continue;
        }
        if (f1 === f0) {
            updated = true;
            to[i][3] = 0;
            to[i][0] = f0 + 1;
            if (f3 === f2) {     // a a b b
                to[i][2] = 0;
                to[i][1] = f2 + 1;
            } else {             // a a b c
                to[i][2] = f3;
                to[i][1] = f2;
            }
        } else {
            to[i][0] = f0;
            if (f2 === f1) {     // a b b c
                updated = true;
                to[i][3] = 0;
                to[i][2] = f3;
                to[i][1] = f1 + 1;
            } else {
                to[i][1] = f1;
                if (f2 === f3) { // a b c c
                    updated = true;
                    to[i][3] = 0;
                    to[i][2] = f2+1;
                } else {         // a b c d
                    to[i][3] = f3;
                    to[i][2] = f2;
                }
            }
        }
   }
    return updated;
}
BoardArr2d.prototype.shiftUp_mostly_unrolled = function(brd) {
    var updated = false;
    var from = this.brd, to = brd.brd;
    for (var j = 0; j < 4; j++) {
        var moved = true;
        var f0 = from[0][j], f1 = from[1][j], f2 = from[2][j], f3 = from[3][j];
        if (f0 === 0) {
            if (f1 === 0) {
                if (f2 === 0) {  // 0 0 0 a
                    if (f3 === 0)
                        moved = false;
                    f0 = f3;
                    f1 = 0;
                } else {         // 0 0 a b
                    f0 = f2;
                    f1 = f3;
                }
                f2 = 0;
            } else {             // f1 !== 0 && f0 === 0
                if (f2 === 0) {  // 0 a 0 b
                    f0 = f1;
                    f1 = f3;
                } else {         // 0 a b c
                    f0 = f1;
                    f1 = f2;
                    f2 = f3;
                }
            }
            f3 = 0;
        } else {                 // f0 !== 0
            if (f1 === 0) {
                if (f2 === 0) {  // a 0 0 b
                    if (f3 === 0)
                        moved = false;
                    f1 = f3;
                } else {         // a 0 b c
                    f1 = f2;
                    f2 = f3;
                }
                f3 = 0;
            } else {             // f1 !== 0 && f0 !== 0
                if (f2 === 0) {  // a b 0 c
                    if (f3 === 0)
                        moved = false;
                    f2 = f3;
                    f3 = 0;
                } else {         // else: a b c d
                    moved = false;
                }
            }
        }
        updated = updated || moved;
        if (f1 === 0) {          // a 0 0 0
            to[3][j] = 0;
            to[2][j] = 0;
            to[1][j] = 0;
            to[0][j] = f0;
            continue;
        }
        if (f2 === 0) {
            to[3][j] = 0;
            to[2][j] = 0;
            if (f1 === f0) {     // a a 0 0
                updated = true;
                to[1][j] = 0;
                to[0][j] = f0 + 1;
            } else {             // a b 0 0
                to[1][j] = f1;
                to[0][j] = f0;
            }
            continue;
        }
        if (f1 === f0) {
            to[3][j] = 0;
            to[0][j] = f0 + 1;
            if (f3 === f2) {     // a a b b
                updated = true;
                to[2][j] = 0;
                to[1][j] = f2 + 1;
            } else {             // a a b c
                to[2][j] = f3;
                to[1][j] = f2;
            }
        } else {
            to[0][j] = f0;
            if (f2 === f1) {     // a b b c
                updated = true;
                to[3][j] = 0;
                to[2][j] = f3;
                to[1][j] = f1 + 1;
            } else {
                to[1][j] = f1;
                if (f2 === f3) { // a b c c
                    updated = true;
                    to[3][j] = 0;
                    to[2][j] = f2+1;
                } else {         // a b c d
                    to[3][j] = f3;
                    to[2][j] = f2;
                }
            }
        }
    }
    return updated;
}

BoardArr2d.prototype.left = BoardArr2d.prototype.shiftLeft_mostly_unrolled;
BoardArr2d.prototype.right = BoardArr2d.prototype.shiftRight_mostly_unrolled;
BoardArr2d.prototype.up = BoardArr2d.prototype.shiftUp_mostly_unrolled;
BoardArr2d.prototype.down = BoardArr2d.prototype.shiftDown_mostly_unrolled;



////////////////////////////////////////////////////////////////
// Board as properties.
////////////////////////////////////////////////////////////////

/* Create board as properties of object.
 * Extract data from 'brd' (which is 2d array) if present. */
function BoardObj(brd) {
    if (brd)
        this.brd = { aa: brd[0][0], ab: brd[0][1], ac: brd[0][2], ad: brd[0][3],
                     ba: brd[1][0], bb: brd[1][1], bc: brd[1][2], bd: brd[1][3],
                     ca: brd[2][0], cb: brd[2][1], cc: brd[2][2], cd: brd[2][3],
                     da: brd[3][0], db: brd[3][1], dc: brd[3][2], dd: brd[3][3] };
    else
        this.brd = { aa: 0, ab: 0, ac: 0, ad: 0,
                     ba: 0, bb: 0, bc: 0, bd: 0,
                     ca: 0, cb: 0, cc: 0, cd: 0,
                     da: 0, db: 0, dc: 0, dd: 0 };
}
BoardObj.arrMap = [["aa", "ab", "ac", "ad"], ["ba", "bb", "bc", "bd"], ["ca", "cb", "cc", "cd"], ["da", "db", "dc", "dd"]];
/* Doesn't designed to be efficient. */
BoardObj.prototype.get = function(i, j) {
    return this.brd[BoardObj.arrMap[i][j]];
}
/* Doesn't designed to be efficient. */
BoardObj.prototype.set = function(i, j, val) {
    this.brd[BoardObj.arrMap[i][j]] = val;
}
/* Return and optionally fill 2d board.
 * Doesn't designed to be efficient. */
BoardObj.prototype.exportTo = function(brd) {
    brd = brd || [[],[],[],[]];
    for (var i = 0; i < 4; i++)
        for (var j = 0; j < 4; j++)
            brd[i][j] = this.brd[4*i + j];
    return brd;
}