游戏规则## 标题
每次控制所有方块向同一个方向运动,两个相同数字的方块撞在一起之后合并成为他们的和,每次操作之后会在空白的方格处随机生成一个2或者4,最终得到一个“2048”的方块就算胜利了。如果16个格子全部填满并且相邻的格子都不相同也就是无法移动的话,那么恭喜你gameover。
*上面似乎也很多细节没有提到(这个给用户看足够了,对开发者来说还有一些小细节)
比方说3个方块2,2,4,0要连续合并吗?最终结果是0,0,0,8?还是0,0,4,4(我们假定不连续合并,因为这样用户决策空间更大)
每次是只要有效滑动就生成新的方块吗?还是得发生碰撞消除才产生新的方块?(我假定有效滑动就生成新的方块)[不是按键就生成,有效滑动是指至少有一个方块移动了位置]
积分规则?假设产生合并出一个数值为n的方格则积分+n。(假设)
界面
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>2048</title>
<link rel="stylesheet" type="text/css" href="./css/style.css">
<script type="text/javascript" src="./js/jquery.js"></script>
<script type="text/javascript" src="./js/init.js"></script>
<script type="text/javascript" src="./js/active.js"></script>
</head>
<body>
<header>
<h1>2048</h1>
<input type="button" value="New Game" id="start" class="btn">
<form>
<label>score:<input type="text" value="0"></label>
</form>
</header>
<section id="container">
<table class="board">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</section>
</body>
</html>
style.css - 页面样式
注意:
table{
...
position: relative;
}
td p{
...
position: absolute;
}
这里是为了后面JS实现的滑动动画效果(删去不会影响页面布局,但会影响后面的JS动画(失效)
这一部分代码是为了让中间的方块刚好填到对应位置
table{
/*这里很重要*/
border-spacing: 0;
}
td{
width: 100px;
height: 100px;
padding: 0;
}
td p{
margin: 0;
padding: 0;
width: 100px;
height: 100px;
}
完整代码
body{
margin: 0 auto;
width: 100%;
text-align: center;
}
header>input{
background-color:#605546;
font-size: 20px;
color: white;
}
header>h1{
margin: 10px 0px;
}
header form{
margin-left: 0px;
}
header label{
font-size: 25px;
}
header label>input{
text-align: center;
border: 0;
width: 60px;
font-size: 25px;
}
.btn{
display: inline-block;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42;
border: 1px solid transparent;
border-radius: 8px;
}
table{
margin: 0 auto;
background-color:#bbada0;
border-radius: 10px;
position: relative;
border-spacing: 0;
}
td{
width: 100px;
height: 100px;
border-radius: 10px;
float: left;
background-color: #ccc0b3;
text-align: center;
font-size: 30px;
margin: 10px;
padding: 0;
}
td p{
margin: 0;
padding: 0;
width: 100px;
height: 100px;
line-height: 90px;
border-radius: 10px;
position: absolute;
}
init.js - 初始化脚本
由于我们假设每次移动不能连续合并所以记一个has_conflicted数组标记当前位置方块是否是本次移动中已经合并过了。
var board = new Array(),
score = 0,
has_conflicted = new Array(),
successString = "Success",
gameOverString = "GameOver";
$(newGame); // 加载完成之后开始游戏
$(document).ready(function(){
$("#start").click(function(){
newGame();
});
})
function newGame(){
for(var i=0;i<4;i++){
board[i] = new Array();
has_conflicted[i] = new Array();
for(var j=0;j<4;j++){
board[i][j] = 0;
has_conflicted[i][j] = false;
}
}
updateBoardView(); //更新棋盘
score = 0;
updateScore(score);//更新分数
//产生两个方格
generate();
generate();
}
function generate(){
if(isNoSpace())return false;
var empty = new Array();
for(var i=0;i<4;i++){
for(var j=0;j<4;j++)if(!board[i][j]){
empty.push(i*4+j);
}
}
var randNum = empty[Math.floor(Math.random()*empty.length)];
var x = parseInt(randNum/4),y = parseInt(randNum%4);
board[x][y] = Math.random()>0.5?4:2;
showNewNum(x,y,board[x][y]);
return true;
}
function showNewNum(i,j,num){
var item = $("tr:eq("+i+")").children("td:eq("+j+")").append("<p>"+num+"</p>").children("p");
item.css("background-color",getBackgroundColor(num));
item.css("color",getColor(num));
item.fadeIn("normal");
}
function updateBoardView(){
$("td").empty();
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
if(board[i][j]){
var item = $("tr:eq("+i+")").children("td:eq("+j+")").append("<p>"+board[i][j]+"</p>").children("p");
item.css("background-color",getBackgroundColor(board[i][j]));
item.css("color",getColor(board[i][j]));
item.show();
}
has_conflicted[i][j] = false;
}
}
}
function updateScore(){
$("label>input").attr("value",""+score);
}
function isNoSpace(){
for(var i=0;i<4;i++){
for(var j=0;j<4;j++)
if(!board[i][j])return false;
}
return true;
}
function getColor(num){
if(num>4){
return "snow";
}
else{
return "#776e65";
}
}
function getBackgroundColor(num){
switch(num){
case 2:return "#eee4da";break;
case 4:return "#eee0c8";break;
case 8: return '#f2b179'; break;
case 16: return '#f59563'; break;
case 32: return '#f67c5f'; break;
case 64: return '#f65e3b'; break;
case 128: return '#edcf72'; break;
case 256: return '#edcc61'; break;
case 512: return '#9c0'; break;
case 1024: return '#33b5e5'; break;
case 2048: return '#09c'; break;
}
return "black";
}
active.js - 游戏事件
$(document).keydown这里捕获了按键事件,拦截默认行为(只放行了F5便于测试)
var flag = true;
$(document).keydown(function(e){
if($("label>input").text()==successString){newGame(); return;}
e.preventDefault();
if(e.keyCode == 116)window.location.href = window.location.href;//恢复F5便于测试
if(move(e.keyCode)){
setTimeout("generate()",200);
setTimeout("checkGameStatus()",400);
}
});
function move(dir){
if(!isCanMove(dir))return false;
var a,b,c,d;//start,end,step,enum
if(dir==39||dir==40)a=2,b=-1,c=-1,d=3;//→/↓
else a=1,b=4,c=1,d=0;//←/↑
for(var i=0;i<4;i++){//行or列(对下面第一组case是行对第二组是列)
for(var j=a;j!=b;j+=c)switch(dir){
case 37:case 39://←/→
if(board[i][j])for(var k=d;k!=j;k+=c)if(isNoBlockBtw(i,j,i,k)){
if(!board[i][k]){
showMoveAnimation(i,j,i,k);
board[i][k]=board[i][j],board[i][j]=0;
break;
}else if(board[i][k]==board[i][j]&&!has_conflicted[i][k]){
showMoveAnimation(i,j,i,k);
board[i][k]+=board[i][j],board[i][j]=0;
score+=board[i][k];
updateScore(score);
has_conflicted[i][k] = true;
break;
}
}
break;
case 38:case 40://↑/↓
if(board[j][i])for(var k=d;k!=j;k+=c)if(isNoBlockBtw(j,i,k,i)){
if(!board[k][i]){
showMoveAnimation(j,i,k,i);
board[k][i]=board[j][i],board[j][i]=0;
break;
}else if(board[k][i]==board[j][i]&&!has_conflicted[k][i]){
showMoveAnimation(j,i,k,i);
board[k][i]+=board[j][i],board[j][i]=0;
score+=board[k][i];
updateScore(score);
has_conflicted[k][i] = true;
break;
}
}
break;
}
}
setTimeout("updateBoardView()",200);
return true;
}
function isNoBlockBtw(x1,y1,x2,y2){
if(x1 == x2){//row
if(y1>y2){var tmp = y1;y1 = y2; y2 = tmp;}
for(var i=y1+1;i<y2;i++)if(board[x1][i])return false;
}else{//col
if(x1>x2){var tmp = x1;x1 = x2; x2 = tmp;}
for(var i=x1+1;i<x2;i++)if(board[i][y1])return false;
}
return true;
}
function showMoveAnimation(fromX,fromY,toX,toY){
var item = $("tr:eq("+fromX+")").children("td:eq("+fromY+")").children("p");
item.animate({top:(toX*(100+20)+12)+"px",left:(toY*(100+20)+12)+"px"},100);
}
function isCanMove(dir){
var a,b,c;
if(dir==39||dir==40)a=2,b=-1,c=-1;else a=1,b=4,c=1;
for(var i=0;i<4;i++)for(var j=a;j!=b;j+=c){
switch(dir){
case 37:case 39:
if(board[i][j]&&(board[i][j-c]==0||board[i][j]==board[i][j-c]))return true;
break;
case 38:case 40:
if(board[j][i]&&(board[j-c][i]==0||board[j][i]==board[j-c][i]))return true;
break;
}
}
return false;
}
function checkGameStatus(){
for(var i=0;i<4;i++)for(var j=0;j<4;j++)if(board[i][j]==2048){
alert(successString); newGame(); return;
}
if(isNoMove())gameOver();
}
function gameOver(){
alert(gameOverString+"\n你的分数是:"+$("label>input").val()); newGame();
}
function isNoMove(){
if(isCanMove(37)||isCanMove(38)||isCanMove(39)||isCanMove(40))return false;
else return true;
}
Notice
1.<button>
标签定义一个按钮。在 button 元素内部,您可以放置内容,比如文本或图像。这是该元素与使用 input 元素创建的按钮之间的不同之处。<button>
控件 与 <input type="button">
相比,提供了更为强大的功能和更丰富的内容。<button>
与 </button>
标签之间的所有内容都是按钮的内容,其中包括任何可接受的正文内容,比如文本或多媒体内容。例如,我们可以在按钮中包括一个图像和相关的文本,用它们在按钮中创建一个吸引人的标记图像。唯一禁止使用的元素是图像映射,因为它对鼠标和键盘敏感的动作会干扰表单按钮的行为。
请始终为按钮规定 type 属性。Internet Explorer 的默认类型是 “button”,而其他浏览器中(包括 W3C 规范)的默认值是 “submit”。
如果在 HTML 表单中使用 button 元素,不同的浏览器会提交不同的值。Internet Explorer 将提交<button>
与 </button>
之间的文本,而其他浏览器将提交 value 属性的内容。请在 HTML 表单中使用 input 元素来创建按钮。 (Ref.1~2)
2.jQuery $()函数传入回调函数时在document对象上绑定一个ready事件监听函数,当DOM结构加载完成的时候执行。(Ref.3)
3.$(…).empty() 删除匹配的元素集合中所有的子节点。
4.$("tr:eq("+i+")").children("td:eq("+j+")")
这里是选中第i行第j列的单元格(Ref.5)
Reference
1.https://www.w3school.com.cn/tags/tag_button.asp
2.https://www.w3school.com.cn/tags/tag_input.asp
3.https://blog.csdn.net/qq_28775437/article/details/80321711
4.https://www.runoob.com/jquery/jquery-tutorial.html
5.https://www.w3school.com.cn/jquery/traversing_eq.asp
6.https://www.cnblogs.com/daysme/p/6272570.html
7.https://www.w3school.com.cn/cssref/pr_tab_border-spacing.asp