百行 HTML5 代码实现四种双人对弈游戏

标签: IT技术 HTML5 五子棋 围棋 游戏 | 发表时间:2012-06-30 22:32 | 作者:童海波
出处:http://blog.jobbole.com

简介: 本文是一个非常具有挑战性的编程,因为 100 行代码,约莫 10000 个字符左右,将实现围棋、五子棋、四子棋和翻转棋四种双人对弈游戏。请注意,这四个对弈游戏不是初级编程者的习作,而是有着棋盘、立体棋子、事件、走棋规则判断、输赢判断的完整对弈游戏,并且可以离线存储到 iPad、Android 平板中,试想一下,把这种游戏下载到平板中,就可以在火车,旅游景区,等没有信号的地方进行对弈,是不是扩展了平板电脑的功能,是不是一种很惬意的事情。而且,关键是,这个程序没有图片,不需要去应用商店付费下载,仅仅是用 HTML5 技术写的 100 行代码而已,绝对是目前最迷您精悍的双人对弈游戏源码。 (编者注:由于网页代码的宽度有限制,所以作者的源代码经过了一些换行处理,特此说明。)

目标

要做一个完整的双人对弈游戏,至少要做如下事情,第一步:绘制棋盘。不同的棋类游戏棋盘不同,这一点需要进行动态处理;第二步:绘制棋子。需要说明的是,围棋,五子棋等这些棋子都是圆的啊,请不要为了图片苦恼,在 HTML5 时代,我们用代码就可以实现立体圆形棋子;第三步:判断落子事件。当然是要定位手指的点击位置,这四种棋中,有的是落在框里面的,有的却是落在纵横交错的棋盘十字线上,需要动态处理;第四步:判断落子规则。下棋都有规则,不要因为代码少,就将规则打折扣,否则程序不成熟,会变成小朋友的玩具了;第五步:判断输赢。最后,我们要判断输赢。也就是要数子,这个事情必须由程序来完成,因为下棋总得需要一个裁判嘛;第六步:就是平板电脑时代,我们得实现离线应用。这个太重要了,否则,要是在台式电脑上,接根网线玩的游戏,已经遍地都是了,您写得再牛,有什么用?就是要移动,在没有信号的地方,才有市场,现在平板,智能手机这么多,在没有网络信号的地方,掏出移动设备来下棋,才是一件很牛的事情。

绘制棋盘

前面说了围棋、五子棋、四子棋和翻转棋的棋盘并不相同,围棋是纵横 18 个格,其他三种棋则是 8 个格。所以绘制棋盘是需要有参数。这是个小问题,大问题是,选择什么方法来绘制棋盘?

HTML5 框架下,有至少 3 种方法:第一种,用 Canvas 画线;第二种,用 DIV,CSS3 里面增加了行列属性;第三种,用 table 标签。

用哪一种速度最快,代码少呢?答案是:第三种。多少有点失望啊,HTML5 不是万能的。详细代码如下:

this.board=function(name,width,height,rowBak,colBak){ /* 画棋盘 */
 nameBak=name;
 if("turnover"==name){row=8;col=8;}else if("gogame"==name){row=18;col=18;}
 var aW=Math.floor(width/(col+2)),aH=Math.floor(height/(row+2));
 minL=(aW>aH?aH:aW)-4;// 这个减法很重要,否则填空时会把表格撑大
 var array=new Array("<div style=\"margin:"+minL+"px;\"> "+
 "<table border=1 cellspacing=0 width=\""+(aW*col)+"\"
 height=\""+(aH*row)+"\">");
 for(var i=0;i<row;i++){
       array.push("<tr>");
       for(var j=0;j<col;j++){array.push("<td align=center>"+
 evt(i,j,minL,minL,aW*j+minL/2+8,aH*i+minL/2)+"</td>");}
       if(nameBak!="four"&&nameBak!="turnover")/* 将事件添加到表格中 */
             array.push(evt(i,col,minL,minL,aW*col+minL/2+8,aH*i+minL/2));
             array.push("</tr>");
		 }
	   if(nameBak!="four"&&nameBak!="turnover"){
           for(var j=0;j<=col;j++){
               array.push(evt(row,j,minL,minL,aW*j+minL/2+8,aH*row+minL/2));
               }
           }
 document.write(array.join("")+"</table></div>");
 setClick(row,col,minL,minL);/* 初始化事件 */
 start();/* 初始化棋子 */
	 }

