Ver código fonte

2048小游戏和趣味英语测试游戏提交

Pan 6 anos atrás
pai
commit
4a65ecb902
100 arquivos alterados com 4006 adições e 0 exclusões
  1. BIN
      game/2048/favicon.ico
  2. BIN
      game/2048/icon.png
  3. 195 0
      game/2048/index.html
  4. 28 0
      game/2048/js/animframe_polyfill.js
  5. 4 0
      game/2048/js/application.js
  6. 9 0
      game/2048/js/bind_polyfill.js
  7. 71 0
      game/2048/js/classlist_polyfill.js
  8. 308 0
      game/2048/js/game_manager.js
  9. 117 0
      game/2048/js/grid.js
  10. 139 0
      game/2048/js/html_actuator.js
  11. 151 0
      game/2048/js/keyboard_input_manager.js
  12. 63 0
      game/2048/js/local_storage_manager.js
  13. 27 0
      game/2048/js/tile.js
  14. BIN
      game/2048/meta/apple-touch-icon.png
  15. BIN
      game/2048/meta/apple-touch-startup-image-640x1096.png
  16. BIN
      game/2048/meta/apple-touch-startup-image-640x920.png
  17. 40 0
      game/2048/style/base.css
  18. BIN
      game/2048/style/fonts/ClearSans-Bold-webfont.eot
  19. 640 0
      game/2048/style/fonts/ClearSans-Bold-webfont.svg
  20. BIN
      game/2048/style/fonts/ClearSans-Bold-webfont.woff
  21. BIN
      game/2048/style/fonts/ClearSans-Light-webfont.eot
  22. 670 0
      game/2048/style/fonts/ClearSans-Light-webfont.svg
  23. BIN
      game/2048/style/fonts/ClearSans-Light-webfont.woff
  24. BIN
      game/2048/style/fonts/ClearSans-Regular-webfont.eot
  25. 669 0
      game/2048/style/fonts/ClearSans-Regular-webfont.svg
  26. BIN
      game/2048/style/fonts/ClearSans-Regular-webfont.woff
  27. 30 0
      game/2048/style/fonts/clear-sans.css
  28. 81 0
      game/2048/style/helpers.scss
  29. 764 0
      game/2048/style/main.css
  30. BIN
      game/EnglishTest/audio/2.mp3
  31. BIN
      game/EnglishTest/audio/bgm.mp3
  32. BIN
      game/EnglishTest/icon.png
  33. BIN
      game/EnglishTest/image/beeper.png
  34. BIN
      game/EnglishTest/image/beeper1.png
  35. BIN
      game/EnglishTest/image/bg.jpg
  36. BIN
      game/EnglishTest/image/bgq.jpg
  37. BIN
      game/EnglishTest/image/book.png
  38. BIN
      game/EnglishTest/image/fp_1a.png
  39. BIN
      game/EnglishTest/image/fp_1b.png
  40. BIN
      game/EnglishTest/image/fp_2a.png
  41. BIN
      game/EnglishTest/image/fp_2b.png
  42. BIN
      game/EnglishTest/image/fp_3a.png
  43. BIN
      game/EnglishTest/image/fp_3b.png
  44. BIN
      game/EnglishTest/image/fp_3c.png
  45. BIN
      game/EnglishTest/image/fp_3d.png
  46. BIN
      game/EnglishTest/image/fp_4.png
  47. BIN
      game/EnglishTest/image/fp_5.png
  48. BIN
      game/EnglishTest/image/fp_6a.png
  49. BIN
      game/EnglishTest/image/fp_6b.png
  50. BIN
      game/EnglishTest/image/fp_6c.png
  51. BIN
      game/EnglishTest/image/fp_7.png
  52. BIN
      game/EnglishTest/image/fp_8.png
  53. BIN
      game/EnglishTest/image/glass.png
  54. BIN
      game/EnglishTest/image/load_1.png
  55. BIN
      game/EnglishTest/image/load_2.png
  56. BIN
      game/EnglishTest/image/load_3.png
  57. BIN
      game/EnglishTest/image/load_4.png
  58. BIN
      game/EnglishTest/image/load_5.png
  59. BIN
      game/EnglishTest/image/logo.png
  60. BIN
      game/EnglishTest/image/lpage1.png
  61. BIN
      game/EnglishTest/image/lpage2_1.png
  62. BIN
      game/EnglishTest/image/lpage2_10.png
  63. BIN
      game/EnglishTest/image/lpage2_2.png
  64. BIN
      game/EnglishTest/image/lpage2_3.png
  65. BIN
      game/EnglishTest/image/lpage2_4.png
  66. BIN
      game/EnglishTest/image/lpage2_5.png
  67. BIN
      game/EnglishTest/image/lpage2_6.png
  68. BIN
      game/EnglishTest/image/lpage2_7.png
  69. BIN
      game/EnglishTest/image/lpage2_8.png
  70. BIN
      game/EnglishTest/image/lpage2_9.png
  71. BIN
      game/EnglishTest/image/lpage3_1.png
  72. BIN
      game/EnglishTest/image/lpage3_2.png
  73. BIN
      game/EnglishTest/image/lpage3_3.png
  74. BIN
      game/EnglishTest/image/lpage3_4.png
  75. BIN
      game/EnglishTest/image/lpage3_5.png
  76. BIN
      game/EnglishTest/image/lpage4_1.png
  77. BIN
      game/EnglishTest/image/lpage4_2.png
  78. BIN
      game/EnglishTest/image/lpage4_3.png
  79. BIN
      game/EnglishTest/image/lpage4_4.png
  80. BIN
      game/EnglishTest/image/lpage4_5.png
  81. BIN
      game/EnglishTest/image/lpage5.png
  82. BIN
      game/EnglishTest/image/lpage6.png
  83. BIN
      game/EnglishTest/image/lpage7.png
  84. BIN
      game/EnglishTest/image/off.png
  85. BIN
      game/EnglishTest/image/on.png
  86. BIN
      game/EnglishTest/image/p10_1n.png
  87. BIN
      game/EnglishTest/image/p10_1r.png
  88. BIN
      game/EnglishTest/image/p10_1w.png
  89. BIN
      game/EnglishTest/image/p10_aa.png
  90. BIN
      game/EnglishTest/image/p10_ab.png
  91. BIN
      game/EnglishTest/image/p10_ac.png
  92. BIN
      game/EnglishTest/image/p10_ad.png
  93. BIN
      game/EnglishTest/image/p10_t.png
  94. BIN
      game/EnglishTest/image/p1_1n.png
  95. BIN
      game/EnglishTest/image/p1_1r.png
  96. BIN
      game/EnglishTest/image/p1_1w.png
  97. BIN
      game/EnglishTest/image/p1_2.png
  98. BIN
      game/EnglishTest/image/p1_aa.png
  99. BIN
      game/EnglishTest/image/p1_ab.png
  100. 0 0
      game/EnglishTest/image/p1_ac.png

BIN
game/2048/favicon.ico


BIN
game/2048/icon.png


+ 195 - 0
game/2048/index.html

