vlambda博客
学习文章列表

HTML5 实现黑白棋游戏|附代码

黑白棋,又叫反棋 (Reversi) 、奥赛罗棋 (Othello) ,苹果棋,翻转棋。黑白棋在西方和日本很流行。游戏通过相互翻转对方的棋子,最后以棋盘上谁的棋子多来判断胜负。黑白棋的棋盘是一个有 8*8 方格的棋盘。下棋时将棋下在空格中间,而不是像围棋一样下在交叉点上。开始时在棋盘正中有两白两黑四个棋子交叉放置,黑棋总是先下子。

下子规则:
把自己颜色的棋子放在棋盘的空格上,而当自己放下的棋子在横、竖、斜八个方向內有一个自己的棋子,则被夹在中间的全部翻转会成为自己的棋子。并且,只有在可以翻转棋子的地方才可以下子。如果玩家在棋盘上没有地方可以下子,则该玩家对手可以连下。
胜负判定条件:
双方都没有棋子可以下时棋局结束,以棋子数目来计算胜负,棋子多的一方获胜。
在棋盘还没有下满时,如果一方的棋子已经被对方吃光,则棋局也结束。将对手棋子吃光的一方获胜。
本章开发黑白棋游戏程序。该游戏具有显示执棋方可以落棋子的位置提示功能和判断胜负功能。在游戏过程中,点击“帮助”按钮则显示执棋方可落子位置(图片表示可落子位置,如图11-2所示)。游戏运行界面如图11-1。
HTML5 实现黑白棋游戏|附代码

图11-1  黑白棋游戏运行界面

HTML5 实现黑白棋游戏|附代码

图11-2  i表示执棋方(黑方)可落子位置

01

黑白棋游戏设计的思路

1. 棋子和棋盘

游戏开发时,需要事先准备黑白两色棋子和棋盘图片(如图11-3所示)。游戏最初显示时,棋盘上画上4个棋子。这里为了便于处理,采用一个qizi二维数组用来存储棋盘上棋子。

HTML5 实现黑白棋游戏|附代码

图11-3  黑白两色棋子

2. 翻转对方的棋子

需要从自己落子(x1,y1)为中心的横、竖、斜八个方向上判断是否需要翻转对方的棋子,程序中由鼠标的"mousedown"事件实现的。在"mousedown"事件中参数event对象含有单击位置像素坐标(event.pageX,event.pageY),处理后变成Canvas对象内像素坐标(x,y)。再如下公式换算:

x1 = Math.round((x - 40) / 80); //Math.round四舍五入
y1 = Math.round((x - 40) / 80);

经过换算转换为棋盘坐标(x1,y1)。

最后从左,左上,上,右上,右,右下,下,左下八个方向上调用过程DirectReverse(x1, y1, dx, dy)翻转对方的棋子。而具体棋子的翻转由FanQi(x, y)实现。FanQi(x, y)修改数组qizi的(x, y)处保存棋盘上的棋子信息。