上面代码中,最重要的是标黑体的第 6 行代码,这里面有两个诀窍,第一个就是 table 的定义,第二个就是使用了 Array 数组。为什么要使用数组,而不是定义一个字符串呢?答案是优化,就是 Array 数组的 push 方法的速度要远远快于 String 字符串的加 + 运算。共计 16 行代码,一个棋盘就画好了,当然这其中不仅仅是画线,还有棋子处理,事件定义等方法的调用,后面将陆续谈到。

绘制棋子

绘制完棋盘,我们来绘制棋子。我们挑选的这四种棋,虽然棋盘不同,但是棋子都是相同的,都是黑白棋子。这在以前,做在线对弈,除了 Flash 能实现美观效果外,其他的必须先请美工做几副小图片,HTML5 时代,美工的人力和沟通成本就节省了。

我们至少有两种方法绘制棋子,第一种是:canvas 类,第二种就是 css 的圆角属性。用哪种速度又快代码又少呢?答案是第二种,圆角。代码如下:

function man(width,height,id,colorBak){ /* 画棋子 */
   var color=colorBak==null?(order++%2==0?"000":"CCC"):colorBak;
   var r="border-radius:"+width/2+"px;";
   var obj=id==null?event.srcElement:_$(id);
   obj.innerHTML="<div id=\"man_"+color+"_"+order+"\" style=\"display:block;-webkit-"
   +r+"-moz-"+r+""+r+"-moz-box-shadow:inset 0 -10px 40px rgba(0,0,0,1);"+
   "box-shadow:inset 0 -10px 40px rgba(0,0,0,1);"+
   "background:-webkit-gradient(radial, 50 40, 30, center center, 80, from(#"+color+"),
      to(rgba(255,255,255,1)));"+
   "width:"+width+"px;height:"+height+"px;\"></div>";
	 }

上面代码中,我们看到,我们将每一个棋子定义了一个 DIV,使用了 CSS3 的 shadow,gradient 属性,并且可以根据棋盘的大小自动计算棋子的大小,另外,如果用户不喜欢黑白颜色,甚至可以定义成红黄颜色,女生和小朋友估计会喜欢。这 5 行代码是画一个棋子的方法,做一个简单的循环,就可以画出多个棋子,方法如下。

function moreMan(array){for(var i=0;i<array.length;i++)
man(minL,minL,nameBak+"_"+array[i]);}
/* 绘制多个棋子 */

处理事件

绘制完棋盘和棋子,我们来分析一下用户的动作。用户的动作无非就是两种,一种是点击棋盘 table,另外一种就是点击棋子 DIV。难点在点击 table 这里,我们要获知用户点击 table 的位置。

传统思路可能是这样,使用 event 方法,获得 x,y 的坐标,然后与 table 的左上角做减法,然后再跟单元格 cell 做除法。听起来都麻烦。

如果您仔细阅读了前面的代码,就应该发现,其实在画棋盘是,我们向 array 数组中 push 了一个 evt 方法,很明显,这个 evt 方法要返回一个字符串变量的,那么他的内容是什么呢?答案揭晓:

function evt(i,j,width,height,left,top){ /* 单一单元格事件 */
  return "<div id=\""+nameBak+"_"+i+"_"+j+"\" style=\"position:"+
 (nameBak=="four"||nameBak=="turnover"?"block":"absolute")+
 ";border:0px solid #000;width:"+
 width+"px;height:"+height+"px;top:"+top+"px;left:"+left+"px;\"></div>";
	 }

原理是一个 DIV。对了,这个添加事件的方法非常特殊,实际上是在每个棋盘的交叉的地方画了一个 DIV,然后给 DIV 添加事件。

function setClick(row,col,width,height){
	    for(var i=0;i<=row;i++){
            for(var j=0;j<=col;j++){
                var els=_$(nameBak+"_"+i+"_"+j);
                if(els!=null)els.onclick=function(){if(rule())man(width,height);};
			 }
	    }
	 }

