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. centos7安装与配置ansible

    此次测试总共有三台机,分别如下: ansible服务器:10.0.0.20 client01:10.0.0.21 client02:10.0.0.22 一.安装ansible 1. python版本需 ...

  2. 关于startservice的几个启动返回值的意义

    START_NOT_STICKY 如果服务进程在它启动后(从onStartCommand()返回后)被kill掉, 并且没有新启动的intent传给他, 那么将服务移出启动状态并且不重新生成, 直到再 ...

  3. css中的定位属性position(转)

    css中的定位属性position   同样的也是上课的时候发现学生难以理解的一些问题拿出来记录一下,希望帮助初学者. 在css中定位属性position的运用在页面中是很常用的,特别是一些结合js来 ...

  4. 我的笔记文档版本控制系统-MediaWiki-安装/基本设置

    如果你一直想要一个可以进行版本控制的文档存储工具,那MediaWiki是一个不错的选择.也许,用版本控制来描述MediaWiki有点不妥,但它对于我来说就是如此了.我会将学习笔记都记录在MediaWi ...

  5. iOS开发中,修改ASIHTTPRequest源码,禁止在POST时URL编码

    通过ASIHTTPRequest库进行POST时,会对POST的文本内容进行encodeURL,而且ASIHTTPRequest自身并没有配置项可以关闭这个转换. 本文提供一个方法关闭encodeUR ...

  6. LOJ6053 简单的函数(min_25筛)

    题目链接:LOJ 题目大意:从前有个积性函数 $f$ 满足 $f(1)=1,f(p^k)=p\oplus k$.(异或)求其前 $n$ 项的和对 $10^9+7$ 取模的值. $1\le n\le 1 ...

  7. Java实例练习——基于UDP协议的多客户端通信

    昨天学习了UDP协议通信,然后就想着做一个基于UDP的多客户端通信(一对多),但是半天没做出来,今天早上在参考了很多代码以后,修改了自己的代码,然后运行成功,在这里分享以下代码,也说一下自己的认识误区 ...

  8. 洛谷P2037 电话号码

    P2037 电话号码 题目描述 一串由长长的数字组成的电话号码通常很难记忆.为了方便记忆,有种方法是用单词来方便记忆.例如用“Three Tens”来记忆电话3-10-10-10. 电话号码的标准形式 ...

  9. bzoj3720: Gty的妹子树(树分块)

    传送门 好珂怕…… 树分块是什么东西啊……感觉好暴力…… 直接贴一下好了->这里 //minamoto #include<iostream> #include<cstdio&g ...

  10. 今天来记录一下关于ajax跨域的一些问题。以备不时之需。

    今天来记录一下关于ajax跨域的一些问题.以备不时之需. 跨域 同源策略限制 同源策略阻止从一个域上加载的脚本获取或操作另一个域上的文档属性.也就是说,受到请求的 URL 的域必须与当前 Web 页面 ...