JS实现2048
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的更多相关文章
- 用js实现2048小游戏
用js实现2048小游戏 笔记仓库:https://github.com/nnngu/LearningNotes 1.游戏简介 2048是一款休闲益智类的数字叠加小游戏.(文末给出源代码和演示地址) ...
- 使用JS实现2048小游戏
JS实现2048小游戏源码 效果图: 代码如下,复制即可使用: (适用浏览器:360.FireFox.Chrome.Opera.傲游.搜狗.世界之窗. 不支持Safari.IE8及以下浏览器.) &l ...
- js写2048游戏代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
- js实现2048小游戏
这是学完javascript基础,编写的入门级web小游戏 游戏规则:在玩法规则也非常的简单,一开始方格内会出现2或者4等这两个小数字,玩家只需要上下左右其中一个方向来移动出现的数字,所有的数字就会想 ...
- 原生js的2048的制作过程
1.首先我们来看一下效果图 开始: 结束: 接下来我们来实现代码部分: HTML部分: 2048大家应该都玩过,首先我们要准备16个盒子让它4*4排列,这里的css我就不说了,这应该使我们都会的,在这 ...
- JS实现2048代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- JS做2048
首先我们了解一下2048这个游戏的原理: 他由一个4x4二维数组组成,在游戏一开始时候在随机位置随机生成一个2或者4 如: 1.每点击一次开始就刷新一次游戏界面: 2.通过键盘的上下左右四个方向键分别 ...
- HTML5小游戏源码收藏
html5魅族创意的贪食蛇游戏源码下载 html5网页版打砖块小游戏源码下载 html5 3D立体魔方小游戏源码下载 html5网页版飞机躲避游戏源码下载 html5三国人物连连看游戏源码下载 js ...
- js、jQuery实现2048小游戏
2048小游戏 一.游戏简介: 2048是一款休闲益智类的数字叠加小游戏 二. 游戏玩法: 在4*4的16宫格中,您可以选择上.下.左.右四个方向进行操作,数字会按方向移动,相邻的两个数字相同就会合 ...
随机推荐
- Servlet编程实例 续2
-----------------siwuxie095 Servlet 跳转之请求的重定向 继续完善登录实例,如下: login.jsp 不变,修改 LoginServlet,新建两个 JSP 文件 ...
- VCF文件处理工具PyVCF
vcf格式示例 ##fileformat=VCFv4.1 ##FILTER=<ID=LowQual,Description=”Low quality”> ##FORMAT=<ID=A ...
- 8、scala函数式编程
一.函数式编程1 1.介绍 Scala中的函数是Java中完全没有的概念.因为Java是完全面向对象的编程语言,没有任何面向过程编程语言的特性,因此Java中的一等公民是类和对象, 而且只有方法的概念 ...
- 14.Nginx 文件名逻辑漏洞(CVE-2013-4547)
由于博主在渗透网站时发现现在Nginx搭建的网站是越来越多 所以对Nginx的漏洞来一个全面性的复习,本次从Nginx较早的漏洞开始分析. 2013年底,nginx再次爆出漏洞(CVE-2013-45 ...
- Django 的认证系统
Django自带的用户认证 auth 模块 from django.contrib import autu django.contrib.auth 中提供了许多方法, 这里主要介绍其中三个: auth ...
- hdu1050
#include <cstdio> #include <algorithm> using namespace std; #define SIZE 205 struct Data ...
- bzoj 4974: [Lydsy八月月赛]字符串大师
4974: [Lydsy八月月赛]字符串大师 Time Limit: 1 Sec Memory Limit: 256 MBSubmit: 371 Solved: 190[Submit][Statu ...
- thinkphp5.1控制器初始化函数initialize与构造函数__construct区别
构造函数中子类的构造方法会覆盖父类的构造方法,如果要继承父类的构造方法可以加入parent::__construct(); 例子: //另一种方法,使用构造函数初始化 public function ...
- EIGRP-3-EIGRP的多参数度量
带宽度量参数本身无法区分10Gbit/s及更高速率的接口.对1Gbit/s接口,默认延迟度量参数已设置为最低值1(10微妙).而且EIGRP承载的是经过换算的参数,每台路由器需要将其换算回再计算新开销 ...
- Unity 行为树-节点间数据传递
一.引言 有以下小场景: 节点A:发现了 敌人. 节点B:追逐敌人. 对于同一个敌人物体,节点AB之间是如何传递数据 的呢? 二.数据传递的3种方式 1.共享变量:面板中创建局部或者全局的共享变量Te ...