需要说明的是,DIV 一定要先定义,即 document.write 输出出来,然后才能执行 onclick 的定义,否则会返回 DIV 未定义的错误。寥寥 10 行代码,把事件问题搞定了。

落子规则

前面说了,用户点击事件有两种,点击棋盘 table 事件我们采用额外增加 DIV 的方法巧妙解决了,第二种点击棋子的方法又该如何呢?

先要说明的是,点击棋子其实是一种错误的事件,点击棋盘可以落子,点击棋子是什么意思?黑白棋点击棋子是无意义的,我们必须要进行判断,不能在有子的地方落子,这是规则之一。所以必须要定义一个方法,判断是不是点击的地方是不是有棋子。代码如下:

function isMan(row,col){var obj=_$(nameBak+"_"+row+"_"+col,1);
if(obj==null||obj.indexOf("man_")==-1)return null;
else if(obj.indexOf("000")!=-1)
  return 0;
else if(obj.indexOf("CCC")!=-1)return 1;}

想不到吧,其实只要一行代码就可以就可以做是否有子的判断,怎么判断的,诀窍就在于判断 DIV 的颜色,棋子要么黑,返回 0,要么白,返回 1,但是空白地方是没有颜色的,返回 null。这里要特别注意返回值,后面判断输赢的时候还要用,所以不能简单通过 true 或者 false 的的返回值来判断是否有子,而是要判断出有什么颜色的子。

对于五子棋和围棋,这一条规则够用了,但是对于翻转棋和四子棋,还有第二条规则:不能在四周空白的地方落子,就是说必须是相连的。也就是说,不仅仅要判断点击的地方是不是有棋子,还要判断其四周是不是有棋子,这个,不是可以有,而是,必须有。需要做一个小循环啊,代码如下:

function rule(){/* 走棋规则 */
 var id=event.srcElement.id;
 if(id.indexOf("man_")==0){alert("不能在有子的地方落子");return false;}else{
     var p=id.indexOf("_"),p1=id.lastIndexOf("_");
     var row=id.substr(p+1,p1-p-1)*1,col=id.substr(p1+1)*1;
     if("gobang"==nameBak)return gobang(row,col);
        else if("four"==nameBak){
     if(isMan(row,col+1)==null&&isMan(row,col-1)==null&&
     isMan(row+1,col)==null&&
     isMan(row-1,col)==null){
     alert("四子棋不能在四周空白的地方落子!");
     return false;
 }
 return gobang(row,col,3);
 }else if("turnover"==nameBak){
 if(isMan(row,col+1)==null&&isMan(row,col-1)==null&&
 isMan(row+1,col)==null&&isMan(row-1,col)==null&&
 isMan(row-1,col-1)==null&&
 isMan(row+1,col+1)==null){
 alert("翻转棋不能在四周空白的地方落子!");
 return false;
 }
  turnover();
 }else if("gogame"==nameBak){
     }
     }
  return true;
 }

循环中,反复调用 isMan 方法判断是否有棋子,所以如果 isMan 写得不够简练,快速,不知道要耗费多少时间啊。数一数,总共 19 行代码就处理了落子规则。

到这里,我们绘制了棋盘,棋子,获得了点击时间,判断了落子规则,才用了 40 行左右的代码,其实程序基本上可用了,但是我们不能满足啊,还得让他更加智能一些,我们还需要一个裁判断输赢。

判断输赢

要判断输赢,我们必须要知道下棋的规则:

五子棋是各个方向的五子相连算赢,四子棋是各个方向四个子相连算赢,翻转棋数棋子的个数,围棋则要麻烦些,不仅仅数棋子个数,还要数围住的区域。

逻辑上好像很复杂啊,似乎也是计算最多的地方,有点人工智能的意思。没错,如果前面的基础打得不好,这里的确要耗费很多代码,但是因为我们前面定义了 DIV 用颜色判断是否存在棋子的 iaMan 方法,这里再使用一个小技巧,就可以轻松搞定这个输赢判断。先看看五子棋和四子棋的输赢判断代码,然后对照代码来分析。