function FanQi(x, y{
if (qizi[x][y] == BLACK) {
qizi[x][y] = WHITE;

            else {
qizi[x][y] = BLACK;
}
}

3. 显示执棋方可落子位置

Can_go(x1,y1)从左,左上,上,右上,右,右下,下,左下八个方向上调用函数CheckDirect(x1, y1, dx, dy)判断某方向上是否形成夹击之势,如果形成且中间无空子则返回True,表示(x1,y1)可以落子。(x1,y1)处可以落子则用图片 i 显示。

4. 判断胜负功能

qizi[][]二维数组保存棋盘上的棋子信息,其中元素保存1,表示此处为黑子;元素保存2,表示此处为白子;元素保存0,表示此处为无棋子。通过对qizi数组中各方棋子数统计,在棋盘无处可下时,根据各方棋子数判断出输赢。

02

关键技术

1.Canvas对象支持的JavaScript的鼠标事件

Canvas对象支持所有的JavaScript的鼠标事件,包括鼠标点击(MouseClick)、鼠标按下(Mouse Down)、鼠标抬起(Mouse Up)和鼠标移动( Mouse Move)。对Canvas添加鼠标事件方式有两种,一种方式如下:
// mouse event 
canvas.addEventListener("mousedown",doMouseDown,false); 
canvas.addEventListener('mousemove', doMouseMove,false); 
canvas.addEventListener('mouseup', doMouseUp, false);
另外一种方式在JavaScript中称为反模式:
canvas.onmousedown = function(e)

canvas.onmouseup = function(e)

canvas.onmousemove = function(e)
}

2. 获取鼠标在Canvas对象上坐标

由于Canvas上鼠标事件中不能直接获取鼠标在Canvas的坐标,所获取的都是基于整个屏幕的坐标。所以通过鼠标事件e.pageX与e.pageY来获取鼠标位置,然后通过Canvas. getBoundingClientRect()来获取Canvas对象相对屏幕的相对位置,通过计算得到鼠标在Canvas的坐标,代码如下:
function getPointOnCanvas(canvas, x, y
var bbox =canvas.getBoundingClientRect(); 
return { x: x- bbox.left *(canvas.width / bbox.width), 
y:y - bbox.top * (canvas.height / bbox.height) 
}; 
}

03

黑白棋游戏设计的步骤

1. 游戏页面

<html>
<head>
<title>黑白棋</title>
<meta http-equiv=content-type content="text/html; charset=utf-8">
<meta name="Generator" content="EditPlus">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
</head>

<body onload="init()" onkeydown="DoKeyDown(event)">
<canvas id="myCanvas" width="720" height="720">你的浏览器还不支持哦</canvas>
<img id="whitestone" src="img/whitestone.png" style="display:none;">
<img id="blackstone" src="img/blackstone.png" style="display:none;">
<img id="qi_pan1" src="img/qi_pan1.jpg" style="display:none;">
<img id="Info2" src="img/Info2.png" style="display:none;">

<div id="message_txt" style="text-align:center;border:1px solid red; width:720px;height:20px;font-size:20px;"></div>
<input type="button" value="走棋提示" onclick="DoHelp()">
<script type="text/javascript" src="Main.js"></script>
</body>
</html>

2. 设计脚本(Main.js)

1)常量定义

游戏中常量定义,其中BLACK黑棋为1,WHITE白棋为1,无棋为0。

 //常量
    var BLACK = 1;
    var WHITE = 2;
    var KONG  = 0;
        var w=80;
        var h=80;

以下获取Canvas对象,及用到的棋子和棋盘图片、提示图片。

var qizi =new Array();//构造一个qizi[][]二维数组用来存储棋子
         var curQizi  = BLACK;// 当前走棋方
         var mycanvas=document.getElementById('myCanvas');
         var context = mycanvas.getContext('2d');
         var whitestone=document.getElementById("whitestone");//白棋图片
         var blackstone=document.getElementById("blackstone");//黑棋图片
         var qipan=document.getElementById("qi_pan1");//棋盘
         var info=document.getElementById("Info2");//提示图形
         var message_txt=document.getElementById("message_txt");//提醒文字

2)初始化游戏界面

游戏开始时,init()对保存棋盘上的棋子信息的qizi数组初始化,同时在棋盘上显示初始的4个棋子。

function init(){
             initLevel();// 棋盘上初始4个棋子
             showMoveInfo();//当前走棋方信息
             mycanvas.addEventListener("mousedown",doMouseDown,false); 
         }
         function initLevel({
//初始化界面
var i,j;
for (i=0; i<8; i++) {
qizi[i]=new Array();
for (j=0; j<8; j++) {
qizi[i][j]=KONG;
}
}
// 棋盘上初始4个棋子
// 1为黑,2为白,0为无棋子
qizi[3][3] = WHITE;
qizi[4][4] = WHITE;
qizi[3][4] = BLACK;
qizi[4][3] = BLACK;
DrawMap();  //画棋盘和所有棋子
message_txt.innerHTML = "该黑棋走子";
}
 //画棋盘和所有棋子
        function DrawMap( )
        
{
            context.clearRect ( 0 , 0 ,720 , 720); 
            context.drawImage(qipan,0,0,qipan.width,qipan.height);
        for(i=0;i<qizi.length;i++)//行号
        {
        for(j=0;j<qizi[i].length;j++)//列号
        {
        var pic;
        switch (qizi[i][j])
        {
        case KONG:                //0
        break;
        case BLACK:               //1
            pic = blackstone;
            context.drawImage(pic, w * j, h * i, pic.width, pic.height);
        break;
        case WHITE:               //2
            pic = whitestone;
            context.drawImage(pic, w * j, h * i, pic.width, pic.height);
        break;
        }
        }
        }
        }

showMoveInfo()显示轮到那方走棋。

function showMoveInfo(){
             if (curQizi== BLACK)// 当前走棋方是黑棋
                  message_txt.innerHTML="该黑棋走子";
             else
                  message_txt.innerHTML="该白棋走子";
         }

init()函数同时对canvas添加鼠标单击事件的侦听。如果canvas被单击则执行doMouseDown函数完成走棋功能。

3)走棋过程