@@ -0,0 +1,195 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<meta name="page-view-size" content="1280*720">
+		<meta name="format-detection" content="telephone=no">
+
+		<title>2048</title>
+		<link rel="stylesheet" type="text/css" href="style/base.css" />
+		<link href="style/main.css" rel="stylesheet" type="text/css">
+		<link rel="shortcut icon" href="favicon.ico">
+	</head>
+
+
+
+
+	<!-- <script>
+		lastScrollY = 0;
+
+		function heartBeat() {
+			var diffY;
+			if (document.documentElement && document.documentElement.scrollTop)
+				diffY = document.documentElement.scrollTop;
+			else if (document.body)
+				diffY = document.body.scrollTop
+			else { /*Netscape stuff*/ }
+			percent = .1 * (diffY - lastScrollY);
+			if (percent > 0) percent = Math.ceil(percent);
+			else percent = Math.floor(percent);
+			document.getElementById("full").style.top = parseInt(document.getElementById("full").style.top) + percent + "px";
+			lastScrollY = lastScrollY + percent;
+		}
+	</script> -->
+	<body>
+		<div class="container">
+			<div class="heading" style="display: inline-block;left: 50px;top: 50px;position: absolute;">
+				<h1 class="title">2048</h1>
+				<div class="scores-container">
+					<div class="best-container">0</div>
+					<div class="score-container">0</div>
+				</div>
+			</div>
+
+			<div class="above-game" style="display: inline-block;">
+				<a class="restart-button">点我开始</a>
+			</div>
+			<div class="game-container" style="display: inline-block;">
+				<div class="game-message">
+					<p></p>
+					<div class="lower">
+						<a class="keep-playing-button">Keep going</a>
+						<a class="retry-button">再玩一次</a>
+					</div>
+				</div>
+
+				<div class="grid-container">
+					<div class="grid-row">
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+					</div>
+					<div class="grid-row">
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+					</div>
+					<div class="grid-row">
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+					</div>
+					<div class="grid-row">
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+						<div class="grid-cell"></div>
+					</div>
+				</div>
+
+				<div class="tile-container">
+
+				</div>
+			</div>
+
+		</div>
+		<script>
+			// 页面适配
+			var deviceWidth = window.innerWidth; //其它获取页面宽度代码,在某些盒子存在为0情况,所以选用此获取值方法。
+			var deviceScale = deviceWidth / 1280;
+			if (deviceWidth > 1280) {
+				document.write('<meta name="viewport" content="width=1280,initial-scale=' + deviceScale + ', minimum-scale = ' +
+					deviceScale + ', maximum-scale = ' + deviceScale + ', target-densitydpi=device-dpi">');
+			} else {
+				document.write('<meta name="viewport" content="width=1280, user-scalable=no">');
+			}
+			
+			
+			document.onkeydown = function(event) {
+				var e = event || window.event || arguments.callee.caller.arguments[0];
+				if (e && e.keyCode == 27) { // 按 Esc
+			
+					//要做的事情
+				}
+			
+				if (e && e.keyCode == 13) { // enter 键
+					//要做的事情
+				}
+			};
+			
+
+// 			var my_list = {
+// 				2: '九头虫',
+// 				4: '白龙马',
+// 				8: '沙僧',
+// 				16: '猪八戒',
+// 				32: '孙悟空',
+// 				64: '唐僧',
+// 				128: '观音',
+// 				256: '弥勒佛',
+// 				512: '上老君',
+// 				1024: '如来佛',
+// 				2048: '神笔记',
+// 				4096: 'http://iduanzi.qiniudn.com/tuoguang/4096.jpg',
+// 				8192: 'http://iduanzi.qiniudn.com/tuoguang/8192.jpg',
+// 				16384: 'http://iduanzi.qiniudn.com/tuoguang/16384.jpg',
+// 				32768: '32768',
+// 				65536: '65536',
+// 				131072: '131072',
+// 				262144: '262144',
+// 				524288: '524288',
+// 				1048576: '1048576',
+// 				2097152: '2097152',
+// 				4194304: '4194304',
+// 				8388608: '8388608',
+// 				16777216: '16777216',
+// 				33554432: '33554432',
+// 				67108864: '67108864',
+// 				134217728: '134217728',
+// 				268435456: '268435456',
+// 				536870912: '536870912',
+// 				1073741824: '1073741824',
+// 				2147483648: '2147483648'
+// 			}
+// 			var my_mark = {
+// 				2: '快来变身吧!',
+// 				4: '小白马就挂了!',
+// 				8: '沙和尚,是你吗',
+// 				16: '对小猪就是你!',
+// 				32: '悟空,等等我!',
+// 				64: '打雷啦,收衣服啦',
+// 				128: '普渡众生!',
+// 				256: '阿弥托福!',
+// 				512: '你这泼猴,不像话!',
+// 				1024: '五指山压死你',
+// 				2048: '恭喜!终于见到本尊了! ',
+// 				4096: '强者!太伟大了!',
+// 				8192: '你有这么厉害呀!',
+// 				16384: '你已跳出三界之外了!',
+// 				32768: 'Game Over, HiScore: 32768',
+// 				65536: 'Game Over, HiScore: 65536',
+// 				131072: 'Game Over, HiScore: 131072',
+// 				262144: 'Game Over, HiScore: 262144',
+// 				524288: 'Game Over, HiScore: 524288',
+// 				1048576: 'Game Over, HiScore: 1048576',
+// 				2097152: 'Game Over, HiScore: 2097152',
+// 				4194304: 'Game Over, HiScore: 4194304',
+// 				8388608: 'Game Over, HiScore: 8388608',
+// 				16777216: 'Game Over, HiScore: 16777216',
+// 				33554432: 'Game Over, HiScore: 33554432',
+// 				67108864: 'Game Over, HiScore: 67108864',
+// 				134217728: 'Game Over, HiScore: 134217728',
+// 				268435456: 'Game Over, HiScore: 268435456',
+// 				536870912: 'Game Over, HiScore: 536870912',
+// 				1073741824: 'Game Over, HiScore: 1073741824',
+// 				2147483648: 'Game Over, HiScore: 2147483648'
+// 			}
+			// var my_goal = 1024;
+		</script>
+		<script src="js/bind_polyfill.js"></script>
+		<script src="js/classlist_polyfill.js"></script>
+		<script src="js/animframe_polyfill.js"></script>
+		<script src="js/keyboard_input_manager.js"></script>
+		<script src="js/html_actuator.js"></script>
+		<script src="js/grid.js"></script>
+		<script src="js/tile.js"></script>
+		<script src="js/local_storage_manager.js"></script>
+		<script src="js/game_manager.js"></script>
+		<script src="js/application.js"></script>
+
+
+	</body>
+</html>

+ 28 - 0
game/2048/js/animframe_polyfill.js

@@ -0,0 +1,28 @@
+(function () {
+  var lastTime = 0;
+  var vendors = ['webkit', 'moz'];
+  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+    window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
+    window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||
+      window[vendors[x] + 'CancelRequestAnimationFrame'];
+  }
+
+  if (!window.requestAnimationFrame) {
+    window.requestAnimationFrame = function (callback) {
+      var currTime = new Date().getTime();
+      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+      var id = window.setTimeout(function () {
+        callback(currTime + timeToCall);
+      },
+      timeToCall);
+      lastTime = currTime + timeToCall;
+      return id;
+    };
+  }
+
+  if (!window.cancelAnimationFrame) {
+    window.cancelAnimationFrame = function (id) {
+      clearTimeout(id);
+    };
+  }
+}());

+ 4 - 0
game/2048/js/application.js

@@ -0,0 +1,4 @@
+// Wait till the browser is ready to render the game (avoids glitches)
+window.requestAnimationFrame(function () {
+  new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
+});

+ 9 - 0
game/2048/js/bind_polyfill.js

@@ -0,0 +1,9 @@
+Function.prototype.bind = Function.prototype.bind || function (target) {
+  var self = this;
+  return function (args) {
+    if (!(args instanceof Array)) {
+      args = [args];
+    }
+    self.apply(target, args);
+  };
+};

+ 71 - 0
game/2048/js/classlist_polyfill.js

@@ -0,0 +1,71 @@
+(function () {
+  if (typeof window.Element === "undefined" ||
+      "classList" in document.documentElement) {
+    return;
+  }
+
+  var prototype = Array.prototype,
+      push = prototype.push,
+      splice = prototype.splice,
+      join = prototype.join;
+
+  function DOMTokenList(el) {
+    this.el = el;
+    // The className needs to be trimmed and split on whitespace
+    // to retrieve a list of classes.
+    var classes = el.className.replace(/^\s+|\s+$/g, '').split(/\s+/);
+    for (var i = 0; i < classes.length; i++) {
+      push.call(this, classes[i]);
+    }
+  }
+
+  DOMTokenList.prototype = {
+    add: function (token) {
+      if (this.contains(token)) return;
+      push.call(this, token);
+      this.el.className = this.toString();
+    },
+    contains: function (token) {
+      return this.el.className.indexOf(token) != -1;
+    },
+    item: function (index) {
+      return this[index] || null;
+    },
+    remove: function (token) {
+      if (!this.contains(token)) return;
+      for (var i = 0; i < this.length; i++) {
+        if (this[i] == token) break;
+      }
+      splice.call(this, i, 1);
+      this.el.className = this.toString();
+    },
+    toString: function () {
+      return join.call(this, ' ');
+    },
+    toggle: function (token) {
+      if (!this.contains(token)) {
+        this.add(token);
+      } else {
+        this.remove(token);
+      }
+
+      return this.contains(token);
+    }
+  };
+
+  window.DOMTokenList = DOMTokenList;
+
+  function defineElementGetter(obj, prop, getter) {
+    if (Object.defineProperty) {
+      Object.defineProperty(obj, prop, {
+        get: getter
+      });
+    } else {
+      obj.__defineGetter__(prop, getter);
+    }
+  }
+
+  defineElementGetter(HTMLElement.prototype, 'classList', function () {
+    return new DOMTokenList(this);
+  });
+})();

+ 308 - 0
game/2048/js/game_manager.js