function gobang(row,col,num){
 num=num==null?4:num;
 var rs=[[],[],[],[]],b=[],w=[];/* 这里采用四维数组来存储棋子位置 */
 for(var i=0,j=0;i<num*2+1;i++,j++){
 rs[0].push(isMan(row-num+i,col));
 rs[1].push(isMan(row,col-num+j));
 rs[2].push(isMan(row-num+i,col-num+j));
 rs[3].push(isMan(row-num+i,col-num+j));
 if(i<num){b.push(0);w.push(1);}
		 }
 if(rs.join("#").indexOf(b.join(","))!=-1){alert("黑棋胜");return false;
 }else if(rs.join("#").indexOf(w.join(","))!=-1){alert("白棋胜");return false;}
     return true;
	 }

共计 9 行代码就搞定,看懂没?首先定义了一个 Javascript 多维数组 rs=[[],[],[],[]],这种定义多维数组的方法,挑出来重点说明一下,因为搜索引擎上都是搜不到的,我讲课时差不多遇到的学生也都不清楚,他们大多采用 new Array,然后加循环的蜗牛方法。

第二步:从落子的地方开始循环,注意,不是循环整个棋盘,为的就是节省时间啊。循环设计纵横交叉四个方向,有棋子的地方,就向这个四维数组 push 棋子的颜色。

第三步:把数组 join 起来就 ok 啦,如果有 4 个或 5 个 1 相连,自然就是白棋胜,否则就是黑棋胜。

写道这里,就有点意思啦,注意我们处理的数据的方法,我称之为“块数据”的处理方法,就是充分利用 array 数组,保存一块一块的数据,无论写入,读取,还是统计分析,都是针对这一块数据进行,这样既可以提高内聚度,便于提炼出可以重用的方法,就可以大大的加快执行速度。

处理相连都不在话下,数子就更简单了,使用块数据处理方法,3 行搞定。

function turnover(){
    if(order<64)return;
    var num=0;var total=row*col;for(var i=0;i<row;i++){
        for(var j=0;j<col;j++){num+=isMan(i+"_"+j);}
    }
 if(num<total/2)alert("黑棋胜"+(total-num*2)+"子");
 else if(num>row*col/2)alert("白棋胜"+(num*2-total)+"子");
 else alert("平局");
	 }

棋子初始化

环环相扣地写到这里,还有最后一个关于棋子的问题需要处理。那就是,下五子棋是从空白棋盘开始,其他三种棋却一开始都是有子的。其实给一个空白棋盘也行,但是其他三种棋因为一般的前几步走法都是固定的,我们为了提高智能化程度,不得不在浪费四行代码,毕竟,我们的目标是一个市场化的产品,而不是一个初学者不考虑用户体验的程序。

 function start(){
   if("turnover"==nameBak){moreMan([3+"_"+3,4+"_"+3,4+"_"+4,3+"_"+4]);
   }else if("four"==nameBak){man(minL,minL,nameBak+"_"+row/2+"_"+0);
   }else if("gogame"==nameBak){moreMan([3+"_"+3,15+"_"+3,15+"_"+15,3+"_"+15]);
   }
	 }

其实就是调用了一下 moreMan 方法,注意也是块数据引用,传输了一个数组,用下划线分割横向和纵向坐标。

做成离线应用

本文开头就说过,台式电脑的双人或多人对弈程序早已多如牛毛烂大街了,只有移动应用才能有市场,我们的目标就是奔着这个来的,所以最后必须做成离线应用。

如何实现 HTML5 的离线应用,搜索引擎很快能找到结果,其实只要三个关键步骤。

第一步;在 Web 服务器的配置文件中声明一下。Tomcat 和 Apache 的声明方式不相同,需要注意;

第二步:定义 manifest 文件,文件格式需要注意;

第三步:在 HTML 的文件中调用一下 manifest 文件。

根据这三个步骤,读者可以自行搜索细节,这里就不赘述了,我只讲搜索引擎搜不到的。

另外需要说明的是,iPad 和 Android 平板上 浏览器实现全屏的方法也不一样,针对 iPad 用户,我们还必须定义一行能够实现全屏的代码。

9. 效果图、在线演示、开放源代码

