一共有16个格子,开局5个随机格子上生成带数字的方块,2或者4
通过控制方向来使原有的方块移动并产生新的方块
新建一个4X4二维数组,存储16个方块的值,
分两个步骤生成第一个和第二个方块
用0-15的随机数代表第几个数组元素上面生成第一个方块块的值(值为2或者4),其余的值为0
具体生成2或者4,设置为随机,其中2的概率高一些,4的概率低一些,比如:
0-10之间,若随机值为0,生成2,否则生成4,比例:10:1
为什么第二个方块(以及其他所有后续的方块)不能使用上述方法?
因为如果两次生成的随机数一样,那么就会出现bug:
同一位置产生两个值
第二个方块如何生成?
先获取16个格子中,空格的个数,也就是二维数组中0的个数
以空格的个数作为循环的次数
移动过程是根据输入的方向来控制方块的移动的
遍历二维数组,找到每一个方块(以左移动为例)
由于第一列无需移动,只从每一行的第二个方块开始判断
(若第一个方块不存在:交换值,并让交换的原位置的值清0
若当前方块的紧邻左方块和当前方块,两者的值相同,那么左边的值变为原来的两倍,当前方块的值清0
若当前方块的紧邻左方块看和当前方块,两者的值不同,不作处理,不移动
快捷键:div#all>div.cell#n0$@0*4+div.cell#n1$@0*4+div.cell#n2$@0*4+div.cell#n3$@0*4
代码:2048.html
<html>
<head>
<meta charset="UTF-8">
<title>2048小游戏title>
<link rel="stylesheet" href="2048.css" />
head>
<body>
<p class="header">SCORE: <span id="score01">span>p>
<div id="all">
<div class="cell" id="n00">div>
<div class="cell" id="n01">div>
<div class="cell" id="n02">div>
<div class="cell" id="n03">div>
<div class="cell" id="n10">div>
<div class="cell" id="n11">div>
<div class="cell" id="n12">div>
<div class="cell" id="n13">div>
<div class="cell" id="n20">div>
<div class="cell" id="n21">div>
<div class="cell" id="n22">div>
<div class="cell" id="n23">div>
<div class="cell" id="n30">div>
<div class="cell" id="n31">div>
<div class="cell" id="n32">div>
<div class="cell" id="n33">div>
div>
<div id="gameover" >
<p>
GAME OVER
<br />
SCORE:<span id="score02">span>
<br />
<a onclick="typeonce()">TYPE AGAINa>
p>
div>
<script type="text/javascript" src="2048.js" >script>
body>
html>
2038.css
*{
font-family: arial;
/* user-select: none;*/
font-weight: bold;
}
.header{
width: 480px;
font-size:40px ;
margin: 60px auto 5px auto;
}
#score01{
color: #F00;
}
#all{
width: 480px;
height: 480px;
background-color: #bbada0;
margin: 0 auto;
border-radius: 20px;
}
.cell{
width: 100px;
height: 100px;
background-color: #ccc0b3;;
border-radius: 10px;
text-align: center;
line-height: 100px;
font-size: 40px;
color: #fff;
float: left;
margin: 16px 0px 0px 16px;
}
.n2{
background-color:#eee3da;
color:#776e65;
}
.n4{
background-color:#ede0c8;
color:#776e65;
}
.n8{
background-color:#f2b179;
}
.n16{
background-color:#f59563;
}
.n32{
background-color:#f67c5f;
}
.n64{
background-color:#f65e3b;
}
.n128{
background-color:#edcf72;
}
.n256{
background-color:#edcc61;
}
.n512{
background-color:#9c0;
}
.n1024{
background-color:#33b5e5;
font-size:40px;
}
.n2048{
background-color:#09c;
font-size:40px;
}
.n4096{
background-color:#a6c;
font-size:40px;
}
.n8192{
background-color:#93c;
font-size:40px;
}
#gameover{
position: absolute;
background-color: rgba(0,0,0,0.2);
left: 0;
top: 0;
right: 0;
bottom: 0;
font-size: 40px;
display: none;
}
#gameover p{
background-color: #fff;
width: 300px;
height: 200px;
border-radius: 10px;
line-height: 66.66px;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
margin-left: -150px;
margin-top: -150px;
}
#gameover p a{
padding: 10px;
text-decoration: none;
background-color: #9f8d77;
color: #fff;
border-radius: 10px;
cursor: pointer;
}
/******思路*******/
/*一.游戏环节:创建game对象*/
//属性:data,score,gamerunning,gameover,status
//普通方法:start,randomNum,dataView,isGameOver
//移动的方法:moveLeft,moveRight,moveUp,moveDown
//移动方法中处理每一行数据的方法:
//moveLeftinRow,moveLeftNum,moveRightinRow,moveRightNum
//moveUpinRow,moveUpNum,moveDowninRow,moveDownNum
// var game={};
/*二.监听鼠标按下事件,并调用game对象中的移动方法*/
//如果监听到上下左右对应的ASCII码,就触发相应的上下左右game移动方法
// document.οnkeydοwn=function(){}
/*三.游戏开始时,调用game对象中的start方法*/
// game.start();
/*四.点击再来一次*/
//隐藏#gameover元素,并跳转回2048.html
// function typeonce(){}
/*一.游戏环节:创建game对象*/
//属性:data,score,gamerunning,gameover,status
//普通方法:start,randomNum,dataView,isGameOver
//移动的方法:moveLeft,moveRight,moveUp,moveDown
//移动方法中处理每一行数据的方法:
//moveLeftinRow,moveLeftNum,moveRightinRow,moveRightNum
//moveUpinRow,moveUpNum,moveDowninRow,moveDownNum
var game={
/*****对象属性*****/
data:[],//二维数组:用于存储2048的值
score:0,//游戏分数:开始时分数为0
gamerunning:1,//游戏开始时:游戏状态为1
gameover:0,//游戏结束时:游戏状态为0
status:1,//个人状态与游戏状态相对应,默认为1
/*****普通对象方法*****/
//1.start方法:游戏开始时还原所有(把game属性还原)
start:function(){
this.data=[//二维数组的值都清零
[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
[0,0,0,0]
];
this.score=0;//分数也清零
this.status=this.gamerunning;//状态码=游戏开始的状态码(也就是1)
// 调用对象方法randomNum,用于获取随机数,调用五次
for(var i=0;i<5;i++){
this.randomNum();
}
// 调用对象方法dataView,用于更新视图
this.dataView();
},
//2.randomNum方法:随机赋值,让随机的数组元素获得2或4作为其值
randomNum:function(){
while(1){//是否方块存在空白位置
var row=Math.floor(Math.random()*4);//获取一个0~3的随机整数,代表第几行
var col=Math.floor(Math.random()*4);//获取一个0~3的随机整数,代表第几列
//判断:如果二维数组data的某个元素的值为0,就让其其值变为2或4
if(this.data[row][col]==0){
var num=Math.random()>.2?2:4;//num的值只能是2或者4,概率分别是80%和20%
this.data[row][col]=num;
break;
}
}
},
//3.dataView方法:更新视图
dataView:function(){
//思路:数组元素的值不为0时,把其值赋给html结构中对应的div中
for(var row=0;row<4;row++){
for(var col=0;col<4;col++){
//拼接成div#n23这样的字符串
var div=document.getElementById("n"+row+col);
if(this.data[row][col]!=0){//数组元素的值不为0
div.innerHTML=this.data[row][col];//2
div.className="cell n"+this.data[row][col];//赋予新类名:n23,即
}else{//数组元素的值为0
div.innerHTML="";//不显示任何内容
div.className="cell";
}
}
}
//更新分数
document.getElementById("score01")=this.score;
//判断游戏是否结束
//游戏结束时弹出gameover框,并显示最终分数
if(this.status==this.gameover){
document.getElementById("gameover").style.display="block";
document.getElementById("score02")=this.score;
}else{
document.getElementById("gameover").style.display="none";
}
},
//4.isGameOver方法:判断游戏是否结束
isGameOver:function(){
//思路:游戏结束就返回一个true,未结束就返回一个return false;
//未结束的几种情况(返回false):1.有空白;2.左右有相同;3.上下有相同
for(var row=0;row<4;row++){
for(var col=0;col<4;col++){
if (this.data[row][col] == 0) { //有空白的时候
return false;
}
if (col < 3) { //判断左右是否有相同,只需要判断到第三个格子即可
if (this.data[row][col] == this.data[row][col + 1]) {
return false;
}
}
if (row < 3) { //判断上下是否有相同,只需要判断到第三个格子即可
if (this.data[row][col] == this.data[row + 1][col]) {
return false;
}
}
}
}
return true;
}
},
/*****移动的对象方法*****/
/*
向左移动的思路:
- 遍历二维数组data,第一列无需移动,只从第二个方块开始
- 若第一个方块不存在:赋值(交换),并让原位置的值清零
- 若当前方块的紧邻左方块和当前方块两者值相同,左方块倍增,右方块清零
- 若当前方块和紧邻左方块和当前方块两者值不同,不作处理
*/
/**1.左移**/
//1.3.moveLeft方法:向左移动
moveLeft:function(){
var before=String(this.data);
for(var row=0;row<4;row++){
this.moveLeftinRow(row);
}
var after=String(this.data);
if(before!=after){
this.randomNum();
if(this.isGameOver()) this.status=this.gameover;
this.dataView();
}
},
//1.2.moveLeftinRow方法:处理向左移动时每一行的数据
moveLeftinRow:function(row){
for(var col=0;col<3;col++){
var nextCol=this.moveLeftNum(row,col);
if(nextCol!=-1){
if(this.data[row][col]==0){
this.data[row][col]=this.data[row][nextCol];
this.data[row][nextCol]=0;
col--;
}else if(this.data[row][col]==this.data[row][nextCol]){
this.data[row][col]*=2;
this.data[row][nextCol]=0;
this.score+=this.data[row][col];
}
}else{
break;
}
}
},
//1.1.moveLeftNum方法:获取符合条件的列数
moveLeftNum:function(row,col){
for(var i=col+1;i<4;i++){
if(this.data[row][i]!=0) return i;
else return -1;
}
},
/**2.右移**/
//2.3.moveRight方法:向右移动
moveRight:function(){
// 移动前:把二维数组data转化成字符串
var before=String(this.data);
// 调用对象方法moveRightinRow:用于处理每一行的函数
for(var row=0;row<4;row++){
this.moveRightinRow(row);
}
// 移动后:把二维数组转化成字符串
var after=String(this.data);
// 判断:移动前后的data是否不一样
if(before!=after){
this.randomNum();//调用对象方法randomNum:生成2或者4的随机数
if(this.isGameOver) this.status=this.gameover;//若游戏结束,状态值为结束状态
this.dataView();//调用对象方法dataView:更新视图
}
},
//2.2.moveRightinRow方法:处理向右移动时每一行的数据
moveRightinRow:function(row){
for(var col=3;col>=0;col--){
// 调用对象方法moveRightNum:获取符合条件的列数i
var nextCol=this.moveRightNum(row,col);//赋值:把符合条件的col赋给nextCol
if(nextCol!=-1){//若有值,说明需要进行左右值交换
if(this.data[row][col]==0){//若当前数组元素的值为0
this.data[row][col]=this.data[row][nextCol];//给当前元素赋值
this.data[row][nextCol]=0;//让下一元素赋值后清零
col++;//再次从最后边的数进行循环
}else if(this.data[row][col]==this.data[row][nextCol]){//若左右元素有相同的值
this.data[row][col]*=2;//当前元素的值倍增
this.data[row][nextCol]=0;//下一元素的值清零
this.score+=this.data[row][col];//分数叠加
}
}else{//若没有值,跳出循环
break;
}
}
},
//2.1.moveRightNum方法:获取符合条件的列数
//形参:row-行数,col-列数
moveRightNum:function(row,col){
//循环并获取前面的数据
for(var i=col-1;i>=0;i--){//最右边一列不移动,从第col-i列开始循环
//判断:前面是否找到数字
if(this.data[row][i]!=0) return i;//返回下标
else return-1;//返回-1
}
},
/**3.上移**/
//3.3.moveUp方法:向上移动
moveUp:function(){
var before=String(this.data);
for(var col=0;col<4;col++){
this.moveUpinCol(col);
}
var after=String(this.data);
if(before!=after){
this.randomNum();
if(this.isGameOver()) this.status=this.gameover;
this.dataView();
}
},
//3.2.moveUpinCol方法:处理所有行的每一列数据
moveUpinCol:function(col){
for(var row=0;row<3;row++){
var nextRow=this.moveUpNum(row,col);
if(nextRow!=-1){
if(this.data[row][col]==0){
this.data[row][col]=this.data[nextRow][col];
this.data[nextRow][col]=0;
row--;
}else if(this.data[row][col]==this.data[nextRow][col]){
this.data[row][col]*=2;
this.data[nextRow][col]=0;
this.score+=this.data[row][col];
}
}else{
break;
}
}
},
//3.1.moveUpNum:获取符合条件的列数
moveUpNum:function(row,col){
for(var i=row+1;i<4;i++){
if(this.data[i][col]!=0) return i;
else return 0;
}
},
/**4.下移**/
//4.3.moveDown方法:向下移动
moveDown:function(){
var before=String(this.data);
for(var col=0;col<4;col++){
this.moveDowninCol(col);
}
var after=String(this.data);
if(before!=after){
this.randomNum();
if(this.isGameOver()) this.status=this.gameover;
this.dataView();
}
},
//4.2.moveDowninCol方法:处理每一列所有行数的数据
moveDowninCol:function(col){
for(var row=3;row>=0;row--){
var nextRow=this.moveDownNum(row,col);
if(nextRow!=-1){
if(this.data[row][col]==0){
this.data[row][col]=this.data[nextRow][col];
this.data[nextRow][col]=0;
row++;
}else if(this.data[row][col]==this.data[nextRow][col]){
this.data[row][col]*=2;
this.data[nextRow][col]=0;
this.score+=this.data[row][col];
}
}else{
break;
}
}
},
//4.1.moveDownNum方法:获取符合条件的行数
moveDownNum:function(row,col){
// 如果当前元素不为空,就返回i
for(var i=row-1;i>=0;i--){
if(this.data[i][col]!=0) return i;
else return -1;
}
}
};
/*二.监听鼠标按下事件,并调用game对象中的移动方法*/
//如果监听到上下左右对应的ASCII码,就触发相应的上下左右game移动方法
document.onkeydown=function(event){
if(event.keyCode==65) game.moveLeft();
if(event.keyCode==87) game.moveUp();
if(event.keyCode==68) game.moveRight();
if(event.keyCode==83) game.moveDown();
}
/*三.游戏开始时,调用game对象中的start方法*/
game.start();
/*四.点击再来一次时*/
//隐藏#gameover元素,并跳转回2048.html
function typeonce(){
document.getElementById("gameover").style.display="none";
window.location.href="2048.html";
}