Moves for 2d array board + test toolkit.
--- a/2048.html Sun Sep 07 00:33:47 2014 +0300
+++ b/2048.html Sun Sep 07 03:33:33 2014 +0300
@@ -80,6 +80,7 @@
<button id="up">up</button>
<button id="down">down</button>
<button id="right">right</button>
+ <button id="test">test</button>
</div>
<h1>AI</h1>
<div>
@@ -205,6 +206,11 @@
}
return false;
});
+
+ document.getElementById("test").addEventListener("click", function() {
+ console.log("x1: %o", ui.brdEngine);
+ board.move.replaceByBoardJS(ui.brdEngine);
+ });
ui.message = {};
var messageDom = document.getElementById("message-area");
--- a/ai.js Sun Sep 07 00:33:47 2014 +0300
+++ b/ai.js Sun Sep 07 03:33:33 2014 +0300
@@ -5,6 +5,7 @@
// Each strategy is a function that except current board position as 2d array and context from
// previous call to share state/precomputed values between calls.
+
////////////////////////////////////////////////////////////////
// Random AI.
@@ -14,25 +15,31 @@
this.brdEngine = brdEngine;
}
ai.random.prototype.analyse = function(brd) {
- var tmpBrd = new this.brdEngine(brd);
+ var origBrd = new this.brdEngine(brd);
while (true) {
var rnd = Math.floor(Math.random()*4);
- if (tmpBrd[["canUp", "canDown", "canLeft", "canRight"][rnd]]())
+ if (origBrd[["canUp", "canDown", "canLeft", "canRight"][rnd]]())
return ["up", "down", "left", "right"][rnd];
}
}
+/* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
+ai.random.prototype.cleanup = function() { }
+
////////////////////////////////////////////////////////////////
// 1 level deep on max scores.
////////////////////////////////////////////////////////////////
-ai.nextMaxScore = function(brd) {
- var tmpBrd = board.create();
- board.copy(brd, tmpBrd);
+ai.nextMaxScore = function(brdEngine) {
+ this.brdEngine = brdEngine;
+}
+ai.nextMaxScore.prototype.analyse = function(brd) {
+ var origBrd = new this.brdEngine(brd);
+ var nextBrd = new this.brdEngine();
var maxScore = -1;
var action;
- if (board.move.up(tmpBrd)) {
+ if (origBrd.up(nextBrd)) {
maxScore = board.score(tmpBrd).score;
action = "up";
}
@@ -62,13 +69,19 @@
}
return action;
}
+/* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
+ai.nextMaxScore.prototype.cleanup = function() { }
+
////////////////////////////////////////////////////////////////
// 1 level deep on max value.
////////////////////////////////////////////////////////////////
-ai.nextMaxValue = function(brd) {
+ai.nextMaxValue = function(brdEngine) {
+ this.brdEngine = brdEngine;
+}
+ai.nextMaxValue.prototype.analyse = function(brd) {
var tmpBrd = board.create();
board.copy(brd, tmpBrd);
var maxMax = -1;
@@ -103,4 +116,6 @@
}
return action;
}
+/* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
+ai.nextMaxScore.prototype.cleanup = function() { }
--- a/board.js Sun Sep 07 00:33:47 2014 +0300
+++ b/board.js Sun Sep 07 03:33:33 2014 +0300
@@ -53,14 +53,14 @@
this.brd[i][j] = brd[i][j];
}
}
-BoardArr.prototype.get = function(i, j) {
+BoardArr2d.prototype.get = function(i, j) {
return this.brd[i][j];
}
-BoardArr.prototype.set = function(i, j, val) {
+BoardArr2d.prototype.set = function(i, j, val) {
this.brd[i][j] = val;
}
/* Return and optionally fill 2d board. */
-BoardArr.prototype.exportTo = function(brd) {
+BoardArr2d.prototype.exportTo = function(brd) {
brd = brd || [[],[],[],[]];
for (var i = 0; i < 4; i++)
for (var j = 0; j < 4; j++)
@@ -74,6 +74,17 @@
brd.brd[i][j] = this.brd[i][j];
return brd;
}
+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.canRight = function() {
for (var i = 0; i < 4; i++) {
@@ -225,47 +236,58 @@
}
}
-BoardArr2d.prototype.shiftLeft_mostly_unrolled = function(brd) {
+BoardArr2d.prototype.shiftRight_mostly_unrolled = function(brd) {
+ var updated = false;
var from = this.brd, to = brd.brd;
- for (var i = 3; i >= 0; i--) {
+ 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 (f1 === 0) { // a 0 0 0
+ if (f0 === 0)
+ moved = false;
f3 = f0;
f2 = 0;
- } else { // a b 0 0
+ } else { // a b 0 0
f3 = f1;
f2 = f0;
}
f1 = 0;
- } else { // f2 !== 0 && f3 === 0
- if (f1 === 0) { // a 0 b 0
+ } else { // f2 !== 0 && f3 === 0
+ if (f1 === 0) { // a 0 b 0
f3 = f2;
f2 = f0;
- } else { // a b c 0
+ } else { // a b c 0
f3 = f2;
f2 = f1;
f1 = f0;
}
}
f0 = 0;
- } else { // f3 !== 0
+ } else { // f3 !== 0
if (f2 === 0) {
- if (f1 === 0) { // a 0 0 b
+ if (f1 === 0) { // a 0 0 b
+ if (f0 === 0)
+ moved = false;
f2 = f0;
- } else { // a b 0 c
+ } else { // a b 0 c
f2 = f1;
f1 = f0;
}
f0 = 0;
- } else { // f2 !== 0 && f3 !== 0
- if (f1 === 0) { // a 0 b c
+ } else { // f2 !== 0 && f3 !== 0
+ if (f1 === 0) { // a 0 b c
+ if (f0 === 0)
+ moved = false;
f1 = f0;
f0 = 0;
- } // else: a b c d
+ } else { // else: a b c d
+ moved = false;
+ }
}
}
+ updated = updated || moved;
if (f2 === 0) {
to[i][0] = 0;
to[i][1] = 0;
@@ -277,6 +299,7 @@
to[i][0] = 0;
to[i][1] = 0;
if (f2 === f3) {
+ updated = true;
to[i][2] = 0;
to[i][3] = f3 + 1;
} else {
@@ -286,6 +309,7 @@
continue;
}
if (f2 === f3) {
+ updated = true;
to[i][0] = 0;
to[i][3] = f3 + 1;
if (f0 === f1) {
@@ -298,17 +322,347 @@
} else {
to[i][3] = f3;
if (f1 === f2) {
+ updated = true;
to[i][0] = 0;
to[i][1] = f0;
to[i][2] = f2 + 1;
} else {
- to[i][0] = f0;
- to[i][1] = f1;
- to[i][2] = f2;
+ 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;
--- a/rule.js Sun Sep 07 00:33:47 2014 +0300
+++ b/rule.js Sun Sep 07 03:33:33 2014 +0300
@@ -101,7 +101,7 @@
state.stack.push(state.curr);
}
board.move = {};
-board.move.up = function(brd) {
+board.move.up = board.move.upOrig = function(brd) {
var updated = false;
for (var j = 0; j < 4; j++) {
var state = board.row.init();
@@ -122,7 +122,7 @@
}
return updated;
};
-board.move.down = function(brd) {
+board.move.down = board.move.downOrig = function(brd) {
var updated = false;
for (var j = 0; j < 4; j++) {
var state = board.row.init();
@@ -143,7 +143,7 @@
}
return updated;
};
-board.move.left = function(brd) {
+board.move.left = board.move.leftOrig = function(brd) {
var updated = false;
for (var i = 0; i < 4; i++) {
var state = board.row.init();
@@ -164,7 +164,7 @@
}
return updated;
};
-board.move.right = function(brd) {
+board.move.right = board.move.rightOrig = function(brd) {
var updated = false;
for (var i = 0; i < 4; i++) {
var state = board.row.init();
@@ -185,3 +185,41 @@
}
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");
+}