本文的在线演示网址是: http://www.chofo.com/chess.htm,效果图如下图所示:

图 1. 效果图


百行 HTML5 代码实现四种双人对弈游戏

图中加了一个选择棋类型和设置背景功能,如要获得全部源代码,只要使用浏览器的查看源代码功能即可,限于篇幅,这里就不贴了。

总结

作为一个 程序员,最高的境界不是写得代码越多越好,而是用最少的代码实现最多的计算,解决最多的问题。回想当年,盖茨在编写 Basic 时,为了节省几个字符需要绞尽脑汁通宵达旦,以至于遗留了千年虫世纪难题,反观今日,在云计算时代,随着硬盘和内存的容量越来越大,CPU 的运算越来越快,很多大型项目的程序员似乎失去了精简代码的习惯。但是移动计算的硬件,目前还没有那么高的配置,本文通过 HTML5 对弈游戏,使用“块数据”计算方法,实现了用最少代码实现最多计算的目标,特别适用移动计算,与大家共勉。

参考资料

学习

● 在线演示地址: http://www.chofo.com/chess.htm

● “ 实现 HTML5 和 CSS3 的跨浏览器功能”(developerWorks,2012 年 2 月):HTML5 和 CSS3 有许多杰出的新特性,比如能够离线储存数据和创建无图像圆角效果。然而并不是所有这些新特性都能够跨浏览器使用。通过本文学习一些能够在所有主要浏览器的最新版本上使用的 HTML5 和 CSS3 技巧,这些浏览器包括 Safari、Internet Explorer、Firefox 和 Chrome。

● “ 拖拽:从 Dojo 到 HTML5”(developerWorks,2011 年 2 月):拖拽是 Web 2.0 应用中最流行的技术之一。本文将介绍如何在网络应用程序中使用 dojo 和 HTML5 这两种技术的拖拽功能。并将通过示例详细介绍 HTML5 的拖拽功能。

● “ HTML5 基础知识,第 1 部分”(developerWorks,2012 年 6 月):HTML5 代表了 WebSphere Application Server 业务和云业务在实现方式上的里程碑式改变。本文是 4 部分系列文章的第 1 部分,该系列旨在介绍 HTML5 的演变,文本首先介绍 HTML5 中新增加的标记和页面结构,提供有关 WebSphere Application Server 页面设计的高级信息、表单的创建、API 的使用和价值、以及 Canvas 提供的种种创新可能。

● “ 基于 HTML5 的 Dojo Widget 开发”(developerWorks,2011 年 7 月):本文主要介绍基于 HTML5 来扩展 Dojo Widget,它不仅具有强大的 JavaScript 逻辑控制,而且具有丰富的页面展现和良好的运行性能。

● developerWorks Web development 专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。

● developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。

● developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过  Web 2.0 新手入门栏目,迅速了解 Web 2.0 的相关概念。

● 查看  HTML5 专题,了解更多和 HTML5 相关的知识和动向。

相关文章

相关 [html5 代码 游戏] 推荐:

【转载】从Mozilla的HTML5游戏Browser Quest看代码漏洞

- - HTML5研究小组
声明:作弊并不是什么好事,我不提倡作弊. 我也不想讽刺Mozilla的代码质量(实际上其代码糟透了). 我不是超级黑客,也不想在此唱高调. 只是想演示一下我在Mozilla新游戏上是如何作弊的,以后大家在编写HTML5游戏时要注意避免这些问题. 为了将Bard’s Tale游戏通过磁盘编辑器进行编辑并存入我的Apple 2e,我不得不学习16进制.

百行 HTML5 代码实现四种双人对弈游戏

- - 博客 - 伯乐在线
简介: 本文是一个非常具有挑战性的编程,因为 100 行代码,约莫 10000 个字符左右,将实现围棋、五子棋、四子棋和翻转棋四种双人对弈游戏. 请注意,这四个对弈游戏不是初级编程者的习作,而是有着棋盘、立体棋子、事件、走棋规则判断、输赢判断的完整对弈游戏,并且可以离线存储到 iPad、Android 平板中,试想一下,把这种游戏下载到平板中,就可以在火车,旅游景区,等没有信号的地方进行对弈,是不是扩展了平板电脑的功能,是不是一种很惬意的事情.

