The 2048 bitcoin game script


SUBMITTED BY: AshuGopal

DATE: Sept. 3, 2017, 7:26 a.m.

UPDATED: Sept. 3, 2017, 7:29 a.m.

FORMAT: Text only

SIZE: 12.8 kB

HITS: 840

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

comments powered by Disqus