giovedì 9 giugno 2016

2048:code

  1. function GameManager(size, InputManager, Actuator, StorageManager) {
  2.   this.size           = size; // Size of the grid
  3.   this.inputManager   = new InputManager;
  4.   this.storageManager = new StorageManager;
  5.   this.actuator       = new Actuator;
  6.  
  7.   this.startTiles     = 2;
  8.  
  9.   this.inputManager.on("move", this.move.bind(this));
  10.   this.inputManager.on("restart", this.restart.bind(this));
  11.   this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));
  12.  
  13.   this.setup();
  14. }
  15.  
  16. // Restart the game
  17. GameManager.prototype.restart = function () {
  18.   this.storageManager.clearGameState();
  19.   this.actuator.continue(); // Clear the game won/lost message
  20.   this.setup();
  21. };
  22.  
  23. // Keep playing after winning (allows going over 2048)
  24. GameManager.prototype.keepPlaying = function () {
  25.   this.keepPlaying = true;
  26.   this.actuator.continue(); // Clear the game won/lost message
  27. };
  28.  
  29. // Return true if the game is lost, or has won and the user hasn't kept playing
  30. GameManager.prototype.isGameTerminated = function () {
  31.   if (this.over || (this.won && !this.keepPlaying)) {
  32.     return true;
  33.   } else {
  34.     return false;
  35.   }
  36. };
  37.  
  38. // Set up the game
  39. GameManager.prototype.setup = function () {
  40.   var previousState = this.storageManager.getGameState();
  41.  
  42.   // Reload the game from a previous game if present
  43.   if (previousState) {
  44.     this.grid        = new Grid(previousState.grid.size,
  45.                                 previousState.grid.cells); // Reload grid
  46.     this.score       = previousState.score;
  47.     this.over        = previousState.over;
  48.     this.won         = previousState.won;
  49.     this.keepPlaying = previousState.keepPlaying;
  50.   } else {
  51.     this.grid        = new Grid(this.size);
  52.     this.score       = 0;
  53.     this.over        = false;
  54.     this.won         = false;
  55.     this.keepPlaying = false;
  56.  
  57.     // Add the initial tiles
  58.     this.addStartTiles();
  59.   }
  60.  
  61.   // Update the actuator
  62.   this.actuate();
  63. };
  64.  
  65. // Set up the initial tiles to start the game with
  66. GameManager.prototype.addStartTiles = function () {
  67.   for (var i = 0; i < this.startTiles; i++) {
  68.     this.addRandomTile();
  69.   }
  70. };
  71.  
  72. // Adds a tile in a random position
  73. GameManager.prototype.addRandomTile = function () {
  74.   if (this.grid.cellsAvailable()) {
  75.     var value = Math.random() < 0.9 ? 2 : 4;
  76.     var tile = new Tile(this.grid.randomAvailableCell(), value);
  77.  
  78.     this.grid.insertTile(tile);
  79.   }
  80. };
  81.  
  82. // Sends the updated grid to the actuator
  83. GameManager.prototype.actuate = function () {
  84.   if (this.storageManager.getBestScore() < this.score) {
  85.     this.storageManager.setBestScore(this.score);
  86.   }
  87.  
  88.   // Clear the state when the game is over (game over only, not win)
  89.   if (this.over) {
  90.     this.storageManager.clearGameState();
  91.   } else {
  92.     this.storageManager.setGameState(this.serialize());
  93.   }
  94.  
  95.   this.actuator.actuate(this.grid, {
  96.     score:      this.score,
  97.     over:       this.over,
  98.     won:        this.won,
  99.     bestScore:  this.storageManager.getBestScore(),
  100.     terminated: this.isGameTerminated()
  101.   });
  102.  
  103. };
  104.  
  105. // Represent the current game as an object
  106. GameManager.prototype.serialize = function () {
  107.   return {
  108.     grid:        this.grid.serialize(),
  109.     score:       this.score,
  110.     over:        this.over,
  111.     won:         this.won,
  112.     keepPlaying: this.keepPlaying
  113.   };
  114. };
  115.  
  116. // Save all tile positions and remove merger info
  117. GameManager.prototype.prepareTiles = function () {
  118.   this.grid.eachCell(function (x, y, tile) {
  119.     if (tile) {
  120.       tile.mergedFrom = null;
  121.       tile.savePosition();
  122.     }
  123.   });
  124. };
  125.  
  126. // Move a tile and its representation
  127. GameManager.prototype.moveTile = function (tile, cell) {
  128.   this.grid.cells[tile.x][tile.y] = null;
  129.   this.grid.cells[cell.x][cell.y] = tile;
  130.   tile.updatePosition(cell);
  131. };
  132.  
  133. // Move tiles on the grid in the specified direction
  134. GameManager.prototype.move = function (direction) {
  135.   // 0: up, 1: right, 2: down, 3: left
  136.   var self = this;
  137.  
  138.   if (this.isGameTerminated()) return; // Don't do anything if the game's over
  139.  
  140.   var cell, tile;
  141.  
  142.   var vector     = this.getVector(direction);
  143.   var traversals = this.buildTraversals(vector);
  144.   var moved      = false;
  145.  
  146.   // Save the current tile positions and remove merger information
  147.   this.prepareTiles();
  148.  
  149.   // Traverse the grid in the right direction and move tiles
  150.   traversals.x.forEach(function (x) {
  151.     traversals.y.forEach(function (y) {
  152.       cell = { x: x, y: y };
  153.       tile = self.grid.cellContent(cell);
  154.  
  155.       if (tile) {
  156.         var positions = self.findFarthestPosition(cell, vector);
  157.         var next      = self.grid.cellContent(positions.next);
  158.  
  159.         // Only one merger per row traversal?
  160.         if (next && next.value === tile.value && !next.mergedFrom) {
  161.           var merged = new Tile(positions.next, tile.value * 2);
  162.           merged.mergedFrom = [tile, next];
  163.  
  164.           self.grid.insertTile(merged);
  165.           self.grid.removeTile(tile);
  166.  
  167.           // Converge the two tiles' positions
  168.           tile.updatePosition(positions.next);
  169.  
  170.           // Update the score
  171.           self.score += merged.value;
  172.  
  173.           // The mighty 2048 tile
  174.           if (merged.value === 2048) self.won = true;
  175.         } else {
  176.           self.moveTile(tile, positions.farthest);
  177.         }
  178.  
  179.         if (!self.positionsEqual(cell, tile)) {
  180.           moved = true; // The tile moved from its original cell!
  181.         }
  182.       }
  183.     });
  184.   });
  185.  
  186.   if (moved) {
  187.     this.addRandomTile();
  188.  
  189.     if (!this.movesAvailable()) {
  190.       this.over = true; // Game over!
  191.     }
  192.  
  193.     this.actuate();
  194.   }
  195. /******************************************/  
  196. /******************************************/
  197. /*    THE AI CODE STARTS FROM HERE        */
  198. /******************************************/
  199. /******************************************/
  200.   /*  0:Up
  201.       1:Right
  202.       2:Down  
  203.       3:Left
  204.   */
  205.  
  206. this.isValid = function(x,y){
  207.   if(< 0 || x >3 || y <0 || y > 3)
  208.     return false;
  209.   return true;
  210. }  
  211. this.moveCells = function(matrix, move){
  212.   var dx = [-1,0,1,0];
  213.   var dy = [0,1,0,-1];
  214.   var nx,ny;
  215.   for(var k = 0;k<3;k++){
  216.     for(var i = 0;i<4;i++){
  217.       for(var j = 0; j<4; j++){
  218.        nx = i+dx[move];
  219.        ny = j+dy[move];
  220.        if(self.isValid(nx,ny)){
  221.           if(matrix[nx][ny] == 0){
  222.            matrix[nx][ny] = matrix[i][j];
  223.            matrix[i][j] = 0;
  224.          }
  225.         }
  226.       }
  227.     }
  228.   }
  229.   for(var i = 0;i<4;i++){
  230.       for(var j = 0; j<4; j++){
  231.         nx = i + dx[move];
  232.         ny = j + dy[move];
  233.         if(self.isValid(nx,ny)){
  234.           if(matrix[i][j] == matrix[nx][ny]){
  235.             matrix[nx][ny] *= -2;
  236.             matrix[i][j] = 0;
  237.           }
  238.         }
  239.       }
  240.     }
  241.     for(var k = 0;k<3;k++){
  242.     for(var i = 0;i<4;i++){
  243.       for(var j = 0; j<4; j++){
  244.         if(matrix[i][j] <0)
  245.           matrix[i][j] *= -1;
  246.         nx = i+dx[move];
  247.         ny = j+dy[move];
  248.         if(self.isValid(nx,ny)){
  249.           if(matrix[nx][ny] == 0){
  250.            matrix[nx][ny] = matrix[i][j];
  251.            matrix[i][j] = 0;
  252.           }
  253.         }
  254.       }
  255.     }
  256.   }
  257.   return matrix;
  258. }
  259.  
  260. this.evaluateMatrix = function(matrix){
  261.   /* Count Number of Free Spaces */
  262.   var cc = 0;
  263.   for(var i = 0;i<4;i++)
  264.     for(var j = 0;j<4;j++){
  265.       if(matrix[i][j] == 0)
  266.         cc += 100;
  267.       else
  268.         cc += matrix[i][j]*matrix[i][j];
  269.     }
  270.  
  271.   return cc;
  272. }
  273.  
  274. this.printMatrix = function(matrix){
  275.   for(var i = 0;i<4;i++){
  276.     var str = ""
  277.     for(var j = 0;j<4;j++)
  278.       str += matrix[i][j] + " ";
  279.     console.log(str)
  280.   }
  281.   console.log("******************************");
  282. }
  283.  
  284. this.findFreeCell = function(matrix){
  285.   var i,j,k=0;
  286.   do{
  287.     i =  (Math.floor(Math.random()*100))%4;
  288.     j =  (Math.floor(Math.random()*100))%4;
  289.     k++;
  290.   }while(matrix[i][j] != 0 && k != 500);
  291.   if(matrix[i][j] != 0)
  292.     for(= 0;i<4;i++)
  293.       for(= 0;j<4;j++)
  294.         if(matrix[i][j] == 0)
  295.           return ({x:i, y:j});
  296.  
  297.   return ({x:i, y:j});
  298. }
  299.  
  300. this.isEqualMatrix = function(m1,m2){
  301.   for(var i = 0;i<4;i++)
  302.     for(var j = 0;j<4;j++)
  303.       if(m1[i][j] != m2[i][j])
  304.         return false;
  305.   return true;
  306. }
  307.  
  308. this.minMax = function(matrix, move, depth){
  309.   if(depth == 6)
  310.     return 0;
  311.   var rmatrix = self.moveCells(self.createCopy(matrix),move);
  312.   var areSame = self.isEqualMatrix(rmatrix, matrix);
  313.   var score = self.evaluateMatrix(rmatrix);
  314.  
  315.   if(areSame == true)
  316.     return score-1;
  317.   var maxVal=-1000,val,ret;
  318.   var freeCell = self.findFreeCell(rmatrix);
  319.   if(freeCell.x == 4 || freeCell.y == 4)
  320.     console.log("YES VALUE IS 4 || " + freeCell.x + " | " + freeCell.y);
  321.   rmatrix[freeCell.x][freeCell.y] = 2;
  322.   for(var x = 0;x<4;x++)
  323.   {
  324.     val = this.minMax(self.createCopy(rmatrix), x, depth+1);
  325.     if(val > maxVal)
  326.       maxVal  = val;
  327.   }
  328.   return (score+maxVal);
  329. }
  330.  
  331.   this.getMove = function(matrix){
  332.  
  333.     var maxVal = 0,val,ret;
  334.     for(var x = 0; x < 4;x++){
  335.       val = this.minMax(self.createCopy(matrix),x,0);
  336.       // console.log("Score for "+ x + ":" + val )
  337.       if(val > maxVal){
  338.         maxVal = val;
  339.         ret = x;
  340.       }
  341.     }
  342.     return ret;
  343.   }
  344.  
  345.  
  346.   this.getMatrix = function(){
  347.     var matrix = [];
  348.     for (var i = 0 ; i <4 ; i++) {
  349.       var row = [];
  350.       for (var j = 0; j < 4; j++) {
  351.         tile = self.grid.cellContent({x:j, y:i});
  352.         if(tile == null)
  353.           row.push(0);
  354.         else
  355.           row.push(tile["value"]);
  356.       };
  357.       matrix.push(row);
  358.     };
  359.     return matrix;
  360.   }
  361.  
  362.   this.createCopy = function(matrix){
  363.     var ret =[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];
  364.     for(var i = 0; i < 4;i++)
  365.       for(var j = 0; j < 4; j++)
  366.         ret[i][j] = matrix[i][j].valueOf();
  367.     return ret;
  368.   }
  369.  
  370.  
  371.  
  372.  
  373. setTimeout(function() {
  374.   matrix = self.getMatrix();
  375.   var myMove = self.getMove(self.createCopy(matrix));
  376.   var rmat = self.moveCells(self.createCopy(matrix), myMove);
  377.   console.log(myMove);
  378.   if( self.isEqualMatrix(rmat,matrix))
  379.     myMove = (Math.floor(Math.random()*100))%4;
  380.   self.move(myMove);
  381.   }, 100);
  382.  
  383. /******************************************/  
  384. /******************************************/
  385. /*        THE AI CODE ENDS  HERE          */
  386. /******************************************/
  387. /******************************************/
  388.  
  389. };
  390.  
  391. // Get the vector representing the chosen direction
  392. GameManager.prototype.getVector = function (direction) {
  393.   // Vectors representing tile movement
  394.   var map = {
  395.     0: { x: 0,  y: -1 }, // Up
  396.     1: { x: 1,  y: 0 },  // Right
  397.     2: { x: 0,  y: 1 },  // Down
  398.     3: { x: -1, y: 0 }   // Left
  399.   };
  400.  
  401.   return map[direction];
  402. };
  403.  
  404. // Build a list of positions to traverse in the right order
  405. GameManager.prototype.buildTraversals = function (vector) {
  406.   var traversals = { x: [], y: [] };
  407.  
  408.   for (var pos = 0; pos < this.size; pos++) {
  409.     traversals.x.push(pos);
  410.     traversals.y.push(pos);
  411.   }
  412.  
  413.   // Always traverse from the farthest cell in the chosen direction
  414.   if (vector.x === 1) traversals.x = traversals.x.reverse();
  415.   if (vector.y === 1) traversals.y = traversals.y.reverse();
  416.  
  417.   return traversals;
  418. };
  419.  
  420. GameManager.prototype.findFarthestPosition = function (cell, vector) {
  421.   var previous;
  422.  
  423.   // Progress towards the vector direction until an obstacle is found
  424.   do {
  425.     previous = cell;
  426.     cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
  427.   } while (this.grid.withinBounds(cell) &&
  428.            this.grid.cellAvailable(cell));
  429.  
  430.   return {
  431.     farthest: previous,
  432.     next: cell // Used to check if a merge is required
  433.   };
  434. };
  435.  
  436. GameManager.prototype.movesAvailable = function () {
  437.   return this.grid.cellsAvailable() || this.tileMatchesAvailable();
  438. };
  439.  
  440. // Check for available matches between tiles (more expensive check)
  441. GameManager.prototype.tileMatchesAvailable = function () {
  442.   var self = this;
  443.  
  444.   var tile;
  445.  
  446.   for (var x = 0; x < this.size; x++) {
  447.     for (var y = 0; y < this.size; y++) {
  448.       tile = this.grid.cellContent({ x: x, y: y });
  449.  
  450.       if (tile) {
  451.         for (var direction = 0; direction < 4; direction++) {
  452.           var vector = self.getVector(direction);
  453.           var cell   = { x: x + vector.x, y: y + vector.y };
  454.  
  455.           var other  = self.grid.cellContent(cell);
  456.  
  457.           if (other && other.value === tile.value) {
  458.             return true; // These two tiles can be merged
  459.           }
  460.         }
  461.       }
  462.     }
  463.   }
  464.  
  465.   return false;
  466. };
  467.  
  468. GameManager.prototype.positionsEqual = function (first, second) {
  469.   return first.x === second.x && first.y === second.y;
  470. };