2048.html
changeset 0 2821cc5e0189
child 1 8fb744f2df13
equal deleted inserted replaced
-1:000000000000 0:2821cc5e0189
       
     1 <!DOCTYPE html>
       
     2 <html>
       
     3 <head>
       
     4   <title>2048 AI</title>
       
     5   <meta name="viewport" content="width=device-width; initial-scale=1.0"/>
       
     6   <meta charset="utf-8"/>
       
     7 
       
     8   <style>
       
     9     body {
       
    10       width: 100%;
       
    11     }
       
    12     h1, .score-area, .control-area, #message-area {
       
    13       text-align: center;
       
    14     }
       
    15     #board {
       
    16       margin: 10px auto;
       
    17     }
       
    18     #board td {
       
    19       width: 40px;
       
    20       height: 40px;
       
    21       border: 1px solid red;
       
    22       margin: 0;
       
    23       text-align: center;
       
    24     }
       
    25   </style>
       
    26 </head>
       
    27 <body>
       
    28 
       
    29   <h1>2048</h1>
       
    30 
       
    31   <div class="score-area">Score: <span id="score">0</span>, Max: <span id="max">0</span></div>
       
    32 
       
    33   <div id="message-area"></div>
       
    34 
       
    35   <table id="board">
       
    36     <tr>
       
    37       <td></td>
       
    38       <td></td>
       
    39       <td></td>
       
    40       <td></td>
       
    41     </tr>
       
    42     <tr>
       
    43       <td></td>
       
    44       <td></td>
       
    45       <td></td>
       
    46       <td></td>
       
    47     </tr>
       
    48     <tr>
       
    49       <td></td>
       
    50       <td></td>
       
    51       <td></td>
       
    52       <td></td>
       
    53     </tr>
       
    54     <tr>
       
    55       <td></td>
       
    56       <td></td>
       
    57       <td></td>
       
    58       <td></td>
       
    59     </tr>
       
    60   </table>
       
    61 
       
    62   <div class="control-area">
       
    63     <div>
       
    64       <button id="start">Start</button>
       
    65       <button id="step">Step</button>
       
    66       <button id="loop">Loop</button>
       
    67       <button id="finish">Finish</button>
       
    68     </div>
       
    69     <div>
       
    70       <button id="left">left</button>
       
    71       <button id="up">up</button>
       
    72       <button id="down">down</button>
       
    73       <button id="right">right</button>
       
    74     </div>
       
    75     <h1>AI</h1>
       
    76     <div>
       
    77       <button id="ai-random">random</button>
       
    78       <button id="ai-next-max-score">next max score</button>
       
    79       <button id="ai-next-max-value">next max value</button>
       
    80     </div>
       
    81   </div>
       
    82 
       
    83   <script>
       
    84     "use strict";
       
    85 
       
    86     var board = {};
       
    87     board.create = function() {
       
    88       var brd = [];
       
    89       for (var i = 0; i < 4; i++) {
       
    90         brd[i] = [];
       
    91         for (var j = 0; j < 4; j++) {
       
    92           brd[i][j] = 0;
       
    93         }
       
    94       }
       
    95       return brd;
       
    96     }
       
    97     board.copy = function(from, to) {
       
    98       for (var i = 0; i < 4; i++) {
       
    99         for (var j = 0; j < 4; j++) {
       
   100           to[i][j] = from[i][j];
       
   101         }
       
   102       }
       
   103     }
       
   104     board.freeCnt = function(brd) {
       
   105       var cnt = 0;
       
   106       for (var i = 0; i < 4; i++) {
       
   107         for (var j = 0; j < 4; j++) {
       
   108           if (brd[i][j] === 0)
       
   109             cnt++;
       
   110         }
       
   111       }
       
   112       return cnt;
       
   113     }
       
   114     board.gameOver = function(brd) {
       
   115       if (board.freeCnt(brd) > 0)
       
   116         return false;
       
   117       for (var i = 0; i < 4; i++) {
       
   118         for (var j = 0; j < 3; j++) {
       
   119           if (brd[i][j] === brd[i][j+1])
       
   120             return false;
       
   121         }
       
   122       }
       
   123       for (var j = 0; j < 4; j++) {
       
   124         for (var i = 0; i < 3; i++) {
       
   125           if (brd[i][j] === brd[i+1][j])
       
   126             return false;
       
   127         }
       
   128       }
       
   129       return true;
       
   130     }
       
   131     board.random = function(brd) {
       
   132       var cnt = board.freeCnt(brd);
       
   133       cnt = Math.floor(Math.random() * cnt)+1;
       
   134       for (var i = 0; i < 4 && cnt > 0; i++) {
       
   135         for (var j = 0; j < 4 && cnt > 0; j++) {
       
   136           if (brd[i][j] !== 0)
       
   137             continue;
       
   138           if (cnt === 1)
       
   139             brd[i][j] = 2;
       
   140           cnt--;
       
   141         }
       
   142       }
       
   143     }
       
   144     /* http://www.reddit.com/r/2048/comments/214njx/highest_possible_score_for_2048_warning_math */
       
   145     board.score = function(brd) {
       
   146       var score = 0;
       
   147       var max = 0;
       
   148       for (var i = 0; i < 4; i++) {
       
   149         for (var j = 0; j < 4; j++) {
       
   150           var val = brd[i][j];
       
   151           if (val > 2)
       
   152             score += Math.log2(val) * val;
       
   153           if (max < val)
       
   154             max = val;
       
   155         }
       
   156       }
       
   157       return {score: score, max: max};
       
   158     }
       
   159 
       
   160     board.row = {};
       
   161     board.row.init = function() {
       
   162       return {stack: [], curr: 0};
       
   163     }
       
   164     board.row.push = function(state, val) {
       
   165       if (val === 0)
       
   166         return;
       
   167       if (state.curr === 0) {
       
   168         state.curr = val;
       
   169         return;
       
   170       }
       
   171       if (state.curr === val) {
       
   172         state.stack.push(state.curr*2);
       
   173         state.curr = 0;
       
   174       } else {
       
   175         state.stack.push(state.curr);
       
   176         state.curr = val;
       
   177       }
       
   178     }
       
   179     board.row.finish = function(state) {
       
   180       if (state.curr !== 0)
       
   181         state.stack.push(state.curr);
       
   182     }
       
   183     board.move = {};
       
   184     board.move.up = function(brd) {
       
   185       var updated = false;
       
   186       for (var j = 0; j < 4; j++) {
       
   187         var state = board.row.init();
       
   188         for (var i = 0; i < 4; i++) {
       
   189           board.row.push(state, brd[i][j]);
       
   190         }
       
   191         board.row.finish(state);
       
   192         for (var i = 0; i < state.stack.length; i++) {
       
   193           if (brd[i][j] !== state.stack[i])
       
   194             updated = true;
       
   195           brd[i][j] = state.stack[i];
       
   196         }
       
   197         for (; i < 4; i++) {
       
   198           if (brd[i][j] !== 0)
       
   199             updated = true;
       
   200           brd[i][j] = 0;
       
   201         }
       
   202       }
       
   203       return updated;
       
   204     };
       
   205     board.move.down = function(brd) {
       
   206       var updated = false;
       
   207       for (var j = 0; j < 4; j++) {
       
   208         var state = board.row.init();
       
   209         for (var i = 3; i >= 0; i--) {
       
   210           board.row.push(state, brd[i][j]);
       
   211         }
       
   212         board.row.finish(state);
       
   213         for (var i = 0; i < state.stack.length; i++) {
       
   214           if (brd[3-i][j] !== state.stack[i])
       
   215             updated = true;
       
   216           brd[3-i][j] = state.stack[i];
       
   217         }
       
   218         for (; i < 4; i++) {
       
   219           if (brd[3-i][j] !== 0)
       
   220             updated = true;
       
   221           brd[3-i][j] = 0;
       
   222         }
       
   223       }
       
   224       return updated;
       
   225     };
       
   226     board.move.left = function(brd) {
       
   227       var updated = false;
       
   228       for (var i = 0; i < 4; i++) {
       
   229         var state = board.row.init();
       
   230         for (var j = 0; j < 4; j++) {
       
   231           board.row.push(state, brd[i][j]);
       
   232         }
       
   233         board.row.finish(state);
       
   234         for (var j = 0; j < state.stack.length; j++) {
       
   235           if (brd[i][j] !== state.stack[j])
       
   236             updated = true;
       
   237           brd[i][j] = state.stack[j];
       
   238         }
       
   239         for (; j < 4; j++) {
       
   240           if (brd[i][j] !== 0)
       
   241             updated = true;
       
   242           brd[i][j] = 0;
       
   243         }
       
   244       }
       
   245       return updated;
       
   246     };
       
   247     board.move.right = function(brd) {
       
   248       var updated = false;
       
   249       for (var i = 0; i < 4; i++) {
       
   250         var state = board.row.init();
       
   251         for (var j = 3; j >= 0; j--) {
       
   252           board.row.push(state, brd[i][j]);
       
   253         }
       
   254         board.row.finish(state);
       
   255         for (var j = 0; j < state.stack.length; j++) {
       
   256           if (brd[i][3-j] !== state.stack[j])
       
   257             updated = true;
       
   258           brd[i][3-j] = state.stack[j];
       
   259         }
       
   260         for (; j < 4; j++) {
       
   261           if (brd[i][3-j] !== 0)
       
   262             updated = true;
       
   263           brd[i][3-j] = 0;
       
   264         }
       
   265       }
       
   266       return updated;
       
   267     };
       
   268 
       
   269     var boardDom = document.getElementById("board");
       
   270     var ui = {};
       
   271     ui.board = {};
       
   272     ui.board.set = function(i, j, val) {
       
   273       boardDom.querySelectorAll("tr")[i].querySelectorAll("td")[j].innerHTML = val;
       
   274     }
       
   275     ui.board.update = function(brd) {
       
   276       for (var i = 0; i < 4; i++) {
       
   277         for (var j = 0; j < 4; j++) {
       
   278           ui.board.set(i, j, (brd[i][j] >= 2) ? brd[i][j] : "");
       
   279         }
       
   280       }
       
   281     }
       
   282     ui.score = {};
       
   283     var scoreDom = document.getElementById("score");
       
   284     var maxDom = document.getElementById("max");
       
   285     ui.score.clear = function(brd) {
       
   286       scoreDom.innerHTML = '0';
       
   287       maxDom.innerHTML = '0';
       
   288     }
       
   289     ui.score.update = function(brd) {
       
   290       var score = board.score(brd);
       
   291       scoreDom.innerHTML = '' + score.score;
       
   292       maxDom.innerHTML = '' + score.max;
       
   293     }
       
   294 
       
   295     function start() {
       
   296       ui.score.clear();
       
   297       ui.message.clear();
       
   298       board.current = board.create();
       
   299       board.random(board.current);
       
   300       ui.board.update(board.current);
       
   301     }
       
   302     document.getElementById("start").addEventListener("click", start);
       
   303 
       
   304     function up() {
       
   305       var updated = board.move.up(board.current);
       
   306       if (updated) {
       
   307         board.random(board.current);
       
   308         ui.board.update(board.current);
       
   309         ui.score.update(board.current);
       
   310       }
       
   311     }
       
   312     document.getElementById("up").addEventListener("click", up);
       
   313     function down() {
       
   314       var updated = board.move.down(board.current);
       
   315       if (updated) {
       
   316         board.random(board.current);
       
   317         ui.board.update(board.current);
       
   318         ui.score.update(board.current);
       
   319       }
       
   320     }
       
   321     document.getElementById("down").addEventListener("click", down);
       
   322     function left() {
       
   323       var updated = board.move.left(board.current);
       
   324       if (updated) {
       
   325         board.random(board.current);
       
   326         ui.board.update(board.current);
       
   327         ui.score.update(board.current);
       
   328       }
       
   329     }
       
   330     document.getElementById("left").addEventListener("click", left);
       
   331     function right() {
       
   332       var updated = board.move.right(board.current);
       
   333       if (updated) {
       
   334         board.random(board.current);
       
   335         ui.board.update(board.current);
       
   336         ui.score.update(board.current);
       
   337       }
       
   338     }
       
   339     document.getElementById("right").addEventListener("click", right);
       
   340 
       
   341     document.body.addEventListener("keydown", function(event) {
       
   342       var key = event.keyCode || event.which;
       
   343       switch (key) {
       
   344           case 38: up(); break;
       
   345           case 40: down(); break;
       
   346           case 37: left(); break;
       
   347           case 39: right(); break;
       
   348       }
       
   349       return false;
       
   350     });
       
   351     
       
   352     ui.message = {};
       
   353     var messageDom = document.getElementById("message-area");
       
   354     ui.message.clear = function() {
       
   355       messageDom.innerHTML = "";
       
   356     }
       
   357     ui.message.set = function(msg) {
       
   358       messageDom.innerHTML = msg;
       
   359     }
       
   360 
       
   361     function step() {
       
   362       ui.message.clear();
       
   363       if (board.gameOver(board.current)) {
       
   364         ui.message.set("Game over!");
       
   365         return;
       
   366       }
       
   367       var tmpBrd = board.create();
       
   368       board.copy(board.current, tmpBrd);
       
   369       var fn = ai.current(tmpBrd);
       
   370       if (typeof fn === 'undefined') {
       
   371         ui.message.set("I don't know how to move!");
       
   372         return;
       
   373       }
       
   374       var updated = board.move[fn].call(null, board.current);
       
   375       if (updated) {
       
   376         board.random(board.current);
       
   377         ui.board.update(board.current);
       
   378         ui.score.update(board.current);
       
   379       } else {
       
   380         ui.message.set("Wrong move!");
       
   381       }
       
   382     }
       
   383     document.getElementById("step").addEventListener("click", step);
       
   384 
       
   385     function finish() {
       
   386       ui.message.clear();
       
   387       while (!board.gameOver(board.current)) {
       
   388         var tmpBrd = board.create();
       
   389         board.copy(board.current, tmpBrd);
       
   390         var fn = ai.current(tmpBrd);
       
   391         if (typeof fn === 'undefined') {
       
   392           ui.message.set("I don't know how to move!");
       
   393           return;
       
   394         }
       
   395         var updated = board.move[fn].call(null, board.current);
       
   396         if (updated) {
       
   397           board.random(board.current);
       
   398         } else {
       
   399           ui.board.update(board.current);
       
   400           ui.score.update(board.current);
       
   401           ui.message.set("Wrong move!");
       
   402           return;
       
   403         }
       
   404       }
       
   405       ui.board.update(board.current);
       
   406       ui.score.update(board.current);
       
   407       ui.message.set("Game over!");
       
   408     }
       
   409     document.getElementById("finish").addEventListener("click", finish);
       
   410 
       
   411     var ai = {};
       
   412 
       
   413     ai.random = function(brd) {
       
   414       var tmpBrd = board.create();
       
   415       do {
       
   416         var action = ["up", "down", "left", "right"][Math.floor(Math.random()*4)];
       
   417         board.copy(brd, tmpBrd);
       
   418       } while (!board.move[action](tmpBrd));
       
   419       return action;
       
   420     }
       
   421     document.getElementById("ai-random").addEventListener("click", function() {
       
   422       ai.current = ai.random;
       
   423     });
       
   424 
       
   425     ai.nextMaxScore = function(brd) {
       
   426       var tmpBrd = board.create();
       
   427       board.copy(brd, tmpBrd);
       
   428       var maxScore = -1;
       
   429       var action;
       
   430       if (board.move.up(tmpBrd)) {
       
   431         maxScore = board.score(tmpBrd).score;
       
   432         action = "up";
       
   433       }
       
   434       board.copy(brd, tmpBrd);
       
   435       if (board.move.left(tmpBrd)) {
       
   436         var score = board.score(tmpBrd).score;
       
   437         if (maxScore < score) {
       
   438           action = "left";
       
   439           maxScore = score;
       
   440         }
       
   441       }
       
   442       board.copy(brd, tmpBrd);
       
   443       if (board.move.down(tmpBrd)) {
       
   444         var score = board.score(tmpBrd).score;
       
   445         if (maxScore < score) {
       
   446           action = "down";
       
   447           maxScore = score;
       
   448         }
       
   449       }
       
   450       board.copy(brd, tmpBrd);
       
   451       if (board.move.right(tmpBrd)) {
       
   452         var score = board.score(tmpBrd).score;
       
   453         if (maxScore < score) {
       
   454           action = "right";
       
   455           maxScore = score;
       
   456         }
       
   457       }
       
   458       return action;
       
   459     }
       
   460     document.getElementById("ai-next-max-score").addEventListener("click", function() {
       
   461       ai.current = ai.nextMaxScore;
       
   462     });
       
   463 
       
   464 
       
   465     ai.nextMaxValue = function(brd) {
       
   466       var tmpBrd = board.create();
       
   467       board.copy(brd, tmpBrd);
       
   468       var maxMax = -1;
       
   469       var action;
       
   470       if (board.move.up(tmpBrd)) {
       
   471         maxMax = board.score(tmpBrd).max;
       
   472         action = "up";
       
   473       }
       
   474       board.copy(brd, tmpBrd);
       
   475       if (board.move.left(tmpBrd)) {
       
   476         var max = board.score(tmpBrd).max;
       
   477         if (maxMax < max) {
       
   478           action = "left";
       
   479           maxMax = max;
       
   480         }
       
   481       }
       
   482       board.copy(brd, tmpBrd);
       
   483       if (board.move.down(tmpBrd)) {
       
   484         var max = board.score(tmpBrd).max;
       
   485         if (maxMax < max) {
       
   486           action = "down";
       
   487           maxMax = max;
       
   488         }
       
   489       }
       
   490       board.copy(brd, tmpBrd);
       
   491       if (board.move.right(tmpBrd)) {
       
   492         var max = board.score(tmpBrd).max;
       
   493         if (maxMax < max) {
       
   494           action = "right";
       
   495           maxMax = max;
       
   496         }
       
   497       }
       
   498       return action;
       
   499     }
       
   500     document.getElementById("ai-next-max-value").addEventListener("click", function() {
       
   501       ai.current = ai.nextMaxValue;
       
   502     });
       
   503 
       
   504   </script>
       
   505   
       
   506 </body>
       
   507 </html>