<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>网页版2048游戏</title>
<style>
/* 游戏棋盘格 */
body{font-family:Arial;text-align:center;}
.game{margin:0 auto;/*margin-top:40px;*/text-align:center;display:inline-block;}
.game-score{font-size:20px;margin:20px auto;}
.game-container{background-color:#bbada0;border-radius:10px;position:relative;}
.game-cell{border-radius:6px;background-color:#ccc0b3;position:absolute;}
.game-num{width:0px;height:0px;border-radius:6px;font-weight:bold;font-size:40px;color:#fff;text-align:center;position:absolute;}
.game-num-2{background:#eee4da;color:#776e65;}
.game-num-4{background:#ede0c8;color:#776e65;}
.game-num-8{background:#f2b179;}
.game-num-16{background:#f59563;}
.game-num-32{background:#f67c5f;}
.game-num-64{background:#f65e3b;}
.game-num-128{background:#edcf72;font-size:35px;}
.game-num-256{background:#edcc61;font-size:35px;}
.game-num-512{background:#9c0;font-size:35px;}
.game-num-1024{background:#33b5e5;font-size:30px;}
.game-num-2048{background:#09c;font-size:30px;}
/*游戏结束*/
.game-over{width:100%;height:100%;position:absolute;border-radius:10px;box-sizing:border-box;z-index:1;display:table;background:rgba(123,102,85,0.5)}
.game-over-info{display:table-cell;vertical-align:middle}
.game-over p{font-size:45px;color:#fff;margin:20px auto;}
.game-over span{cursor:pointer;background-color:rgba(103,82,65,0.6);display:block;margin:20px auto;width:180px;padding:10px 10px;font-size:25px;color:#f7f2e5;border-radius:10px;border:1px solid #978271;transition:all .2s}
.game-over span:hover{background-color:rgba(103,82,65,0.7);color:#fff}
.game-hide{display:none;}
</style>
</head>
<body>
<div id="game" class="game">
<div class="game-score">
<blockquote>
<p>分数:<span id="game_score">0</span></p>
</blockquote>
</div>
<div id="game_container" class="game-container">
<div id="game_over" class="game-over game-hide">
<div class="game-over-info">
<div id="game_over_info"></div>
<span id="game_restart">重新开始</span>
</div>
</div>
</div>
</div>
<script src="js/jquery-1.12.4.min.js"></script>
<script src="js/Game2048.js"></script>
<script>
Game2048({prefix: 'game', len: 4, size: 100, margin: 20});
</script>
</body>
</html>
(function(window, document, $) { function Game2048(opt) { var prefix = opt.prefix, len = opt.len, size = opt.size, margin = opt.margin; var score = 0; var winNum = 2048; var isGameOver = true; var board = new Board(len); var view = new View(prefix, len, size, margin); view.init(); board.onGenerate = function(e) { view.addNum(e.x, e.y, e.num); }; board.onMove = function(e) { if (e.to.num >= winNum) { isGameOver = true; setTimeout(function() { view.win(); }, 300); } if (e.to.num > e.from.num) { score += e.to.num; view.updateScore(score); } view.move(e.from, e.to); }; board.onMoveComplete = function(e) { if (!board.canMove()) { isGameOver = true; setTimeout(function() { view.over(score); }, 300); } if (e.moved) { setTimeout(function(){ board.generate(); }, 200); } }; $(document).keydown(function(e) { if (isGameOver) { return false; } switch (e.which) { case 37: board.moveLeft(); break; case 38: board.moveUp(); break; case 39: board.moveRight(); break; case 40: board.moveDown(); break; } }); function start() { score = 0; view.updateScore(0); view.cleanNum(); board.init(); board.generate(); board.generate(); isGameOver = false; } $('#' + prefix + '_restart').click(start); start(); }; // 数据处理 function Board(len) { this.len = len; this.arr = []; } Board.prototype = { // 事件 onGenerate: function() {}, onMove: function() {}, onMoveComplete: function() {}, // 创建数组 init: function() { for (var arr = [], x = 0, len = this.len; x < len; ++x) { arr[x] = []; for (var y = 0; y < len; ++y) { arr[x][y] = 0; } } this.arr = arr; }, // 在随机位置增加一个随机数 generate: function() { var empty = []; for (var x = 0, arr = this.arr, len = arr.length; x < len; ++x) { for (var y = 0; y < len; ++y) { if (arr[x][y] === 0) { empty.push({x: x, y: y}); } } } if (empty.length < 1) { return false; } var pos = empty[Math.floor((Math.random() * empty.length))]; this.arr[pos.x][pos.y] = Math.random() < 0.5 ? 2 : 4; this.onGenerate({x: pos.x, y: pos.y, num: this.arr[pos.x][pos.y]}); }, // 左移 moveLeft: function() { var canMove = false; // 从上到下,从左到右 for (var x = 0, len = this.arr.length; x < len; ++x) { for (var y = 0, arr = this.arr[x]; y < len; ++y) { // 从 y + 1 位置开始,向右查找 for (var next = y + 1; next < len; ++next) { // 如果 next 单元格是 0,找下一个不是 0 的单元格 if (arr[next] === 0) { continue; } // 如果 y 数字是 0,则将 next 移动到 y 位置,然后将 y 减 1 重新查找 if (arr[y] === 0) { arr[y] = arr[next]; this.onMove({from: {x: x, y: next, num: arr[next]}, to: {x: x, y: y, num: arr[y]}}); arr[next] = 0; canMove = true; --y; // 如果 y 与 next 单元格数字相等,则将 next 移动并合并给 y } else if (arr[y] === arr[next]) { arr[y] += arr[next]; this.onMove({from: {x: x, y: next, num: arr[next]}, to: {x: x, y: y, num: arr[y]}}); arr[next] = 0; canMove = true; } break; } } } this.onMoveComplete({moved: canMove}); }, moveRight: function() { var moved = false; for (var x = 0, len = this.arr.length; x < len; ++x) { for (var y = len - 1, arr = this.arr[x]; y >= 0; --y) { for (var prev = y - 1; prev >= 0; --prev) { if (arr[prev] === 0) { continue; } if (arr[y] === 0) { arr[y] = arr[prev]; this.onMove({from: {x: x, y: prev, num: arr[prev]}, to: {x: x, y: y, num: arr[y]}}); arr[prev] = 0; moved = true; ++y; } else if (arr[y] === arr[prev]) { arr[y] += arr[prev]; this.onMove({from: {x: x, y: prev, num: arr[prev]}, to: {x: x, y: y, num: arr[y]}}); arr[prev] = 0; moved = true; } break; } } } this.onMoveComplete({moved: moved}); }, moveUp: function() { var canMove = false; for (var arr = this.arr, len = arr.length, y = 0; y < len; ++y) { for (var x = 0; x < len; ++x) { for (var next = x + 1; next < len; ++next) { if (arr[next][y] === 0) { continue; } if (arr[x][y] === 0) { arr[x][y] = arr[next][y]; this.onMove({from: {x: next, y: y, num: arr[next][y]}, to: {x: x, y: y, num: arr[x][y]}}); arr[next][y] = 0; canMove = true; --x; } else if (arr[x][y] === arr[next][y]) { arr[x][y] += arr[next][y]; this.onMove({from: {x: next, y: y, num: arr[next][y]}, to: {x: x, y: y, num: arr[x][y]}}); arr[next][y] = 0; canMove = true; } break; } } } this.onMoveComplete({moved: canMove}); }, moveDown: function() { var canMove = false; for (var arr = this.arr, len = arr.length, y = 0; y < len; ++y) { for (var x = len - 1; x >= 0; --x) { for (var prev = x - 1; prev >= 0; --prev) { if (arr[prev][y] === 0) { continue; } if (arr[x][y] === 0) { arr[x][y] = arr[prev][y]; this.onMove({from: {x: prev, y: y, num: arr[prev][y]}, to: {x: x, y: y, num: arr[x][y]}}); arr[prev][y] = 0; canMove = true; ++x; } else if (arr[x][y] === arr[prev][y]) { arr[x][y] += arr[prev][y]; this.onMove({from: {x: prev, y: y, num: arr[prev][y]}, to: {x: x, y: y, num: arr[x][y]}}); arr[prev][y] = 0; canMove = true; } break; } } } this.onMoveComplete({moved: canMove}); }, canMove: function() { for (var x = 0, arr = this.arr, len = arr.length; x < len; ++x) { for (var y = 0; y < len; ++y) { if (arr[x][y] === 0) { return true; } var curr = arr[x][y], right = arr[x][y + 1]; var down = arr[x + 1] ? arr[x + 1][y] : null; if (right === curr || down === curr) { return true; } } } return false; } }; // 视图处理 function View(prefix, len, size, margin) { this.prefix = prefix; this.len = len; // 单元格单边的数量(实际数量 len * len) this.size = size; // 每个单元格的边长 this.margin = margin; // 每个单元格的间距 this.score = $('#' + prefix + '_score'); this.container = $('#' + prefix + '_container'); var containerSize = len * size + margin * (len + 1); this.container.css({width:containerSize , height: containerSize}); this.nums = {}; } View.prototype = { // 计算位置 getPos: function(n) { return this.margin + n * (this.size + this.margin); }, init: function() { for (var x = 0, len = this.len; x < len; ++x) { for (var y = 0; y < len; ++y) { var $cell = $('<div class="' + this.prefix + '-cell"></div>'); $cell.css({ width: this.size + 'px', height: this.size + 'px', top: this.getPos(x), left: this.getPos(y) }).appendTo(this.container); } } }, addNum: function(x, y, num) { var $num = $('<div class="' + this.prefix + '-num ' + this.prefix + '-num-' + num + ' ">'); $num.text(num).css({ top: this.getPos(x) + parseInt(this.size / 2), left: this.getPos(y) + parseInt(this.size / 2) }).appendTo(this.container).animate({ width: this.size + 'px', height: this.size + 'px', lineHeight: this.size + 'px', top: this.getPos(x), left: this.getPos(y) }, 100); this.nums[x + '-' + y] = $num; }, move: function(from, to) { var fromIndex = from.x + '-' + from.y, toIndex = to.x + '-' + to.y; var clean = this.nums[toIndex]; this.nums[toIndex] = this.nums[fromIndex]; delete this.nums[fromIndex]; var prefix = this.prefix + '-num-'; var pos = {top: this.getPos(to.x), left: this.getPos(to.y)}; this.nums[toIndex].finish().animate(pos, 200, function() { if (to.num > from.num) { clean.remove(); $(this).text(to.num).removeClass(prefix + from.num).addClass(prefix + to.num); } }); }, updateScore: function(score) { this.score.text(score); }, win: function() { $('#' + this.prefix + '_over_info').html('<p>您获胜了</p>'); $('#' + this.prefix + '_over').removeClass(this.prefix + '-hide'); }, over: function(score) { $('#' + this.prefix + '_over_info').html('<p>本次得分</p><p>' + score + '</p>'); $('#' + this.prefix + '_over').removeClass(this.prefix + '-hide'); }, cleanNum: function() { this.nums = {}; $('#' + this.prefix + '_over').addClass(this.prefix + '-hide'); $('.' + this.prefix + '-num').remove(); } }; window['Game2048'] = Game2048; })(window, document, jQuery);