如果是棋盘被单击,则此位置像素信息(event.pageX,event.pageY)可以转换成棋盘坐标 (x1,y1),然后判断当前位置(x1, y1)是否可以放棋子(符合夹角之势),如果可以则此位置显示自己棋子图形,调用FanALLQi(i, j)从左,左上,上,右上等八个方向翻转对方的棋。最后判断对方是否有棋可走,如果对方可以走棋则交换走棋方。如果对方不可以走棋,则自己可以继续走棋,直到双方都不能走棋,显示输赢信息。

 function doMouseDown(event
             var x = event.pageX; 
             var y = event.pageY; 
             var canvas = event.target; 
             var loc = getPointOnCanvas(canvas, x, y); 
             console.log("mouse down at point( x:" + loc.x + ", y:" + loc.y + ")"); 
             clickQi(loc);             
         }
     function getPointOnCanvas(canvas, x, y
             var bbox = canvas.getBoundingClientRect(); 
             return { x: x - bbox.left * (canvas.width / bbox.width),
                      y: y - bbox.top * (canvas.height / bbox.height)};
         }
    function clickQi(thisQi{
var x1, y1;
x1 = Math.round((thisQi.y - 40) / 80);  
y1 = Math.round((thisQi.x - 40) / 80); //Math.round()四舍五入
if (Can_go(x1, y1)) {// 判断当前位置是否可以放棋子
//trace("can");
qizi[x1][y1] = curQizi;
FanALLQi(x1, y1);//从左,左上,上,右上,右,右下,下,左下方向翻转对方的棋
DrawMap();
//判断对方是否有棋可走,如有交换走棋方
if (curQizi==WHITE &&checkNext(BLACK) ||curQizi==BLACK &&checkNext(WHITE)) {
if (curQizi==WHITE) {
curQizi=BLACK;
message_txt.innerHTML = "该黑棋走子";
else {
curQizi=WHITE;
message_txt.innerHTML = "该白棋走子";
}
else if (checkNext(curQizi)) {
                       //判断自己是否有棋可走,如有,给出提示
                        message_txt.innerHTML = "对方无棋可走,请继续";
else {//双方都无棋可走,游戏结束,显示输赢信息
isLoseWin();
}//统计双方的棋子数量,显示输赢信息。
}
            else {
                message_txt.innerHTML = "不能落子!";
}
}

4)可否落子判断

Can_go(x1,y1)从左,左上,上,右上,右,右下,下,左下八个方向判断(x1,y1)处可否落子。

function Can_go( x1,  y1){
//从左,左上,上,右上,右,右下,下,左下八个方向判断
if (CheckDirect(x1, y1, -10) == true) {
return true;
}
if (CheckDirect(x1, y1, -1-1) == true) {
return true;
}
if (CheckDirect(x1, y1, 0-1) == true) {
return true;
}
if (CheckDirect(x1, y1, 1-1) == true) {
return true;
}
if (CheckDirect(x1, y1, 10) == true) {
return true;
}
if (CheckDirect(x1, y1, 11) == true) {
return true;
}
if (CheckDirect(x1, y1, 01) == true) {
return true;
}
if (CheckDirect(x1, y1, -11) == true) {
return true;
}
return false;
}

CheckDirect()判断某方向上是否形成夹击之势,如果形成且中间无空子则返回True。

  function CheckDirect( x1,  y1,  dx,  dy){
            var x,y;
            var flag= false;
            x = x1 + dx;
            y = y1 + dy;
            while (InBoard(x, y) && !Ismychess(x, y) && qizi[x][y] != 0) {
                x += dx;
                y += dy;
                flag = true;//构成夹击之势
            }
            if (InBoard(x, y) && Ismychess(x, y) && flag == true) {
                return true;//该方向落子有效
            }
            return false;
        } 

checkNext(i)验证参数代表的走棋方是否还有棋可走。

/**
 * 验证参数代表的走棋方是否还有棋可走
 * @param i   代表走棋方,1为黑方,2为白方
 * @return true/false
 */

function checkNext(i){
            old=curQizi;
            curQizi=i;
if ( Can_Num()>0) {
                curQizi=old;
return true;

            else {
                curQizi=old;
return false;
}

Can_Num()统计可以落子的位置数。

 function Can_Num({//统计可以落子的位置数
            var i, j, n = 0;
            for (i = 1; i <= 8; i++) {
                for (j = 1; j <= 8; j++) {
                    if (Can_go(i, j)) {
                        n = n + 1;
                    }
                }
            }
            return n;//可以落子的位置个数
         }

5)翻转对方的棋子

FanALLQi(int x1, int y1) 从左,左上,上,右上,右,右下,下,左下八个方向翻转对方的棋子。

function FanALLQi(x1, y1{
//从左,左上,上,右上,右,右下,下,左下八个方向翻转
if (CheckDirect(x1, y1, -10) == true) {
DirectReverse(x1, y1, -10);
}
if (CheckDirect(x1, y1, -1-1) == true) {
DirectReverse(x1, y1, -1-1);
}
if (CheckDirect(x1, y1, 0-1) == true) {
DirectReverse(x1, y1, 0-1);
}
if (CheckDirect(x1, y1, 1-1) == true) {
DirectReverse(x1, y1, 1-1);
}
if (CheckDirect(x1, y1, 10) == true) {
DirectReverse(x1, y1, 10);
}
if (CheckDirect(x1, y1, 11) == true) {
DirectReverse(x1, y1, 11);
}
if (CheckDirect(x1, y1, 01) == true) {
DirectReverse(x1, y1, 01);
}
if (CheckDirect(x1, y1, -11) == true) {
DirectReverse(x1, y1, -11);
}
}

DirectReverse()针对已形成夹击之势某方向上的对方棋子进行翻转。

 function DirectReverse(x1, y1, dx, dy{
var x, y;
var flag= false;
x = x1 + dx;
y = y1 + dy;
while (InBoard(x, y) && !Ismychess(x, y) && qizi[x][y] != 0) {
x += dx;
y += dy;
flag = true;//构成夹击之势
}
if (InBoard(x, y) && Ismychess(x, y) && flag == true) {
do {
x -= dx;
y -= dy;
if ((x != x1 || y != y1)) {
FanQi(x, y);
}
while ((x != x1 || y != y1));
}
}

FanQi(int x, int y)将存储(x, y)处棋子信息qizi[x][y]的反色处理。

 function FanQi(x, y{
if (qizi[x][y] == BLACK) {
qizi[x][y] = WHITE;

else {
qizi[x][y] = BLACK;
}
}

InBoard()判断(x,y)是否在棋盘界内,如果在界内则返回真,否则返回假。

//InBoard()判断(x,y)是否在棋盘界内,如果在界内则返回真,否则返回假。
 function InBoard(x,y ){
if (x >= 0 && x <= 7 && y >= 0 && y <= 7) {
return true;
else {
return false;
}
}

6)显示执棋方可落子位置

“走棋提示”按钮单击事件函数是DoHelp(),它显示可以落子的位置提示。Show_Can_Position()用图片i显示可以落子的位置。

 function DoHelp({
            showCanPosition();//显示可以落子的位置
        }
        function showCanPosition({
            //显示可以落子的位置
            var i,j;
            var n = 0;//可以落子的位置统计
            for (i = 0; i <= 7; i++) {
                for (j = 0; j <= 7; j++) {
                    if (qizi[i][j] == 0 && Can_go(i, j)) {
                      n = n + 1;
                      pic=info;
                      context.drawImage(pic,w*j+20,h*i+20,pic.width,pic.height);;//显示提示图形
                    }
                }
            }
        }

7)判断胜负功能

isLoseWin()统计双方的棋子数量,显示输赢信息。

 // 显示输赢信息
         function isLoseWin({
            var whitenum = 0;
            var blacknum = 0;
            var n = 0,x,y;
            for (x = 0; x <= 8; x++) {
                for (y = 0; y <= 8; y++) {
                    if (qizi[x][y] != 0) {
                        n = n + 1;
                        if (qizi[x][y] == 2) {
                            whitenum += 1;
                        }
                        if (qizi[x][y] == 1) {
                            blacknum += 1;
                        }
                    }
                }
            }
            if (blacknum > whitenum) {
                message_txt.innerHTML = "游戏结束黑方胜利,黑方:" + String(blacknum) + "白方:" + String(whitenum);
            } else {
                message_txt.innerHTML = "游戏结束白方胜利, 黑方:" + String(blacknum) + "白方:" + String(whitenum);
            }
        }
至此就完成黑白棋游戏设计了。

04

源代码下载

” 即可获得完整源代码。

05

参考书籍

《HTML5 网页游戏设计从基础到开发》

ISBN:978-7-302-49591-8

夏敏捷 编著

定价:69.8元

HTML5 实现黑白棋游戏|附代码

06

精彩文章回顾



  • 检测