@@ -0,0 +1,308 @@
+function GameManager(size, InputManager, Actuator, StorageManager) {
+  this.size           = size; // Size of the grid
+  this.inputManager   = new InputManager;
+  this.storageManager = new StorageManager;
+  this.actuator       = new Actuator;
+
+  this.startTiles     = 2;
+
+  this.inputManager.on("move", this.move.bind(this));
+  this.inputManager.on("restart", this.restart.bind(this));
+  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));
+
+  this.setup();
+}
+
+
+// Restart the game重新开始游戏
+GameManager.prototype.restart = function () {
+  this.storageManager.clearGameState();
+  this.actuator.continueGame(); // Clear the game won/lost message
+  this.setup();
+};
+
+// document.onkeydown = function(event) {
+// 	var e = event || window.event || arguments.callee.caller.arguments[0];
+// 	if (e && e.keyCode == 27) { // 按 Esc
+// 
+// 		//要做的事情
+// 	}
+// 
+// 	if (e && e.keyCode == 13) { // enter 键
+// 		//要做的事情
+// 	}
+// };
+
+// Keep playing after winning (allows going over 2048)
+// 获胜后继续
+GameManager.prototype.keepPlaying = function () {
+  this.keepPlaying = true;
+  this.actuator.continueGame(); // Clear the game won/lost message清除游戏中获胜/丢失的消息
+};
+
+// Return true if the game is lost, or has won and the user hasn't kept playing
+// 如果游戏输了,或者已经赢了,并且用户没有继续玩下去,请返回true
+GameManager.prototype.isGameTerminated = function () {
+  return this.over || (this.won && !this.keepPlaying);
+};
+
+// Set up the game
+// 设置游戏
+GameManager.prototype.setup = function () {
+  var previousState = this.storageManager.getGameState();
+
+  // Reload the game from a previous game if present
+	// 如果存在,则从以前的游戏中重新加载游戏。
+  if (previousState) {
+    this.grid        = new Grid(previousState.grid.size,
+                                previousState.grid.cells); // Reload grid
+		// 再装填栅格
+    this.score       = previousState.score;
+    this.over        = previousState.over;
+    this.won         = previousState.won;
+    this.keepPlaying = previousState.keepPlaying;
+  } else {
+    this.grid        = new Grid(this.size);
+    this.score       = 0;
+    this.over        = false;
+    this.won         = false;
+    this.keepPlaying = false;
+
+    // Add the initial tiles
+		// 添加初始化
+    this.addStartTiles();
+  }
+
+  // Update the actuator
+	// 更新执行器
+  this.actuate();
+};
+
+// Set up the initial tiles to start the game with
+// 设置参数以开始游戏
+GameManager.prototype.addStartTiles = function () {
+  for (var i = 0; i < this.startTiles; i++) {
+    this.addRandomTile();
+  }
+};
+
+// Adds a tile in a random position
+// 将目标添加到任意位置
+GameManager.prototype.addRandomTile = function () {
+  if (this.grid.cellsAvailable()) {
+    var value = Math.random() < 0.9 ? 2 : 4;
+    var tile = new Tile(this.grid.randomAvailableCell(), value);
+
+    this.grid.insertTile(tile);
+  }
+};
+
+// Sends the updated grid to the actuator
+// 将更新的网格发送给执行器。
+GameManager.prototype.actuate = function () {
+  if (this.storageManager.getBestScore() < this.score) {
+    this.storageManager.setBestScore(this.score);
+  }
+
+  // Clear the state when the game is over (game over only, not win)
+	// 游戏结束后清除状态
+  if (this.over) {
+    this.storageManager.clearGameState();
+  } else {
+    this.storageManager.setGameState(this.serialize());
+  }
+
+  this.actuator.actuate(this.grid, {
+    score:      this.score,
+    over:       this.over,
+    won:        this.won,
+    bestScore:  this.storageManager.getBestScore(),
+    terminated: this.isGameTerminated()
+  });
+
+};
+
+// Represent the current game as an object
+// 将当前游戏表示为对象
+GameManager.prototype.serialize = function () {
+  return {
+    grid:        this.grid.serialize(),
+    score:       this.score,
+    over:        this.over,
+    won:         this.won,
+    keepPlaying: this.keepPlaying
+  };
+};
+
+// Save all tile positions and remove merger info
+// 保存所有tile位置并删除合并信息
+GameManager.prototype.prepareTiles = function () {
+  this.grid.eachCell(function (x, y, tile) {
+    if (tile) {
+      tile.mergedFrom = null;
+      tile.savePosition();
+    }
+  });
+};
+
+// Move a tile and its representation
+// 移动tile及其表示
+GameManager.prototype.moveTile = function (tile, cell) {
+  this.grid.cells[tile.x][tile.y] = null;
+  this.grid.cells[cell.x][cell.y] = tile;
+  tile.updatePosition(cell);
+};
+
+// Move tiles on the grid in the specified direction
+// 按指定的方向移动网格上的元素
+GameManager.prototype.move = function (direction) {
+  // 0: up, 1: right, 2: down, 3: left
+  var self = this;
+
+  if (this.isGameTerminated()) return; // Don't do anything if the game's over
+	// 如果游戏结束了,什么都不要做
+
+  var cell, tile;
+
+  var vector     = this.getVector(direction);
+  var traversals = this.buildTraversals(vector);
+  var moved      = false;
+
+  // Save the current tile positions and remove merger information  保存当前的元素位置并删除合并信息。
+  this.prepareTiles();
+
+  // Traverse the grid in the right direction and move tiles
+	// 沿着正确的方向遍历网格并移动元素
+  traversals.x.forEach(function (x) {
+    traversals.y.forEach(function (y) {
+      cell = { x: x, y: y };
+      tile = self.grid.cellContent(cell);
+
+      if (tile) {
+        var positions = self.findFarthestPosition(cell, vector);
+        var next      = self.grid.cellContent(positions.next);
+
+        // Only one merger per row traversal?每行遍历只有一次合并?
+        if (next && next.value === tile.value && !next.mergedFrom) {
+          var merged = new Tile(positions.next, tile.value * 2);
+          merged.mergedFrom = [tile, next];
+
+          self.grid.insertTile(merged);
+          self.grid.removeTile(tile);
+
+          // Converge the two tiles' positions
+					// 收敛两个元素的位置
+          tile.updatePosition(positions.next);
+
+          // Update the score
+					// 会聚两个元素的位置-更新分数
+          self.score += merged.value;
+
+          // The mighty 2048 tile
+          if (merged.value === 2048) self.won = true;
+        } else {
+          self.moveTile(tile, positions.farthest);
+        }
+
+        if (!self.positionsEqual(cell, tile)) {
+          moved = true; // The tile moved from its original cell!
+        // 元素从原来的网格里移动了!
+				}
+      }
+    });
+  });
+
+  if (moved) {
+    this.addRandomTile();
+
+    if (!this.movesAvailable()) {
+      this.over = true; // 你输啦!
+    }
+
+    this.actuate();
+  }
+};
+
+// Get the vector representing the chosen direction
+// 获取表示所选方向的向量
+GameManager.prototype.getVector = function (direction) {
+  // Vectors representing tile movement
+	// 表示平铺运动的矢量
+  var map = {
+    0: { x: 0,  y: -1 }, // Up
+    1: { x: 1,  y: 0 },  // Right
+    2: { x: 0,  y: 1 },  // Down
+    3: { x: -1, y: 0 }   // Left
+  };
+
+  return map[direction];
+};
+
+// Build a list of positions to traverse in the right order
+// 按正确的顺序建立要遍历的位置列表。
+GameManager.prototype.buildTraversals = function (vector) {
+  var traversals = { x: [], y: [] };
+
+  for (var pos = 0; pos < this.size; pos++) {
+    traversals.x.push(pos);
+    traversals.y.push(pos);
+  }
+
+  // Always traverse from the farthest cell in the chosen direction  总是沿着选择的方向从最远的网格中走过。
+  if (vector.x === 1) traversals.x = traversals.x.reverse();
+  if (vector.y === 1) traversals.y = traversals.y.reverse();
+
+  return traversals;
+};
+
+GameManager.prototype.findFarthestPosition = function (cell, vector) {
+  var previous;
+
+  // Progress towards the vector direction until an obstacle is found    向矢量方向前进,直到发现障碍
+  do {
+    previous = cell;
+    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
+  } while (this.grid.withinBounds(cell) &&
+           this.grid.cellAvailable(cell));
+
+  return {
+    farthest: previous,
+    next: cell // Used to check if a merge is required用于检查是否需要合并。
+  };
+};
+
+GameManager.prototype.movesAvailable = function () {
+  return this.grid.cellsAvailable() || this.tileMatchesAvailable();
+};
+
+// Check for available matches between tiles (more expensive check)检查平铺之间的可用匹配
+GameManager.prototype.tileMatchesAvailable = function () {
+  var self = this;
+
+  var tile;
+
+  for (var x = 0; x < this.size; x++) {
+    for (var y = 0; y < this.size; y++) {
+      tile = this.grid.cellContent({ x: x, y: y });
+
+      if (tile) {
+        for (var direction = 0; direction < 4; direction++) {
+          var vector = self.getVector(direction);
+          var cell   = { x: x + vector.x, y: y + vector.y };
+
+          var other  = self.grid.cellContent(cell);
+
+          if (other && other.value === tile.value) {
+            return true; // These two tiles can be merged这两个元素可以合并。
+          }
+        }
+      }
+    }
+  }
+
+  return false;
+};
+
+GameManager.prototype.positionsEqual = function (first, second) {
+  return first.x === second.x && first.y === second.y;
+};