割绳子游戏HTML5版

- - 我爱互联网
在拉斯维加斯正在举行的CES大会上,微软CEO鲍尔默宣布了基于IE9和 html 5框架的 割绳子游戏,由微软及游戏开发商ZeptoLab共同推出,用于促进IE9的使用及网页的美化,网页版点击这里. 游戏主角就是一个贪吃的小绿怪兽,必须通过割绳子来将糖果送到小怪物嘴里,并搜集星星来增加得分. 到目前为止,下载量已经超过6000万,潜力足以和《愤怒的小鸟》媲美.

游戏开发商开源HTML5游戏

- - Solidot
游戏工作室Wooga开源了其开发的HTML5游戏Pocket Island,源代码托管在GitHub上,该公司在官方博客上介绍了他们的开发经验,认为HTML5游戏有潜力,但尚未做好准备,开源的意图将是让其他人了解他们的工作,学习和改进. Wooga认为,2012年也许不是HTML5的黄金时代,但它的黄金时代即将到来.

HTML5 tower defense – HTML5 塔防游戏 | 小众软件 > 游戏

- Yishen - 小众软件
塔防类游戏还是很受人喜爱的,今天的这个塔防是用 HTML5 写成的,虽然是简单的画面,但是深得塔防精髓. 官方网站 | 来自小众软件. ©2011 root for 小众软件 | 原文链接 | 0 留言 | 加入我们 | 投稿 | 订阅指南. vNES – 经典的在线电视游戏. 玛丽猫 – 经典的变态超级玛丽.

推荐10个HTML5游戏网站

- ZB - cnBeta.COM
原文作者Julio Rivera是一名来自纽约市的创意总监和网页设计师,他是Underworld Magazines的创始人. 到现在为止,我玩了好几年的在线游戏. 我已经看过用Flash和其他软件制作游戏,目前是在用HTML5做游戏,只看到一个完整用CSS3做的游戏.

25 超棒的 HTML5 Canvas 游戏

- 迎客松 - LinuxEden开源社区-Linux伊甸园
Canvas 元素作为HTML5标准的一部分,允许你通过脚本动态渲染点阵图像. 这是为了基于浏览器的矢量图形而设计. HTML Canvas 把一个绘图 API 展现给 JS 客户端,使得脚本能够把想绘制的东西都绘制到一块画布上. 阅读全文 | 邮件推荐 | 评论回复.

推荐10个HTML5游戏网站

- scotty - 博客园新闻频道
  导读:原文作者Julio Rivera是一名来自纽约市的创意总监和网页设计师,他是Underworld Magazines的创始人.   到现在为止,我玩了好几年的在线游戏. 我已经看过用Flash和其他软件制作游戏,目前是在用HTML5做游戏,只看到一个完整用CSS3做的游戏.   HTML5正在慢慢成为新的Flash,以前在Flash上很酷的东东,如拖放和下载条等,现在已可以用HTML5做到了.

HTML5游戏开发工具推荐:IMPACT

- iDesperadO - HTML5研究小组
Impact 是一个 JavaScript 游戏引擎,可以为桌面和手机浏览器开发令人惊叹的 HTML5 游戏. 我已经试过了四个其他 JavaScript 游戏引擎,这是我用过的第一个比较有意义的(……)Impact 是市场上第一个真正专业级的 JavaScript 和 HTML5 游戏引擎. ——《Game Developer Magazine》,2011年5月.

HTML5游戏前端开发秘籍

- - 腾讯ISUX - 社交用户体验设计 - Better Experience Through Design
QQ空间Android版4.2和4.5上线的玩吧游戏“空间疯狂套牛”是一款使用HTML5开发出的手机游戏,虽然还有很多不足,但其中使用的一些技术与技巧还是很有必要为大家分享出来,方便大家用秘籍打通各种关卡创造出更多更好的HTML5游戏. (本秘籍主要讲述使用HTML + CSS技术方面). Android手机的屏幕碎片化非常严重,各种各样的分辨率非常之多,那么如何让游戏可以适配这些机型就显得尤为重要.