2048这个游戏是通过对二维数组的操作来实现的,其算法核心如下:

  (以一行左移为例)

    c从0开始,遍历当前行中的元素,到<CN-1(CN是一个常量,表示的是游戏格子的列数)结束,每次+1

    找到当前位置下一个不为0的位置

    如果没找到

      直接退出循环

    否则

      如果当前值等于0

        将下一位置的值与当前位置的值交换

        将下一位置设为0

        将c-1(为了让下次循环依然检查当前位置)

      否则,如果当前值等于下一个值

        将当前值 * 2

        下一位置值设为0

具体代码如下

 <!-- 2048.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2048</title>
<link rel="stylesheet" href="2048.css">
<script src="2048.js"></script>
</head>
<body>
<!-- 分数 -->
<p>Score:<span id="score">0</span></p> <!-- 实现游戏的网格 -->
<div id="gridPanel">
</div> <div id="gameOver"><!--同时包含前景和背景的容器-->
<div><!--半透明灰色背景--></div>
<p><!--居中小窗口-->
Game Over!<br>
Score:<span id="finalScore"></span><br>
<a class="button" onclick="game.start()">Try again!</a>
</p>
</div>
</body>
</html>
 /*2048.css*/
@charset "utf-8"; #gridPanel{
width: 480px;
height: 480px;
margin: 0 auto;
background-color: #bbada0;
border-radius: 10px;
position: relative;
}
.grid, .cell{
width: 100px;
height: 100px;
border-radius: 6px;
}
.grid{
background-color: #ccc0b3;
float: left;
margin-top: 16px;
margin-left: 16px;
}
.cell{
text-align: center;
line-height: 100px;
color: #fff;
font-size: 60px;
position: absolute;
} /*由于想要更改游戏的网格数,设置了最大限制为8*8,所以此处前景格和背景格的样式如下*/
/*此功能的实现应该还有更简便的方法,能力有限,目前学的知识只能做到这样*/
#c00, #c01, #c02, #c03, #c04, #c05, #c06, #c07{top: 16px;}
#c10, #c11, #c12, #c13, #c14, #c15, #c16, #c17{top: 132px;}
#c20, #c21, #c22, #c23, #c24, #c25, #c26, #c27{top: 248px;}
#c30, #c31, #c32, #c33, #c34, #c35, #c36, #c37{top: 364px;}
#c40, #c41, #c42, #c43, #c44, #c45, #c46, #c47{top: 480px;}
#c50, #c51, #c52, #c53, #c54, #c55, #c56, #c57{top: 596px;}
#c60, #c61, #c62, #c63, #c64, #c65, #c66, #c67{top: 712px;}
#c70, #c71, #c72, #c73, #c74, #c75, #c76, #c77{top: 828px;} #c00, #c10, #c20, #c30, #c40, #c50, #c60, #c70{left: 16px;}
#c01, #c11, #c21, #c31, #c41, #c51, #c61, #c71{left: 132px;}
#c02, #c12, #c22, #c32, #c42, #c52, #c62, #c72{left: 248px;}
#c03, #c13, #c23, #c33, #c43, #c53, #c63, #c73{left: 364px;}
#c04, #c14, #c24, #c34, #c44, #c54, #c64, #c74{left: 480px;}
#c05, #c15, #c25, #c35, #c45, #c55, #c65, #c75{left: 596px;}
#c06, #c16, #c26, #c36, #c46, #c56, #c66, #c76{left: 712px;}
#c07, #c17, #c27, #c37, #c47, #c57, #c67, #c77{left: 828px;} .n2{background-color: #eee3da;}
.n4{background-color: #ede0c8;}
.n8{background-color: #f2b179;}
.n16{background-color: #f59563;}
.n32{background-color: #f67c5f;}
.n64{background-color: #f65e3d;}
.n128{background-color: #edcf72;}
.n256{background-color: #edcc61;}
.n512{background-color: #9c0;}
.n1024{background-color: #33b5e5;}
.n2048{background-color: #09c;}
.n4096{background-color: #a6c;}
.n8192{background-color: #93c;} .n2, .n4{color: #776e65;}
.n1024, .n2048, .n4096, .n8192{font-size: 40px;} /*显示分数*/
p{
width: 480px;
margin: 0 auto;
font-family: Arial;
font-weight: bold;
font-size: 40px;
padding-top: 50px;
} /* 游戏结束 */
/*Game Over*/
#gameOver{
width:100%;
height:100%;
position:absolute;
top:;
left:;
display:none;
}
#gameOver>div{
width:100%;
height:100%;
background-color:#555;
opacity:0.5;
}
#gameOver>p{
width:300px;
height:200px;
border:1px solid #edcf72;
line-height:1.6em;
text-align:center;
background-color:#fff;
border-radius:10px;
position:absolute;
top:50%;
left:50%;
margin-top:-100px;
margin-left:-150px;
}
.button{
padding:10px;
border-radius:6px;
background-color:#9f8b77;
color:#fff;
cursor:pointer;
}
 /*2048.js*/
var game = {
data : [], //存储所有单元格数据,二维数组
RN : 4, //总行数,可在此处改变,最大为8
CN : 4, //总列数,可在此处改变,最大为8
score : 0, //保存分数
state: 0, //游戏当前状态:Running|GameOver
RUNNING : 1, //运行中
GAMEOVER : 0, //游戏结束
PLAYING : 2; //动画播放中 //获得所有背景格的html代码
getGridHTML : function(){
for(var r = 0, arr = []; r < this.RN; r++){
for(var c = 0; c < this.CN; c++){
arr.push("" + r + c);
}
}
return '<div id="g' + arr.join('" class="grid"></div><div id="g') + '" class="grid"></div>';
},
//获得所有前景格的html代码
getCellHTML : function(){
for(var r = 0, arr = []; r < this.RN; r++){
for(var c = 0; c < this.CN; c++){
arr.push("" + r + c);
}
}
return '<div id="c' + arr.join('" class="cell"></div><div id="c') + '" class="cell"></div>';
},
//判断游戏状态为结束
isGameOver:function(){
//如果没有满,则返回false
if(!this.isFull()){
return false;
}else{//否则
//从左上角第一个元素开始,遍历二维数组
for(var r = 0; r < this.RN; r++){
for(var c = 0; c < this.CN; c++){
//如果当前元素不是最右侧元素
if(c < this.CN-1){
// 如果当前元素==右侧元素
if(this.data[r][c] == this.data[r][c + 1]){
return false;
}
}
//如果当前元素不是最下方元素
if(r < this.RN - 1){
// 如果当前元素==下方元素
if(this.data[r][c] == this.data[r + 1][c]){
return false;
}
}
}
}
return true;
}
},
//开始游戏
start : function(){
var panel = document.getElementById('gridPanel');
//游戏开始获得网格布局
panel.innerHTML = this.getGridHTML() + this.getCellHTML();
//将panel的高度设置为RN*116+16+"px"
panel.style.height = this.RN * 116 + 16 + 'px';
//将panel的宽度设置为CN*116+16+"px"
panel.style.width = this.CN * 116 + 16 + 'px'; this.data = []; //清空旧数组
for(var r = 0; r < this.RN; r++){ //r从0开始,到<RN结束,每次+1
this.data.push([]); //在data中压入一个空数组
for(var c = 0; c < this.CN; c++){ //c从0开始,到<CN结束,每次+1
this.data[r].push(0); //向data中r行,压入一个0
}
} this.state = this.RUNNING; //设置游戏状态
this.score = 0; //分数重置为0
//找到游戏结束界面,隐藏
var div = document.getElementById("gameOver");
div.style.display = "none"; this.randomNum();
this.randomNum();
this.updateView();
},
//初始界面生成两个随机数
randomNum : function(){ //在随机的不重复的位置生成一个2或4
if(!this.isFull()){ //只有不满时,才尝试生成随机数
for(;;){
var r = Math.floor(Math.random() * this.RN); //在0~RN-1之间生成一个行下标,存在r中
var c = Math.floor(Math.random() * this.CN); //在0~CN-1之间生成一个列下标,存在c中
/*
如果data中r行c列等于0
生成一个0~1之间的随机数
如果随机数>0.5,就在r行c列放入4
否则放入2
*/
if (this.data[r][c] == 0) {
this.data[r][c] = Math.random() > 0.5 ? 4 : 2;
break;// 退出循环
}
}
}
},
//将data数组中每个元素更新到页面div
updateView : function(){
//遍历data中每个元素的值
for(var r = 0; r < this.RN; r++){
for(var c = 0; c < this.CN; c++){
//找到页面上和当前位置对相应的div
var divObj = document.getElementById("c" + r + c);
if (this.data[r][c] == 0) { //如果当前值为0
divObj.innerHTML = ""; //清除innerHTML
divObj.className = "cell"; //还原className为"cell"
}else{
divObj.innerHTML = this.data[r][c]; //否则,将当前值放入innerHTML
divObj.className = "cell n" + this.data[r][c]; //修改className为"cell n" + 当前值
}
}
}
var span = document.getElementById("score");
span.innerHTML = this.score;
//判断并修改游戏状态为GAMEOVER
if(this.isGameOver()){
this.state = this.GAMEOVER;
var div = document.getElementById("gameOver");
var span = document.getElementById("finalScore");
span.innerHTML = this.score;
div.style.display = "block"; //修改div的style属性下的display子属性为"block"
}
},
//判断是否满格
isFull : function(){
for (var r = 0; r < this.RN; r++) {
for (var c = 0; c < this.CN; c++) {
if (this.data[r][c] == 0) { //如果当前元素等于0
return false; //返回false
}
}
}
return true; //遍历结束,返回true
},
//左移所有行
moveLeft : function(){
var before = this.data.toString();
for (var r = 0; r < this.RN; r++) { //遍历data中的每一行
this.moveLeftInRow(r); //左移当前行
}
var after = this.data.toString();
if(before != after){
animation.state();
// this.randomNum();
// this.updateView();
}
},
//左移一行,传入要移动的行号
moveLeftInRow : function(r){
//c从0开始,遍历当前行中的元素,到<CN-1结束,每次+1
for (var c = 0; c < this.CN-1; c++) {
//找到c之后下一个不为0的值的位置,存在next中
var nextc = this.getNextInRow(r,c);
if(nextc == -1){
break; //如果nextc等于-1,退出循环
}else{ //否则
if(this.data[r][c] == 0){ //如果当前位置等于0
this.data[r][c] = this.data[r][nextc]; //将当前位置设为下一个位置的值
this.data[r][nextc] = 0; //将下一位置设为0
var div = document.getElementById("c" + r + nextc);
animation.addTask(div,r,nextc,r,c);
c--; //保证下次依然检查当前元素
}else if(this.data[r][c] == this.data[r][nextc]){ //否则,如果当前位置等于下一位置
this.data[r][c] *= 2; //当前位置 = 当前位置值*2
this.score += this.data[r][c]; //增加分数
this.data[r][nextc] = 0; //将下一位置设为0
var div = document.getElementById("c" + r + nextc);
animation.addTask(div,r,nextc,r,c);
}
}
}
},
//找r行c列位置之后,不为0的下一个位置
getNextInRow : function(r,c){
for(var nextc = c + 1; nextc < this.CN; nextc++){ //nextc从c+1开始,遍历r行剩余元素
if(this.data[r][nextc] != 0){ //如果nextc不等于0
return nextc;
}
}
return -1; //循环结束,返回-1
},
//右移所有行
moveRight : function(){
var before = this.data.toString();
for (var r = 0; r < this.RN; r++) { //遍历data中的每一行
this.moveRightInRow(r); //右移当前行
}
var after = this.data.toString();
if(before != after){
animation.state();
}
},
//右移一行,传入要移动的行号
moveRightInRow : function(r){
//c从CN-1开始,到>0结束,每次-1
for (var c = this.CN-1; c > 0 ; c--) {
//找到c之后下一个不为0的值的位置,存在next中
var prevc = this.getPrevInRow(r,c);
if(prevc == -1){
break; //如果prevc等于-1,退出循环
}else{ //否则
if(this.data[r][c] == 0){ //如果当前位置等于0
this.data[r][c] = this.data[r][prevc]; //将当前位置设为下一个位置的值
this.data[r][prevc] = 0; //将下一位置设为0
var div = document.getElementById("c" + r + prevc);
animation.addTask(div, r, prevc, r, c);
c++; //保证下次依然检查当前元素
}else if(this.data[r][c] == this.data[r][prevc]){ //否则,如果当前位置等于下一位置
this.data[r][c] *= 2; //当前位置 = 当前位置值*2
this.score += this.data[r][c]; //增加分数
this.data[r][prevc] = 0; //将下一位置设为0
var div = document.getElementById("c"+r+prevc);
animation.addTask(div,r,prevc,r,c);
}
}
}
},
//找r行c列位置之后,不为0的下一个位置
getPrevInRow : function(r,c){
for(var prevc = c - 1; prevc >= 0; prevc--){ //prevc从c+1开始,遍历r行剩余元素
if(this.data[r][prevc] != 0){ //如果prevc不等于0
return prevc;
}
}
return -1; //循环结束,返回-1
},
//上移所有行
moveUp : function(){
var before = this.data.toString();
for (var c = 0; c < this.CN; c++) { //遍历data中的每一列
this.moveUpInCol(c); //右移当前行
}
var after = this.data.toString();
if(before != after){
animation.start();
}
},
//上移一列,传入要移动的列号
moveUpInCol : function(c){
//r从0开始,遍历当前列中的元素,到<RN-1结束,每次+1
for (var r = 0; r < this.RN-1 ; r++) {
//找到c之后下一个不为0的值的位置,存在next中
var nextr = this.getNextInCol(r,c);
if(nextr == -1){
break; //如果nextr等于-1,退出循环
}else{ //否则
if(this.data[r][c] == 0){ //如果当前位置等于0
this.data[r][c] = this.data[nextr][c]; //将当前位置设为下一个位置的值
this.data[nextr][c] = 0; //将下一位置设为0
var div = document.getElementById("c"+ nextr + c);
animation.addTask(div,nextr,c,r,c);
r--; //保证下次依然检查当前元素
}else if(this.data[r][c] == this.data[nextr][c]){ //否则,如果当前位置等于下一位置
this.data[r][c] *= 2; //当前位置 = 当前位置值*2
this.score += this.data[r][c]; //增加分数
this.data[nextr][c] = 0; //将下一位置设为0
var div = document.getElementById("c"+ nextr + c);
animation.addTask(div,nextr,c,r,c);
}
}
}
},
//找r行c列位置之后,不为0的下一个位置
getNextInCol : function(r,c){
for(var nextr = r + 1; nextr < this.RN; nextr++){ //nextr从c+1开始,遍历c列剩余元素
if(this.data[nextr][c] != 0){ //如果nextr不等于0
return nextr;
}
}
return -1; //循环结束,返回-1
},
//下移所有行
moveDown : function(){
var before = this.data.toString();
for (var c = 0; c < this.CN; c++) { //遍历data中的每一列
this.moveDownInCol(c); //右移当前行
}
var after = this.data.toString();
if(before != after){
animation.start();
}
},
//下移一列,传入要移动的列号
moveDownInCol : function(c){
//r从RN-1开始,遍历当前列中的元素,到>0结束,每次-1
for (var r = this.RN-1; r > 0 ; r--) {
//找到c之后下一个不为0的值的位置,存在next中
var prevr = this.getPrevInCol(r,c);
if(prevr == -1){
break; //如果prevr等于-1,退出循环
}else{ //否则
if(this.data[r][c] == 0){ //如果当前位置等于0
this.data[r][c] = this.data[prevr][c]; //将当前位置设为下一个位置的值
this.data[prevr][c] = 0; //将下一位置设为0
var div = document.getElementById("c" + prevr + c);
animation.addTask(div,prevr,c,r,c);
r++; //保证下次依然检查当前元素
}else if(this.data[r][c] == this.data[prevr][c]){ //否则,如果当前位置等于下一位置
this.data[r][c] *= 2; //当前位置 = 当前位置值*2
this.score += this.data[r][c]; //增加分数
this.data[prevr][c] = 0; //将下一位置设为0
var div = document.getElementById("c" + prevr + c);
animation.addTask(div,prevr,c,r,c);
}
}
}
},
//找r行c列位置之后,不为0的下一个位置
getPrevInCol : function(r,c){
for(var prevr = r - 1; prevr >= 0; prevr--){ //prevr从r-1开始,遍历c列剩余元素
if(this.data[prevr][c] != 0){ //如果prevr不等于0
return prevr;
}
}
return -1; //循环结束,返回-1
}
};
//当窗口加载后
window.onload = function(){
game.start();
/*键盘事件绑定*/
document.onkeydown = function(){
if(game.state == game.running){
var e = window.event || arguments[0];
var code = e.keyCode;
//如果按的是向左箭头,就调用左移方法
//左37 上38 右39 下40
if(code == 37){
game.moveLeft();
}else if(code == 39){
game.moveRight();
}else if(code == 38){
game.moveUp();
}else if(code == 40){
game.moveDown();
}
}
}
}
 
 /*animation.js*/
/*实现上下左右移动时的动画*/ var animation = fucntion(){
DURE : 500; //总时间
STEPS : 50; //总步数
moved : 0; //当前移动步数
timer : null; //保存当前计时器的序号
tasks : []; //放入每次任务需要移动的所有元素和距离 //像tasks数组中增加任务对象
addTask : function(divObj,currC,tarR,tarC){
var topDist = (tarR - currR) * 116;
var leftDist = (tarC - currC) * 116;
var topStep = topDist / this.STEPS;
var leftStep = leftDist / this.STEPS;
this.tasks.push(
{obj:divObj,top:topStep,left:leftStep}
);
},
move : function(){
for (var i = 0; i < this.tasks.length; i++) {
var task = this.tasks[i];
var style = getComputedStyle(task.obj);
task.obj.style.top = parseFloat(style.top) + task.top + "px";
task.obj.style.left = parseFloat(style.left) + task.left + "px";
}
if (--this.moved == 0) {
clearInterval(this.timer);
for (var i = 0; i < this.tasks.length; i++) {
var task = this.tasks[i];
task.obj.style.top = "";
task.obj.style.left = "";
}
this.tasks = [];
game.randomNum();
game.state = game.RUNNING;
game.updateView();
}
},
start : function(){
game.state = game.PLAYING;
var self = this;
self.moved = self.STEPS;
self.timer = setInterval(function(){
self.move();
},self.DURE / self.STEPS);
}
};

JS实现2048的更多相关文章

  1. 用js实现2048小游戏

    用js实现2048小游戏 笔记仓库:https://github.com/nnngu/LearningNotes 1.游戏简介 2048是一款休闲益智类的数字叠加小游戏.(文末给出源代码和演示地址) ...

  2. 使用JS实现2048小游戏

    JS实现2048小游戏源码 效果图: 代码如下,复制即可使用: (适用浏览器:360.FireFox.Chrome.Opera.傲游.搜狗.世界之窗. 不支持Safari.IE8及以下浏览器.) &l ...

  3. js写2048游戏代码

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  4. js实现2048小游戏

    这是学完javascript基础,编写的入门级web小游戏 游戏规则:在玩法规则也非常的简单,一开始方格内会出现2或者4等这两个小数字,玩家只需要上下左右其中一个方向来移动出现的数字,所有的数字就会想 ...

  5. 原生js的2048的制作过程

    1.首先我们来看一下效果图 开始: 结束: 接下来我们来实现代码部分: HTML部分: 2048大家应该都玩过,首先我们要准备16个盒子让它4*4排列,这里的css我就不说了,这应该使我们都会的,在这 ...

  6. JS实现2048代码

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. JS做2048

    首先我们了解一下2048这个游戏的原理: 他由一个4x4二维数组组成,在游戏一开始时候在随机位置随机生成一个2或者4 如: 1.每点击一次开始就刷新一次游戏界面: 2.通过键盘的上下左右四个方向键分别 ...

  8. HTML5小游戏源码收藏

    html5魅族创意的贪食蛇游戏源码下载 html5网页版打砖块小游戏源码下载 html5 3D立体魔方小游戏源码下载 html5网页版飞机躲避游戏源码下载 html5三国人物连连看游戏源码下载 js ...

  9. js、jQuery实现2048小游戏

    2048小游戏 一.游戏简介:  2048是一款休闲益智类的数字叠加小游戏 二. 游戏玩法: 在4*4的16宫格中,您可以选择上.下.左.右四个方向进行操作,数字会按方向移动,相邻的两个数字相同就会合 ...

随机推荐

  1. ubuntu部署supersor

     1.安装pip $ sudo apt-get install python-pip –y $ sudo pip install - -upgrade pip //升级pip 2.安装supervis ...

  2. 浏览器默认标签样式总结及css初始化程序

    html中的大部分的标签都有一些糟糕的样式,有的是标签天然自带的,有的是浏览器默认设置的,我们在写网页时,这些默认的样式就会时不时的跳出来捣一下乱,搞得我们很是无奈.所以成手在写css样式时,一般都会 ...

  3. nodejs中 underscore 包有什么作用

    Underscore一个JavaScript实用库,提供了一整套函数式变成有用的实用功能,但是没有扩展任何JavaScript内置对象.它是这个问题的答案:“如果我在一个空白的HTML页面前坐下, 并 ...

  4. Debian安装Docker

    Debian 安装 Docker CE 准备工作 系统要求 Docker CE 支持以下版本的 Debian 操作系统: Stretch 9 Jessie 8 (LTS) Wheezy 7.7 (LT ...

  5. console (控制台)

    console 模块提供了一个简单的调试控制台,类似于 Web 浏览器提供的 JavaScript 控制台. 该模块导出了两个特定的组件: 一个 Console 类,包含 console.log()  ...

  6. [CentOS7] parted用于磁盘分区(同时支持GPT和MBR分区表)

    声明:本文主要总结自:鸟哥的Linux私房菜-第七章.Linux 磁碟與檔案系統管理,如有侵权,请通知博主 fdisk支持MBR分区表,gdisk支持GPT分区表,而parted支持两者 不知道为什么 ...

  7. 树状数组的神操作QAQ

    卧槽 厉害了,我的树状数组 1.单点修改,单点查询 用差分数组维护 #include<cstdio> #include<iostream> using namespace st ...

  8. 帝都Day6——图论

    //P2O5呢? 一.图的存储: 邻接矩阵:邻接表. 邻接矩阵:n*n的[][],[i][j]节点有边记1没边0 缺点 空间复杂度O(n^2) 占用内存较大(我为什么要把这些东西写到这里呢???) 邻 ...

  9. 帝都Day4(3)——还是数据结构

    可并堆 左偏树中 dist[x]=dist[rs[x]]+1 合并的时候,把权志较大的根作为根节点,把这棵树右子树和另一棵树合并. 说明白点:(上图描述有点问题) 设x表示根权值较大的左偏树,y表示根 ...

  10. shell学习(11)- seq

    今天是五一劳动节,窗户外边,草长莺飞,惠风和畅,但坐在办公室里值班也需要做点事情,今天就写写seq的用法. 作用:用于以指定增量从首数开始打印数字到尾数,即产生从某个数到另外一个数之间的所有整数,并且 ...