+ 117 - 0
game/2048/js/grid.js

@@ -0,0 +1,117 @@
+function Grid(size, previousState) {
+  this.size = size;
+  this.cells = previousState ? this.fromState(previousState) : this.empty();
+}
+
+// Build a grid of the specified size
+Grid.prototype.empty = function () {
+  var cells = [];
+
+  for (var x = 0; x < this.size; x++) {
+    var row = cells[x] = [];
+
+    for (var y = 0; y < this.size; y++) {
+      row.push(null);
+    }
+  }
+
+  return cells;
+};
+
+Grid.prototype.fromState = function (state) {
+  var cells = [];
+
+  for (var x = 0; x < this.size; x++) {
+    var row = cells[x] = [];
+
+    for (var y = 0; y < this.size; y++) {
+      var tile = state[x][y];
+      row.push(tile ? new Tile(tile.position, tile.value) : null);
+    }
+  }
+
+  return cells;
+};
+
+// Find the first available random position
+Grid.prototype.randomAvailableCell = function () {
+  var cells = this.availableCells();
+
+  if (cells.length) {
+    return cells[Math.floor(Math.random() * cells.length)];
+  }
+};
+
+Grid.prototype.availableCells = function () {
+  var cells = [];
+
+  this.eachCell(function (x, y, tile) {
+    if (!tile) {
+      cells.push({ x: x, y: y });
+    }
+  });
+
+  return cells;
+};
+
+// Call callback for every cell
+Grid.prototype.eachCell = function (callback) {
+  for (var x = 0; x < this.size; x++) {
+    for (var y = 0; y < this.size; y++) {
+      callback(x, y, this.cells[x][y]);
+    }
+  }
+};
+
+// Check if there are any cells available
+Grid.prototype.cellsAvailable = function () {
+  return !!this.availableCells().length;
+};
+
+// Check if the specified cell is taken
+Grid.prototype.cellAvailable = function (cell) {
+  return !this.cellOccupied(cell);
+};
+
+Grid.prototype.cellOccupied = function (cell) {
+  return !!this.cellContent(cell);
+};
+
+Grid.prototype.cellContent = function (cell) {
+  if (this.withinBounds(cell)) {
+    return this.cells[cell.x][cell.y];
+  } else {
+    return null;
+  }
+};
+
+// Inserts a tile at its position
+Grid.prototype.insertTile = function (tile) {
+  this.cells[tile.x][tile.y] = tile;
+};
+
+Grid.prototype.removeTile = function (tile) {
+  this.cells[tile.x][tile.y] = null;
+};
+
+Grid.prototype.withinBounds = function (position) {
+  return position.x >= 0 && position.x < this.size &&
+         position.y >= 0 && position.y < this.size;
+};
+
+Grid.prototype.serialize = function () {
+  var cellState = [];
+
+  for (var x = 0; x < this.size; x++) {
+    var row = cellState[x] = [];
+
+    for (var y = 0; y < this.size; y++) {
+      row.push(this.cells[x][y] ? this.cells[x][y].serialize() : null);
+    }
+  }
+
+  return {
+    size: this.size,
+    cells: cellState
+  };
+};

+ 139 - 0
game/2048/js/html_actuator.js

@@ -0,0 +1,139 @@
+function HTMLActuator() {
+  this.tileContainer    = document.querySelector(".tile-container");
+  this.scoreContainer   = document.querySelector(".score-container");
+  this.bestContainer    = document.querySelector(".best-container");
+  this.messageContainer = document.querySelector(".game-message");
+
+  this.score = 0;
+}
+
+HTMLActuator.prototype.actuate = function (grid, metadata) {
+  var self = this;
+
+  window.requestAnimationFrame(function () {
+    self.clearContainer(self.tileContainer);
+
+    grid.cells.forEach(function (column) {
+      column.forEach(function (cell) {
+        if (cell) {
+          self.addTile(cell);
+        }
+      });
+    });
+
+    self.updateScore(metadata.score);
+    self.updateBestScore(metadata.bestScore);
+
+    if (metadata.terminated) {
+      if (metadata.over) {
+        self.message(false); // You lose
+      } else if (metadata.won) {
+        self.message(true); // You win!
+      }
+    }
+
+  });
+};
+
+// Continues the game (both restart and keep playing)
+HTMLActuator.prototype.continueGame = function () {
+  this.clearMessage();
+};
+
+HTMLActuator.prototype.clearContainer = function (container) {
+  while (container.firstChild) {
+    container.removeChild(container.firstChild);
+  }
+};
+
+HTMLActuator.prototype.addTile = function (tile) {
+  var self = this;
+
+  var wrapper   = document.createElement("div");
+  var inner     = document.createElement("div");
+  var position  = tile.previousPosition || { x: tile.x, y: tile.y };
+  var positionClass = this.positionClass(position);
+
+  // We can't use classlist because it somehow glitches when replacing classes
+  var classes = ["tile", "tile-" + tile.value, positionClass];
+
+  if (tile.value > 2048) classes.push("tile-super");
+
+  this.applyClasses(wrapper, classes);
+
+  inner.classList.add("tile-inner");
+  inner.textContent = tile.value;
+
+  if (tile.previousPosition) {
+    // Make sure that the tile gets rendered in the previous position first
+    window.requestAnimationFrame(function () {
+      classes[2] = self.positionClass({ x: tile.x, y: tile.y });
+      self.applyClasses(wrapper, classes); // Update the position
+    });
+  } else if (tile.mergedFrom) {
+    classes.push("tile-merged");
+    this.applyClasses(wrapper, classes);
+
+    // Render the tiles that merged
+    tile.mergedFrom.forEach(function (merged) {
+      self.addTile(merged);
+    });
+  } else {
+    classes.push("tile-new");
+    this.applyClasses(wrapper, classes);
+  }
+
+  // Add the inner part of the tile to the wrapper
+  wrapper.appendChild(inner);
+
+  // Put the tile on the board
+  this.tileContainer.appendChild(wrapper);
+};
+
+HTMLActuator.prototype.applyClasses = function (element, classes) {
+  element.setAttribute("class", classes.join(" "));
+};
+
+HTMLActuator.prototype.normalizePosition = function (position) {
+  return { x: position.x + 1, y: position.y + 1 };
+};
+
+HTMLActuator.prototype.positionClass = function (position) {
+  position = this.normalizePosition(position);
+  return "tile-position-" + position.x + "-" + position.y;
+};
+
+HTMLActuator.prototype.updateScore = function (score) {
+  this.clearContainer(this.scoreContainer);
+
+  var difference = score - this.score;
+  this.score = score;
+
+  this.scoreContainer.textContent = this.score;
+
+  if (difference > 0) {
+    var addition = document.createElement("div");
+    addition.classList.add("score-addition");
+    addition.textContent = "+" + difference;
+
+    this.scoreContainer.appendChild(addition);
+  }
+};
+
+HTMLActuator.prototype.updateBestScore = function (bestScore) {
+  this.bestContainer.textContent = bestScore;
+};
+
+HTMLActuator.prototype.message = function (won) {
+  var type    = won ? "game-won" : "game-over";
+  var message = won ? "You win!" : "Game over!";
+
+  this.messageContainer.classList.add(type);
+  this.messageContainer.getElementsByTagName("p")[0].textContent = message;
+};
+
+HTMLActuator.prototype.clearMessage = function () {
+  // IE only takes one value to remove at a time.
+  this.messageContainer.classList.remove("game-won");
+  this.messageContainer.classList.remove("game-over");
+};

+ 151 - 0
game/2048/js/keyboard_input_manager.js

