2048.html
changeset 10 70ece7f758a0
parent 7 d8106c8c0481
child 11 2264f7a3f7e0
equal deleted inserted replaced
9:961eff57a23f 10:70ece7f758a0
     3 <head>
     3 <head>
     4   <title>2048 AI</title>
     4   <title>2048 AI</title>
     5   <meta name="viewport" content="width=device-width; initial-scale=1.0"/>
     5   <meta name="viewport" content="width=device-width; initial-scale=1.0"/>
     6   <meta charset="utf-8"/>
     6   <meta charset="utf-8"/>
     7 
     7 
       
     8   <script src="rule.js"></script>
     8   <script src="board.js"></script>
     9   <script src="board.js"></script>
     9   <script src="perf.js"></script>
    10   <script src="perf.js"></script>
       
    11   <script src="ai.js"></script>
    10 
    12 
    11   <style>
    13   <style>
    12     body {
    14     body {
    13       width: 100%;
    15       width: 100%;
    14     }
    16     }
   110     </div>
   112     </div>
   111   </div>
   113   </div>
   112 
   114 
   113   <script>
   115   <script>
   114     "use strict";
   116     "use strict";
   115 
       
   116     var board = {};
       
   117     board.create = function() {
       
   118       var brd = [];
       
   119       for (var i = 0; i < 4; i++) {
       
   120         brd[i] = [];
       
   121         for (var j = 0; j < 4; j++) {
       
   122           brd[i][j] = 0;
       
   123         }
       
   124       }
       
   125       return brd;
       
   126     }
       
   127     board.copy = function(from, to) {
       
   128       for (var i = 0; i < 4; i++) {
       
   129         for (var j = 0; j < 4; j++) {
       
   130           to[i][j] = from[i][j];
       
   131         }
       
   132       }
       
   133     }
       
   134     board.freeCnt = function(brd) {
       
   135       var cnt = 0;
       
   136       for (var i = 0; i < 4; i++) {
       
   137         for (var j = 0; j < 4; j++) {
       
   138           if (brd[i][j] === 0)
       
   139             cnt++;
       
   140         }
       
   141       }
       
   142       return cnt;
       
   143     }
       
   144     board.gameOver = function(brd) {
       
   145       if (board.freeCnt(brd) > 0)
       
   146         return false;
       
   147       for (var i = 0; i < 4; i++) {
       
   148         for (var j = 0; j < 3; j++) {
       
   149           if (brd[i][j] === brd[i][j+1])
       
   150             return false;
       
   151         }
       
   152       }
       
   153       for (var j = 0; j < 4; j++) {
       
   154         for (var i = 0; i < 3; i++) {
       
   155           if (brd[i][j] === brd[i+1][j])
       
   156             return false;
       
   157         }
       
   158       }
       
   159       return true;
       
   160     }
       
   161     board.putRandom = function(brd) {
       
   162       var cnt = board.freeCnt(brd);
       
   163       cnt = Math.floor(Math.random() * cnt)+1;
       
   164       for (var i = 0; i < 4 && cnt > 0; i++) {
       
   165         for (var j = 0; j < 4 && cnt > 0; j++) {
       
   166           if (brd[i][j] !== 0)
       
   167             continue;
       
   168           if (cnt === 1)
       
   169             brd[i][j] = (Math.random() > .9) ? 2 : 1;
       
   170           cnt--;
       
   171         }
       
   172       }
       
   173     }
       
   174     /* http://www.reddit.com/r/2048/comments/214njx/highest_possible_score_for_2048_warning_math */
       
   175     var boardScoreTbl = [0];
       
   176     for (var i = 1, exp = 2; i < 16; i++, exp *= 2) {
       
   177       boardScoreTbl[i] = (i-1)*exp;
       
   178     }
       
   179     board.score = function(brd) {
       
   180       var score = 0;
       
   181       var max = 0;
       
   182       for (var i = 0; i < 4; i++) {
       
   183         for (var j = 0; j < 4; j++) {
       
   184           var val = brd[i][j];
       
   185           score += boardScoreTbl[val];
       
   186           if (max < val)
       
   187             max = val;
       
   188         }
       
   189       }
       
   190       return {score: score, max: Math.pow(2, max)};
       
   191     }
       
   192 
       
   193     board.row = {};
       
   194     board.row.init = function() {
       
   195       return {stack: [], curr: 0};
       
   196     }
       
   197     board.row.push = function(state, val) {
       
   198       if (val === 0)
       
   199         return;
       
   200       if (state.curr === 0) {
       
   201         state.curr = val;
       
   202         return;
       
   203       }
       
   204       if (state.curr === val) {
       
   205         state.stack.push(state.curr+1);
       
   206         state.curr = 0;
       
   207       } else {
       
   208         state.stack.push(state.curr);
       
   209         state.curr = val;
       
   210       }
       
   211     }
       
   212     board.row.finish = function(state) {
       
   213       if (state.curr !== 0)
       
   214         state.stack.push(state.curr);
       
   215     }
       
   216     board.move = {};
       
   217     board.move.up = function(brd) {
       
   218       var updated = false;
       
   219       for (var j = 0; j < 4; j++) {
       
   220         var state = board.row.init();
       
   221         for (var i = 0; i < 4; i++) {
       
   222           board.row.push(state, brd[i][j]);
       
   223         }
       
   224         board.row.finish(state);
       
   225         for (var i = 0; i < state.stack.length; i++) {
       
   226           if (brd[i][j] !== state.stack[i])
       
   227             updated = true;
       
   228           brd[i][j] = state.stack[i];
       
   229         }
       
   230         for (; i < 4; i++) {
       
   231           if (brd[i][j] !== 0)
       
   232             updated = true;
       
   233           brd[i][j] = 0;
       
   234         }
       
   235       }
       
   236       return updated;
       
   237     };
       
   238     board.move.down = function(brd) {
       
   239       var updated = false;
       
   240       for (var j = 0; j < 4; j++) {
       
   241         var state = board.row.init();
       
   242         for (var i = 3; i >= 0; i--) {
       
   243           board.row.push(state, brd[i][j]);
       
   244         }
       
   245         board.row.finish(state);
       
   246         for (var i = 0; i < state.stack.length; i++) {
       
   247           if (brd[3-i][j] !== state.stack[i])
       
   248             updated = true;
       
   249           brd[3-i][j] = state.stack[i];
       
   250         }
       
   251         for (; i < 4; i++) {
       
   252           if (brd[3-i][j] !== 0)
       
   253             updated = true;
       
   254           brd[3-i][j] = 0;
       
   255         }
       
   256       }
       
   257       return updated;
       
   258     };
       
   259     board.move.left = function(brd) {
       
   260       var updated = false;
       
   261       for (var i = 0; i < 4; i++) {
       
   262         var state = board.row.init();
       
   263         for (var j = 0; j < 4; j++) {
       
   264           board.row.push(state, brd[i][j]);
       
   265         }
       
   266         board.row.finish(state);
       
   267         for (var j = 0; j < state.stack.length; j++) {
       
   268           if (brd[i][j] !== state.stack[j])
       
   269             updated = true;
       
   270           brd[i][j] = state.stack[j];
       
   271         }
       
   272         for (; j < 4; j++) {
       
   273           if (brd[i][j] !== 0)
       
   274             updated = true;
       
   275           brd[i][j] = 0;
       
   276         }
       
   277       }
       
   278       return updated;
       
   279     };
       
   280     board.move.right = function(brd) {
       
   281       var updated = false;
       
   282       for (var i = 0; i < 4; i++) {
       
   283         var state = board.row.init();
       
   284         for (var j = 3; j >= 0; j--) {
       
   285           board.row.push(state, brd[i][j]);
       
   286         }
       
   287         board.row.finish(state);
       
   288         for (var j = 0; j < state.stack.length; j++) {
       
   289           if (brd[i][3-j] !== state.stack[j])
       
   290             updated = true;
       
   291           brd[i][3-j] = state.stack[j];
       
   292         }
       
   293         for (; j < 4; j++) {
       
   294           if (brd[i][3-j] !== 0)
       
   295             updated = true;
       
   296           brd[i][3-j] = 0;
       
   297         }
       
   298       }
       
   299       return updated;
       
   300     };
       
   301 
   117 
   302     var boardDom = document.getElementById("board");
   118     var boardDom = document.getElementById("board");
   303     var ui = {};
   119     var ui = {};
   304     ui.board = {};
   120     ui.board = {};
   305     ui.board.set = function(i, j, val) {
   121     ui.board.set = function(i, j, val) {
   405         ui.message.set("Game over!");
   221         ui.message.set("Game over!");
   406         return;
   222         return;
   407       }
   223       }
   408       var tmpBrd = board.create();
   224       var tmpBrd = board.create();
   409       board.copy(board.current, tmpBrd);
   225       board.copy(board.current, tmpBrd);
   410       var fn = ai.current(tmpBrd);
   226       var move = ui.ai.analyse(tmpBrd);
   411       if (typeof fn === 'undefined') {
   227       if (typeof move === 'undefined') {
   412         ui.message.set("I don't know how to move!");
   228         ui.message.set("I don't know how to move!");
   413         return;
   229         return;
   414       }
   230       }
   415       var updated = board.move[fn].call(null, board.current);
   231       var updated = board.move[move].call(null, board.current);
   416       if (updated) {
   232       if (updated) {
   417         board.putRandom(board.current);
   233         board.putRandom(board.current);
   418         ui.board.update(board.current);
   234         ui.board.update(board.current);
   419         ui.score.update(board.current);
   235         ui.score.update(board.current);
   420       } else {
   236       } else {
   428       var step = 0;
   244       var step = 0;
   429       var tsFrom = new Date().getTime();
   245       var tsFrom = new Date().getTime();
   430       while (!board.gameOver(board.current)) {
   246       while (!board.gameOver(board.current)) {
   431         var tmpBrd = board.create();
   247         var tmpBrd = board.create();
   432         board.copy(board.current, tmpBrd);
   248         board.copy(board.current, tmpBrd);
   433         var fn = ai.current(tmpBrd);
   249         var move = ui.ai.analyse(tmpBrd);
   434         if (typeof fn === 'undefined') {
   250         if (typeof move === 'undefined') {
   435           ui.message.set("I don't know how to move!");
   251           ui.message.set("I don't know how to move!");
   436           return;
   252           return;
   437         }
   253         }
   438         var updated = board.move[fn].call(null, board.current);
   254         var updated = board.move[move].call(null, board.current);
   439         if (updated) {
   255         if (updated) {
   440           board.putRandom(board.current);
   256           board.putRandom(board.current);
   441         } else {
   257         } else {
   442           ui.board.update(board.current);
   258           ui.board.update(board.current);
   443           ui.score.update(board.current);
   259           ui.score.update(board.current);
   452       ui.score.speed(step*1000.0/(tsTo-tsFrom), step);
   268       ui.score.speed(step*1000.0/(tsTo-tsFrom), step);
   453       ui.message.set("Game over!");
   269       ui.message.set("Game over!");
   454     }
   270     }
   455     document.getElementById("finish").addEventListener("click", finish);
   271     document.getElementById("finish").addEventListener("click", finish);
   456 
   272 
   457     var ai = {};
   273     ////////////////////////////////////////////////////////////////
   458 
   274     // Register AIs.
   459     ai.random = function(brd) {
   275 
   460       var tmpBrd = board.create();
   276     ui.brdEngine = BoardArr2d; // TODO make user selectable
   461       do {
   277 
   462         var action = ["up", "down", "left", "right"][Math.floor(Math.random()*4)];
       
   463         board.copy(brd, tmpBrd);
       
   464       } while (!board.move[action](tmpBrd));
       
   465       return action;
       
   466     }
       
   467     document.getElementById("ai-random").addEventListener("click", function() {
   278     document.getElementById("ai-random").addEventListener("click", function() {
   468       ai.current = ai.random;
   279       ui.ai = new ai.random(ui.brdEngine);
   469     });
   280     });
   470 
       
   471     ai.nextMaxScore = function(brd) {
       
   472       var tmpBrd = board.create();
       
   473       board.copy(brd, tmpBrd);
       
   474       var maxScore = -1;
       
   475       var action;
       
   476       if (board.move.up(tmpBrd)) {
       
   477         maxScore = board.score(tmpBrd).score;
       
   478         action = "up";
       
   479       }
       
   480       board.copy(brd, tmpBrd);
       
   481       if (board.move.left(tmpBrd)) {
       
   482         var score = board.score(tmpBrd).score;
       
   483         if (maxScore < score) {
       
   484           action = "left";
       
   485           maxScore = score;
       
   486         }
       
   487       }
       
   488       board.copy(brd, tmpBrd);
       
   489       if (board.move.down(tmpBrd)) {
       
   490         var score = board.score(tmpBrd).score;
       
   491         if (maxScore < score) {
       
   492           action = "down";
       
   493           maxScore = score;
       
   494         }
       
   495       }
       
   496       board.copy(brd, tmpBrd);
       
   497       if (board.move.right(tmpBrd)) {
       
   498         var score = board.score(tmpBrd).score;
       
   499         if (maxScore < score) {
       
   500           action = "right";
       
   501           maxScore = score;
       
   502         }
       
   503       }
       
   504       return action;
       
   505     }
       
   506     document.getElementById("ai-next-max-score").addEventListener("click", function() {
   281     document.getElementById("ai-next-max-score").addEventListener("click", function() {
   507       ai.current = ai.nextMaxScore;
   282       ui.ai = new ai.nextMaxScore(ui.brdEngine);
   508     });
   283     });
   509 
       
   510 
       
   511     ai.nextMaxValue = function(brd) {
       
   512       var tmpBrd = board.create();
       
   513       board.copy(brd, tmpBrd);
       
   514       var maxMax = -1;
       
   515       var action;
       
   516       if (board.move.up(tmpBrd)) {
       
   517         maxMax = board.score(tmpBrd).max;
       
   518         action = "up";
       
   519       }
       
   520       board.copy(brd, tmpBrd);
       
   521       if (board.move.left(tmpBrd)) {
       
   522         var max = board.score(tmpBrd).max;
       
   523         if (maxMax < max) {
       
   524           action = "left";
       
   525           maxMax = max;
       
   526         }
       
   527       }
       
   528       board.copy(brd, tmpBrd);
       
   529       if (board.move.down(tmpBrd)) {
       
   530         var max = board.score(tmpBrd).max;
       
   531         if (maxMax < max) {
       
   532           action = "down";
       
   533           maxMax = max;
       
   534         }
       
   535       }
       
   536       board.copy(brd, tmpBrd);
       
   537       if (board.move.right(tmpBrd)) {
       
   538         var max = board.score(tmpBrd).max;
       
   539         if (maxMax < max) {
       
   540           action = "right";
       
   541           maxMax = max;
       
   542         }
       
   543       }
       
   544       return action;
       
   545     }
       
   546     document.getElementById("ai-next-max-value").addEventListener("click", function() {
   284     document.getElementById("ai-next-max-value").addEventListener("click", function() {
   547       ai.current = ai.nextMaxValue;
   285       ui.ai = new ai.nextMaxValue(ui.brdEngine);
   548     });
   286     });
   549 
   287 
   550   </script>
   288   </script>
   551   
   289   
   552 </body>
   290 </body>