百行 HTML5 代碼實現四種雙人對弈遊戲

    簡介: 本文是一個很是具備挑戰性的編程,由於 100 行代碼,約莫 10000 個字符左右,將實現圍棋、五子棋、四子棋和翻轉棋四種雙人對弈遊戲。請注意,這四個對弈遊戲不是初級編程者的習做,而是有着棋盤、立體棋子、事件、走棋規 則判斷、輸贏判斷的完整對弈遊戲,而且能夠離線存儲到 iPad、Android 平板中,試想一下,把這種遊戲下載到平板中,就能夠在火車,旅遊景區,等沒有信號的地方進行對弈,是否是擴展了平板電腦的功能,是否是一種很愜意的事情。 並且,關鍵是,這個程序沒有圖片,不須要去應用商店付費下載,僅僅是用 HTML5 技術寫的 100 行代碼而已,絕對是目前最迷您精悍的雙人對弈遊戲源碼。(編者注:因爲網頁代碼的寬度有限制,因此做者的源代碼通過了一些換行處理,特此說明。)css

目標html

要作一個完整的雙人對弈遊戲,至少要作以下事情,第一步:繪製棋盤。不一樣的棋類遊戲棋盤不一樣,這一點須要進行動態處理;第二步:繪製棋子。 須要說明的是,圍棋,五子棋等這些棋子都是圓的啊,請不要爲了圖片苦惱,在 HTML5 時代,咱們用代碼就能夠實現立體圓形棋子;第三步:判斷落子事件。固然是要定位手指的點擊位置,這四種棋中,有的是落在框裏面的,有的倒是落在縱橫交錯的 棋盤十字線上,須要動態處理;第四步:判斷落子規則。下棋都有規則,不要由於代碼少,就將規則打折扣,不然程序不成熟,會變成小朋友的玩具了;第五步:判 斷輸贏。最後,咱們要判斷輸贏。也就是要數子,這個事情必須由程序來完成,由於下棋總得須要一個裁判嘛;第六步:就是平板電腦時代,咱們得實現離線應用。 這個過重要了,不然,要是在臺式電腦上,接根網線玩的遊戲,已經遍地都是了,您寫得再牛,有什麼用?就是要移動,在沒有信號的地方,纔有市場,如今平板, 智能手機這麼多,在沒有網絡信號的地方,掏出移動設備來下棋,纔是一件很牛的事情。html5

 

繪製棋盤程序員

前面說了圍棋、五子棋、四子棋和翻轉棋的棋盤並不相同,圍棋是縱橫 18 個格,其餘三種棋則是 8 個格。因此繪製棋盤是須要有參數。這是個小問題,大問題是,選擇什麼方法來繪製棋盤?web

HTML5 框架下,有至少 3 種方法:第一種,用 Canvas 畫線;第二種,用 DIV,CSS3 裏面增長了行列屬性;第三種,用 table 標籤。ajax

用哪種速度最快,代碼少呢?答案是:第三種。多少有點失望啊,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 行代碼,一個棋盤就畫好了,固然這其中不只僅是畫線,還有棋子處理,事件定義等方法的調用,後面將陸續談到。canvas

 

繪製棋子數組

繪製完棋盤,咱們來繪製棋子。咱們挑選的這四種棋,雖然棋盤不一樣,可是棋子都是相同的,都是黑白棋子。這在之前,作在線對弈,除了 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. 效果圖
圖 1. 效果圖

圖中加了一個選擇棋類型和設置背景功能,如要得到所有源代碼,只要使用瀏覽器的查看源代碼功能便可,限於篇幅,這裏就不貼了。

 

總結

做爲一個程序員,最高的境界不是寫得代碼越多越好,而是用最少的代碼實現最多的計算,解決最多的問題。回想當年,蓋茨在編寫 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 相關的知識和動向。

討論

加入 developerWorks 中文社區。查看開發人員推進的博客、論壇、組和維基,並與其餘 developerWorks 用戶交流。
相關文章
相關標籤/搜索