@@ -0,0 +1,151 @@
+function KeyboardInputManager() {
+	this.events = {};
+
+	if (window.navigator.msPointerEnabled) {
+		//Internet Explorer 10 style
+		this.eventTouchstart = "MSPointerDown";
+		this.eventTouchmove = "MSPointerMove";
+		this.eventTouchend = "MSPointerUp";
+	} else {
+		this.eventTouchstart = "touchstart";
+		this.eventTouchmove = "touchmove";
+		this.eventTouchend = "touchend";
+	}
+
+	this.listen();
+}
+function one(){
+	
+}
+
+KeyboardInputManager.prototype.on = function(event, callback) {
+	if (!this.events[event]) {
+		this.events[event] = [];
+	}
+	this.events[event].push(callback);
+};
+
+KeyboardInputManager.prototype.emit = function(event, data) {
+	var callbacks = this.events[event];
+	if (callbacks) {
+		callbacks.forEach(function(callback) {
+			callback(data);
+		});
+	}
+};
+
+KeyboardInputManager.prototype.listen = function() {
+	var self = this;
+
+	var map = {
+		38: 0, // Up
+		39: 1, // Right
+		40: 2, // Down
+		37: 3, // Left
+		75: 0, // Vim up
+		76: 1, // Vim right
+		74: 2, // Vim down
+		72: 3, // Vim left
+		87: 0, // W
+		68: 1, // D
+		83: 2, // S
+		65: 3 // A
+	};
+
+	// Respond to direction keys
+	document.addEventListener("keydown", function(event) {
+		var modifiers = event.altKey || event.ctrlKey || event.metaKey ||
+			event.shiftKey;
+		var mapped = map[event.which];
+		if (!modifiers) {
+			if (mapped !== undefined) {
+				event.preventDefault();
+				self.emit("move", mapped);
+			}
+		}
+
+		// R key restarts the gameR键重新启动游戏
+		if (!modifiers && event.which === 27) {
+			self.restart.call(self, event);
+		}else if (!modifiers && event.which === 13) {
+			self.restart.call(self, event);
+		}
+	});
+
+	// Respond to button presses
+	// 响应按下按钮
+	this.bindButtonPress(".retry-button", this.restart);
+	this.bindButtonPress(".restart-button", this.restart);
+	this.bindButtonPress(".keep-playing-button", this.keepPlaying);
+
+	// Respond to swipe events响应滑动事件
+	var touchStartClientX, touchStartClientY;
+	var gameContainer = document.getElementsByClassName("game-container")[0];
+
+	gameContainer.addEventListener(this.eventTouchstart, function(event) {
+		if ((!window.navigator.msPointerEnabled && event.touches.length > 1) ||
+			event.targetTouches > 1) {
+			return; // Ignore if touching with more than 1 finger如果超过一个手指触摸,忽略
+		}
+
+		if (window.navigator.msPointerEnabled) {
+			touchStartClientX = event.pageX;
+			touchStartClientY = event.pageY;
+		} else {
+			touchStartClientX = event.touches[0].clientX;
+			touchStartClientY = event.touches[0].clientY;
+		}
+
+		event.preventDefault();
+	});
+
+	gameContainer.addEventListener(this.eventTouchmove, function(event) {
+		event.preventDefault();
+	});
+
+	gameContainer.addEventListener(this.eventTouchend, function(event) {
+		if ((!window.navigator.msPointerEnabled && event.touches.length > 0) ||
+			event.targetTouches > 0) {
+			return; // Ignore if still touching with one or more fingers如果仍用一个或多个手指触摸,则忽略
+		}
+
+		var touchEndClientX, touchEndClientY;
+
+		if (window.navigator.msPointerEnabled) {
+			touchEndClientX = event.pageX;
+			touchEndClientY = event.pageY;
+		} else {
+			touchEndClientX = event.changedTouches[0].clientX;
+			touchEndClientY = event.changedTouches[0].clientY;
+		}
+
+		var dx = touchEndClientX - touchStartClientX;
+		var absDx = Math.abs(dx);
+
+		var dy = touchEndClientY - touchStartClientY;
+		var absDy = Math.abs(dy);
+
+		if (Math.max(absDx, absDy) > 10) {
+			// (right : left) : (down : up)
+			self.emit("move", absDx > absDy ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0));
+		}
+	});
+};
+
+
+
+KeyboardInputManager.prototype.restart = function(event) {
+	event.preventDefault();
+	this.emit("restart");
+};
+
+KeyboardInputManager.prototype.keepPlaying = function(event) {
+	event.preventDefault();
+	this.emit("keepPlaying");
+};
+
+KeyboardInputManager.prototype.bindButtonPress = function(selector, fn) {
+	var button = document.querySelector(selector);
+	button.addEventListener("click", fn.bind(this));
+	button.addEventListener(this.eventTouchend, fn.bind(this));
+};

+ 63 - 0
game/2048/js/local_storage_manager.js

@@ -0,0 +1,63 @@
+window.fakeStorage = {
+  _data: {},
+
+  setItem: function (id, val) {
+    return this._data[id] = String(val);
+  },
+
+  getItem: function (id) {
+    return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
+  },
+
+  removeItem: function (id) {
+    return delete this._data[id];
+  },
+
+  clear: function () {
+    return this._data = {};
+  }
+};
+
+function LocalStorageManager() {
+  this.bestScoreKey     = "bestScore";
+  this.gameStateKey     = "gameState";
+
+  var supported = this.localStorageSupported();
+  this.storage = supported ? window.localStorage : window.fakeStorage;
+}
+
+LocalStorageManager.prototype.localStorageSupported = function () {
+  var testKey = "test";
+  var storage = window.localStorage;
+
+  try {
+    storage.setItem(testKey, "1");
+    storage.removeItem(testKey);
+    return true;
+  } catch (error) {
+    return false;
+  }
+};
+
+// Best score getters/setters
+LocalStorageManager.prototype.getBestScore = function () {
+  return this.storage.getItem(this.bestScoreKey) || 0;
+};
+
+LocalStorageManager.prototype.setBestScore = function (score) {
+  this.storage.setItem(this.bestScoreKey, score);
+};
+
+// Game state getters/setters and clearing
+LocalStorageManager.prototype.getGameState = function () {
+  var stateJSON = this.storage.getItem(this.gameStateKey);
+  return stateJSON ? JSON.parse(stateJSON) : null;
+};
+
+LocalStorageManager.prototype.setGameState = function (gameState) {
+  this.storage.setItem(this.gameStateKey, JSON.stringify(gameState));
+};
+
+LocalStorageManager.prototype.clearGameState = function () {
+  this.storage.removeItem(this.gameStateKey);
+};

+ 27 - 0
game/2048/js/tile.js

@@ -0,0 +1,27 @@
+function Tile(position, value) {
+  this.x                = position.x;
+  this.y                = position.y;
+  this.value            = value || 2;
+
+  this.previousPosition = null;
+  this.mergedFrom       = null; // Tracks tiles that merged together
+}
+
+Tile.prototype.savePosition = function () {
+  this.previousPosition = { x: this.x, y: this.y };
+};
+
+Tile.prototype.updatePosition = function (position) {
+  this.x = position.x;
+  this.y = position.y;
+};
+
+Tile.prototype.serialize = function () {
+  return {
+    position: {
+      x: this.x,
+      y: this.y
+    },
+    value: this.value
+  };
+};

BIN
game/2048/meta/apple-touch-icon.png


BIN
game/2048/meta/apple-touch-startup-image-640x1096.png


BIN
game/2048/meta/apple-touch-startup-image-640x920.png


+ 40 - 0
game/2048/style/base.css

@@ -0,0 +1,40 @@
+/*(1)清除默认样式*/
+html, body, ul, li, ol, dl, dd, dt, p, h1, h2, h3, h4, h5, h6, form, fieldset, legend, img,input{ margin:0; padding:0; }
+fieldset, img,input,button { border:0 none; padding:0;margin:0;outline-style:none; }   /*去掉input等聚焦时的蓝色边框*/
+ul,li,ol{ list-style:none; }
+
+select, input { vertical-align:middle;}
+
+textarea { resize:none; } /*防止拖动*/
+img {border:0; vertical-align:middle; }  
+/*  去掉图片低测默认的3像素空白缝隙*/
+ table { border-collapse:collapse; } 
+
+a {text-decoration:none;color: #000000;}
+bady{
+	height: 100%;
+	width: 100%;
+}
+
+/*固定盒子*/
+/**{
+	box-sizing: border-box;
+}*/
+
+.clearfix:before,.clearfix:after {  /*清楚浮动*/
+    content:"";
+    display:table;
+}
+.clearfix:after{clear:both;}
+.clearfix{
+    *zoom:1;/*IE/7/6*/
+}
+
+/*公共类*/
+.container {  /*内容、版心 提取 */
+    width: 1190px;
+    /*100%是继承父元素的高或者宽*/
+    height: 100%; 
+    /*background-color:yellow;*/
+    margin:0 auto;
+}

BIN
game/2048/style/fonts/ClearSans-Bold-webfont.eot


Diferenças do arquivo suprimidas por serem muito extensas
+ 640 - 0
game/2048/style/fonts/ClearSans-Bold-webfont.svg


BIN
game/2048/style/fonts/ClearSans-Bold-webfont.woff


BIN
game/2048/style/fonts/ClearSans-Light-webfont.eot


Diferenças do arquivo suprimidas por serem muito extensas
+ 670 - 0
game/2048/style/fonts/ClearSans-Light-webfont.svg


BIN
game/2048/style/fonts/ClearSans-Light-webfont.woff


BIN
game/2048/style/fonts/ClearSans-Regular-webfont.eot


Diferenças do arquivo suprimidas por serem muito extensas
+ 669 - 0
game/2048/style/fonts/ClearSans-Regular-webfont.svg


BIN
game/2048/style/fonts/ClearSans-Regular-webfont.woff


+ 30 - 0
game/2048/style/fonts/clear-sans.css

@@ -0,0 +1,30 @@
+@font-face {
+    font-family: "Clear Sans";
+    src: url("ClearSans-Light-webfont.eot");
+    src: url("ClearSans-Light-webfont.eot?#iefix") format("embedded-opentype"),
+         url("ClearSans-Light-webfont.svg#clear_sans_lightregular") format("svg"),
+         url("ClearSans-Light-webfont.woff") format("woff");
+    font-weight: 200;
+    font-style: normal;
+}
+
+@font-face {
+    font-family: "Clear Sans";
+    src: url("ClearSans-Regular-webfont.eot");
+    src: url("ClearSans-Regular-webfont.eot?#iefix") format("embedded-opentype"),
+         url("ClearSans-Regular-webfont.svg#clear_sansregular") format("svg"),
+         url("ClearSans-Regular-webfont.woff") format("woff");
+    font-weight: normal;
+    font-style: normal;
+}
+
+@font-face {
+    font-family: "Clear Sans";
+    src: url("ClearSans-Bold-webfont.eot");
+    src: url("ClearSans-Bold-webfont.eot?#iefix") format("embedded-opentype"),
+         url("ClearSans-Bold-webfont.svg#clear_sansbold") format("svg"),
+         url("ClearSans-Bold-webfont.woff") format("woff");
+    font-weight: 700;
+    font-style: normal;
+}
+

+ 81 - 0
game/2048/style/helpers.scss

@@ -0,0 +1,81 @@
+// Exponent
+// From: https://github.com/Team-Sass/Sassy-math/blob/master/sass/math.scss#L36
+
+@function exponent($base, $exponent) {
+  // reset value
+  $value: $base;
+  // positive intergers get multiplied
+  @if $exponent > 1 {
+    @for $i from 2 through $exponent {
+      $value: $value * $base; } }
+  // negitive intergers get divided. A number divided by itself is 1
+  @if $exponent < 1 {
+    @for $i from 0 through -$exponent {
+      $value: $value / $base; } }
+  // return the last value written
+  @return $value;
+}
+
+@function pow($base, $exponent) {
+  @return exponent($base, $exponent);
+}
+
+// Transition mixins
+@mixin transition($args...) {
+  -webkit-transition: $args;
+  -moz-transition: $args;
+  transition: $args;
+}
+
+@mixin transition-property($args...) {
+  -webkit-transition-property: $args;
+  -moz-transition-property: $args;
+  transition-property: $args;
+}
+
+@mixin animation($args...) {
+  -webkit-animation: $args;
+  -moz-animation: $args;
+  animation: $args;
+}
+
+@mixin animation-fill-mode($args...) {
+  -webkit-animation-fill-mode: $args;
+  -moz-animation-fill-mode: $args;
+  animation-fill-mode: $args;
+}
+
+@mixin transform($args...) {
+  -webkit-transform: $args;
+  -moz-transform: $args;
+  transform: $args;
+}
+
+// Keyframe animations
+@mixin keyframes($animation-name) {
+  @-webkit-keyframes $animation-name {
+    @content;
+  }
+  @-moz-keyframes $animation-name {
+    @content;
+  }
+  @keyframes $animation-name {
+    @content;
+  }
+}
+
+// Media queries
+@mixin smaller($width) {
+  @media screen and (max-width: $width) {
+    @content;
+  }
+}
+
+// Clearfix
+@mixin clearfix {
+  &:after {
+    content: "";
+    display: block;
+    clear: both;
+  }
+}

+ 764 - 0
game/2048/style/main.css

@@ -0,0 +1,764 @@
+@import url(fonts/clear-sans.css);
+
+html,
+body {
+	margin: 0;
+	padding: 0;
+	background: #faf8ef;
+	color: #776e65;
+	font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif;
+	font-size: 18px;
+}
+
+
+
+h1.title {
+	font-size: 80px;
+	font-weight: bold;
+	display: block;
+	position: absolute;
+}
+
+@-webkit-keyframes move-up {
+	0% {
+		top: 25px;
+		opacity: 1;
+	}
+
+	100% {
+		top: -50px;
+		opacity: 0;
+	}
+}
+
+@-moz-keyframes move-up {
+	0% {
+		top: 25px;
+		opacity: 1;
+	}
+
+	100% {
+		top: -50px;
+		opacity: 0;
+	}
+}
+
+@keyframes move-up {
+	0% {
+		top: 25px;
+		opacity: 1;
+	}
+
+	100% {
+		top: -50px;
+		opacity: 0;
+	}
+}
+
+.scores-container {
+display: block;
+	margin-top: 300px;
+	float: left;
+	text-align: right;
+}
+.best-container{
+	margin-right: 100px;
+}
+.score-container,
+.best-container {
+	width: 100px;
+	height: 50px;
+	position: relative;
+	display: inline-block;
+	background: #bbada0;
+	padding: 15px 25px;
+	font-size: 25px;
+	height: 25px;
+	line-height: 47px;
+	font-weight: bold;
+	border-radius: 3px;
+	color: white;
+	margin-top: 8px;
+	text-align: center;
+}
+
+.score-container:after,
+.best-container:after {
+	position: absolute;
+	width: 100%;
+	top: 10px;
+	left: 0;
+	text-transform: uppercase;
+	font-size: 13px;
+	line-height: 13px;
+	text-align: center;
+	color: #eee4da;
+}
+
+.score-container .score-addition,
+.best-container .score-addition {
+	position: absolute;
+	right: 30px;
+	color: red;
+	font-size: 25px;
+	line-height: 25px;
+	font-weight: bold;
+	color: rgba(119, 110, 101, 0.9);
+	z-index: 100;
+	-webkit-animation: move-up 600ms ease-in;
+	-moz-animation: move-up 600ms ease-in;
+	animation: move-up 600ms ease-in;
+	-webkit-animation-fill-mode: both;
+	-moz-animation-fill-mode: both;
+	animation-fill-mode: both;
+}
+
+.score-container:after {
+	content: "我的分数";
+}
+
+.best-container:after {
+	content: "最高分数";
+}
+
+p {
+	margin-top: 0;
+	margin-bottom: 10px;
+	line-height: 1.65;
+}
+
+a {
+	color: #776e65;
+	font-weight: bold;
+	text-decoration: underline;
+	cursor: pointer;
+}
+
+strong.important {
+	text-transform: uppercase;
+}
+
+hr {
+	border: none;
+	border-bottom: 1px solid #d8d4d0;
+	margin-top: 20px;
+	margin-bottom: 30px;
+}
+
+.container {
+	width: 1280px;
+	height: 720px;
+}
+
+
+
+
+@-webkit-keyframes fade-in {
+	0% {
+		opacity: 0;
+	}
+
+	100% {
+		opacity: 1;
+	}
+}
+
+@-moz-keyframes fade-in {
+	0% {
+		opacity: 0;
+	}
+
+	100% {
+		opacity: 1;
+	}
+}
+
+@keyframes fade-in {
+	0% {
+		opacity: 0;
+	}
+
+	100% {
+		opacity: 1;
+	}
+}
+
+.game-container {
+	margin-left: 560px;
+	 margin-top: 30px; 
+	position: relative;
+	padding: 15px;
+	cursor: default;
+	-webkit-touch-callout: none;
+	-ms-touch-callout: none;
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	-ms-touch-action: none;
+	touch-action: none;
+	background: #bbada0;
+	border-radius: 6px;
+	width: 660px;
+	height: 660px;
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+}
+
+.game-container .game-message {
+	display: none;
+	position: absolute;
+	top: 0;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	background: rgba(238, 228, 218, 0.5);
+	z-index: 100;
+	text-align: center;
+	-webkit-animation: fade-in 800ms ease 1200ms;
+	-moz-animation: fade-in 800ms ease 1200ms;
+	animation: fade-in 800ms ease 1200ms;
+	-webkit-animation-fill-mode: both;
+	-moz-animation-fill-mode: both;
+	animation-fill-mode: both;
+}
+
+.game-container .game-message p {
+	font-size: 60px;
+	font-weight: bold;
+	height: 60px;
+	line-height: 60px;
+	margin-top: 222px;
+}
+
+.game-container .game-message .lower {
+	display: block;
+	margin-top: 59px;
+}
+
+.game-container .game-message a {
+	display: inline-block;
+	background: #8f7a66;
+	border-radius: 3px;
+	padding: 0 20px;
+	text-decoration: none;
+	color: #f9f6f2;
+	height: 40px;
+	line-height: 42px;
+	margin-left: 9px;
+}
+
+.game-container .game-message a.keep-playing-button {
+	display: none;
+}
+
+.game-container .game-message.game-won {
+	background: rgba(237, 194, 46, 0.5);
+	color: #f9f6f2;
+}
+
+.game-container .game-message.game-won a.keep-playing-button {
+	display: inline-block;
+}
+
+.game-container .game-message.game-won,
+.game-container .game-message.game-over {
+	display: block;
+}
+
+.grid-container {
+	position: absolute;
+	z-index: 1;
+}
+
+.grid-row {
+	margin-bottom: 15px;
+}
+
+.grid-row:last-child {
+	margin-bottom: 0;
+}
+
+.grid-row:after {
+	content: "";
+	display: block;
+	clear: both;
+}
+
+.grid-cell {
+	width: 146.5px;
+	height: 146.5px;
+	margin-right: 15px;
+	float: left;
+	border-radius: 3px;
+	background: rgba(238, 228, 218, 0.35);
+}
+
+.grid-cell:last-child {
+	margin-right: 0;
+}
+
+.tile-container {
+	width: 631px;
+	height: 631px;
+	position: absolute;
+	z-index: 2;
+}
+
+.tile,
+.tile .tile-inner {
+	width: 146.5px;
+	height: 146.5px;
+	line-height: 146.5px;
+}
+
+.tile.tile-position-1-1 {
+	-webkit-transform: translate(0px, 0px);
+	-moz-transform: translate(0px, 0px);
+	transform: translate(0px, 0px);
+}
+
+.tile.tile-position-1-2 {
+	-webkit-transform: translate(0px, 161.5px);
+	-moz-transform: translate(0px, 161.5px);
+	transform: translate(0px, 161.5px);
+}
+
+.tile.tile-position-1-3 {
+	-webkit-transform: translate(0px, 323px);
+	-moz-transform: translate(0px, 323px);
+	transform: translate(0px, 323px);
+}
+
+.tile.tile-position-1-4 {
+	-webkit-transform: translate(0px, 484.5px);
+	-moz-transform: translate(0px, 484.5px);
+	transform: translate(0px, 484.5px);
+}
+
+.tile.tile-position-2-1 {
+	-webkit-transform: translate(161.5px, 0px);
+	-moz-transform: translate(161.5px, 0px);
+	transform: translate(161.5px, 0px);
+}
+
+.tile.tile-position-2-2 {
+	-webkit-transform: translate(161.5px, 161.5px);
+	-moz-transform: translate(161.5px, 161.5px);
+	transform: translate(161.5px, 161.5px);
+}
+
+.tile.tile-position-2-3 {
+	-webkit-transform: translate(161.5px, 323px);
+	-moz-transform: translate(161.5px, 323px);
+	transform: translate(161.5px, 323px);
+}
+
+.tile.tile-position-2-4 {
+	-webkit-transform: translate(161.5px, 484.5px);
+	-moz-transform: translate(161.5px, 484.5px);
+	transform: translate(161.5px, 484.5px);
+}
+
+.tile.tile-position-3-1 {
+	-webkit-transform: translate(323px, 0px);
+	-moz-transform: translate(323px, 0px);
+	transform: translate(323px, 0px);
+}
+
+.tile.tile-position-3-2 {
+	-webkit-transform: translate(323px, 161.5px);
+	-moz-transform: translate(323px, 161.5px);
+	transform: translate(323px, 161.5px);
+}
+
+.tile.tile-position-3-3 {
+	-webkit-transform: translate(323px, 323px);
+	-moz-transform: translate(323px, 323px);
+	transform: translate(323px, 323px);
+}
+
+.tile.tile-position-3-4 {
+	-webkit-transform: translate(323px, 484.5px);
+	-moz-transform: translate(323px, 484.5px);
+	transform: translate(323px, 484.5px);
+}
+
+.tile.tile-position-4-1 {
+	-webkit-transform: translate(484.5px, 0px);
+	-moz-transform: translate(484.5px, 0px);
+	transform: translate(484.5px, 0px);
+}
+
+.tile.tile-position-4-2 {
+	-webkit-transform: translate(484.5px, 161.5px);
+	-moz-transform: translate(484.5px, 161.5px);
+	transform: translate(484.5px, 161.5px);
+}
+
+.tile.tile-position-4-3 {
+	-webkit-transform: translate(484.5px, 323px);
+	-moz-transform: translate(484.5px, 323px);
+	transform: translate(484.5px, 323px);
+}
+
+.tile.tile-position-4-4 {
+	-webkit-transform: translate(484.5px, 484.5px);
+	-moz-transform: translate(484.5px, 484.5px);
+	transform: translate(484.5px, 484.5px);
+}
+
+.tile {
+	position: absolute;
+	-webkit-transition: 100ms ease-in-out;
+	-moz-transition: 100ms ease-in-out;
+	transition: 100ms ease-in-out;
+	-webkit-transition-property: -webkit-transform;
+	-moz-transition-property: -moz-transform;
+	transition-property: transform;
+}
+
+.tile .tile-inner {
+	border-radius: 3px;
+	background: #eee4da;
+	text-align: center;
+	font-weight: bold;
+	z-index: 10;
+	font-size: 55px;
+}
+
+.tile.tile-2 .tile-inner {
+	background: #eee4da;
+	box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0);
+}
+
+.tile.tile-4 .tile-inner {
+	background: #ede0c8;
+	box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0);
+}
+
+.tile.tile-8 .tile-inner {
+	color: #f9f6f2;
+	background: #f2b179;
+}
+
+.tile.tile-16 .tile-inner {
+	color: #f9f6f2;
+	background: #f59563;
+}
+
+.tile.tile-32 .tile-inner {
+	color: #f9f6f2;
+	background: #f67c5f;
+}
+
+.tile.tile-64 .tile-inner {
+	color: #f9f6f2;
+	background: #f65e3b;
+}
+
+.tile.tile-128 .tile-inner {
+	color: #f9f6f2;
+	background: #edcf72;
+	box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.2381), inset 0 0 0 1px rgba(255, 255, 255, 0.14286);
+	font-size: 45px;
+}
+
+@media screen and (max-width: 520px) {
+	.tile.tile-128 .tile-inner {
+		font-size: 25px;
+	}
+}
+
+.tile.tile-256 .tile-inner {
+	color: #f9f6f2;
+	background: #edcc61;
+	box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.31746), inset 0 0 0 1px rgba(255, 255, 255, 0.19048);
+	font-size: 45px;
+}
+
+@media screen and (max-width: 520px) {
+	.tile.tile-256 .tile-inner {
+		font-size: 25px;
+	}
+}
+
+.tile.tile-512 .tile-inner {
+	color: #f9f6f2;
+	background: #edc850;
+	box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.39683), inset 0 0 0 1px rgba(255, 255, 255, 0.2381);
+	font-size: 45px;
+}
+
+@media screen and (max-width: 520px) {
+	.tile.tile-512 .tile-inner {
+		font-size: 25px;
+	}
+}
+
+.tile.tile-1024 .tile-inner {
+	color: #f9f6f2;
+	background: #edc53f;
+	box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.47619), inset 0 0 0 1px rgba(255, 255, 255, 0.28571);
+	font-size: 35px;
+}
+
+@media screen and (max-width: 520px) {
+	.tile.tile-1024 .tile-inner {
+		font-size: 15px;
+	}
+}
+
+.tile.tile-2048 .tile-inner {
+	color: #f9f6f2;
+	background: #edc22e;
+	box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55556), inset 0 0 0 1px rgba(255, 255, 255, 0.33333);
+	font-size: 35px;
+}
+
+@media screen and (max-width: 520px) {
+	.tile.tile-2048 .tile-inner {
+		font-size: 15px;
+	}
+}
+
+.tile.tile-super .tile-inner {
+	color: #f9f6f2;
+	background: #3c3a32;
+	font-size: 30px;
+}
+
+@media screen and (max-width: 520px) {
+	.tile.tile-super .tile-inner {
+		font-size: 10px;
+	}
+}
+
+@-webkit-keyframes appear {
+	0% {
+		opacity: 0;
+		-webkit-transform: scale(0);
+		-moz-transform: scale(0);
+		transform: scale(0);
+	}
+
+	100% {
+		opacity: 1;
+		-webkit-transform: scale(1);
+		-moz-transform: scale(1);
+		transform: scale(1);
+	}
+}
+
+@-moz-keyframes appear {
+	0% {
+		opacity: 0;
+		-webkit-transform: scale(0);
+		-moz-transform: scale(0);
+		transform: scale(0);
+	}
+
+	100% {
+		opacity: 1;
+		-webkit-transform: scale(1);
+		-moz-transform: scale(1);
+		transform: scale(1);
+	}
+}
+
+@keyframes appear {
+	0% {
+		opacity: 0;
+		-webkit-transform: scale(0);
+		-moz-transform: scale(0);
+		transform: scale(0);
+	}
+
+	100% {
+		opacity: 1;
+		-webkit-transform: scale(1);
+		-moz-transform: scale(1);
+		transform: scale(1);
+	}
+}
+
+.tile-new .tile-inner {
+	-webkit-animation: appear 200ms ease 100ms;
+	-moz-animation: appear 200ms ease 100ms;
+	animation: appear 200ms ease 100ms;
+	-webkit-animation-fill-mode: backwards;
+	-moz-animation-fill-mode: backwards;
+	animation-fill-mode: backwards;
+}
+
+@-webkit-keyframes pop {
+	0% {
+		-webkit-transform: scale(0);
+		-moz-transform: scale(0);
+		transform: scale(0);
+	}
+
+	50% {
+		-webkit-transform: scale(1.2);
+		-moz-transform: scale(1.2);
+		transform: scale(1.2);
+	}
+
+	100% {
+		-webkit-transform: scale(1);
+		-moz-transform: scale(1);
+		transform: scale(1);
+	}
+}
+
+@-moz-keyframes pop {
+	0% {
+		-webkit-transform: scale(0);
+		-moz-transform: scale(0);
+		transform: scale(0);
+	}
+
+	50% {
+		-webkit-transform: scale(1.2);
+		-moz-transform: scale(1.2);
+		transform: scale(1.2);
+	}
+
+	100% {
+		-webkit-transform: scale(1);
+		-moz-transform: scale(1);
+		transform: scale(1);
+	}
+}
+
+@keyframes pop {
+	0% {
+		-webkit-transform: scale(0);
+		-moz-transform: scale(0);
+		transform: scale(0);
+	}
+
+	50% {
+		-webkit-transform: scale(1.2);
+		-moz-transform: scale(1.2);
+		transform: scale(1.2);
+	}
+
+	100% {
+		-webkit-transform: scale(1);
+		-moz-transform: scale(1);
+		transform: scale(1);
+	}
+}
+
+.tile-merged .tile-inner {
+	z-index: 20;
+	-webkit-animation: pop 200ms ease 100ms;
+	-moz-animation: pop 200ms ease 100ms;
+	animation: pop 200ms ease 100ms;
+	-webkit-animation-fill-mode: backwards;
+	-moz-animation-fill-mode: backwards;
+	animation-fill-mode: backwards;
+}
+
+.above-game{
+	position: absolute;
+	left: 120px;
+	top:550px;
+	width: 300px;
+	height: 50px;
+}
+
+.game-intro {
+	float: left;
+	line-height: 42px;
+	margin-bottom: 0;
+}
+
+.restart-button {
+	display: inline-block;
+	background: #8f7a66;
+	border-radius: 3px;
+	padding: 20px 80px;
+	text-decoration: none;
+	color: #f9f6f2;
+	height: 40px;
+	line-height: 42px;
+	display: block;
+	text-align: center;
+	float: right;
+	font-size: 30px;
+}
+
+.game-explanation {
+	margin-top: 50px;
+}
+
+@media screen and (max-width: 520px) {
+
+	html,
+	body {
+		font-size: 15px;
+	}
+
+	body {
+		margin: 20px 0;
+		padding: 0 20px;
+	}
+
+	h1.title {
+		font-size: 27px;
+		margin-top: 15px;
+	}
+
+	.score-container,
+	.best-container {
+		margin-top: 0;
+		padding: 15px 10px;
+		min-width: 40px;
+	}
+
+
+	.heading {
+		position: absolute;
+		left: 50px;
+		top: 50px;
+		display: inline-block;
+		width: 50px;
+		height: 400px;
+	}
+
+
+	.game-intro {
+		width: 55%;
+		display: block;
+		box-sizing: border-box;
+		line-height: 1.65;
+	}
+
+	.restart-button {
+		width: 42%;
+		padding: 0;
+		display: block;
+		box-sizing: border-box;
+		margin-top: 2px;
+	}
+	
+
+	.tile .tile-inner {
+		font-size: 35px;
+	}
+
+	.game-message p {
+		font-size: 30px !important;
+		height: 30px !important;
+		line-height: 30px !important;
+		margin-top: 90px !important;
+	}
+
+	.game-message .lower {
+		margin-top: 30px !important;
+	}
+}
+
+

BIN
game/EnglishTest/audio/2.mp3


BIN
game/EnglishTest/audio/bgm.mp3


BIN
game/EnglishTest/icon.png


BIN
game/EnglishTest/image/beeper.png


BIN
game/EnglishTest/image/beeper1.png


BIN
game/EnglishTest/image/bg.jpg


BIN
game/EnglishTest/image/bgq.jpg


BIN
game/EnglishTest/image/book.png


BIN
game/EnglishTest/image/fp_1a.png


BIN
game/EnglishTest/image/fp_1b.png


BIN
game/EnglishTest/image/fp_2a.png


BIN
game/EnglishTest/image/fp_2b.png


BIN
game/EnglishTest/image/fp_3a.png


BIN
game/EnglishTest/image/fp_3b.png


BIN
game/EnglishTest/image/fp_3c.png


BIN
game/EnglishTest/image/fp_3d.png


BIN
game/EnglishTest/image/fp_4.png


BIN
game/EnglishTest/image/fp_5.png


BIN
game/EnglishTest/image/fp_6a.png


BIN
game/EnglishTest/image/fp_6b.png


BIN
game/EnglishTest/image/fp_6c.png


BIN
game/EnglishTest/image/fp_7.png


BIN
game/EnglishTest/image/fp_8.png


BIN
game/EnglishTest/image/glass.png


BIN
game/EnglishTest/image/load_1.png


BIN
game/EnglishTest/image/load_2.png


BIN
game/EnglishTest/image/load_3.png


BIN
game/EnglishTest/image/load_4.png


BIN
game/EnglishTest/image/load_5.png


BIN
game/EnglishTest/image/logo.png


BIN
game/EnglishTest/image/lpage1.png


BIN
game/EnglishTest/image/lpage2_1.png


BIN
game/EnglishTest/image/lpage2_10.png


BIN
game/EnglishTest/image/lpage2_2.png


BIN
game/EnglishTest/image/lpage2_3.png


BIN
game/EnglishTest/image/lpage2_4.png


BIN
game/EnglishTest/image/lpage2_5.png


BIN
game/EnglishTest/image/lpage2_6.png


BIN
game/EnglishTest/image/lpage2_7.png


BIN
game/EnglishTest/image/lpage2_8.png


BIN
game/EnglishTest/image/lpage2_9.png


BIN
game/EnglishTest/image/lpage3_1.png


BIN
game/EnglishTest/image/lpage3_2.png


BIN
game/EnglishTest/image/lpage3_3.png


BIN
game/EnglishTest/image/lpage3_4.png


BIN
game/EnglishTest/image/lpage3_5.png


BIN
game/EnglishTest/image/lpage4_1.png


BIN
game/EnglishTest/image/lpage4_2.png


BIN
game/EnglishTest/image/lpage4_3.png


BIN
game/EnglishTest/image/lpage4_4.png


BIN
game/EnglishTest/image/lpage4_5.png


BIN
game/EnglishTest/image/lpage5.png


BIN
game/EnglishTest/image/lpage6.png


BIN
game/EnglishTest/image/lpage7.png


BIN
game/EnglishTest/image/off.png


BIN
game/EnglishTest/image/on.png


BIN
game/EnglishTest/image/p10_1n.png


BIN
game/EnglishTest/image/p10_1r.png


BIN
game/EnglishTest/image/p10_1w.png


BIN
game/EnglishTest/image/p10_aa.png


BIN
game/EnglishTest/image/p10_ab.png


BIN
game/EnglishTest/image/p10_ac.png


BIN
game/EnglishTest/image/p10_ad.png


BIN
game/EnglishTest/image/p10_t.png


BIN
game/EnglishTest/image/p1_1n.png


BIN
game/EnglishTest/image/p1_1r.png


BIN
game/EnglishTest/image/p1_1w.png


BIN
game/EnglishTest/image/p1_2.png


BIN
game/EnglishTest/image/p1_aa.png


BIN
game/EnglishTest/image/p1_ab.png


+ 0 - 0
game/EnglishTest/image/p1_ac.png


Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff