五子棋的棋盘与黑白棋、围棋等具有非常大的相似度,所以为了以后开发黑白棋、围棋等游戏对于五子棋的棋盘需要可以单独使用。基于这个要求我们需要把棋盘作为单独的对象提取出来,并可以应用与任何棋子游戏中。
棋盘应具有以下几个功能:
棋盘可以分别绘制左上角、上部、右上角、左部、中间(十字)、右部、左下角、下部、右下角。在显示上可以使用背景图片的方式处理,这样行数为row,列数为col的棋盘可以使用row*col个背景图片来组成,而其中不同的背景图片仅有上面分析的9种。
棋盘的具体实现代码如下:
ChessBoard(row, col, element)={
initBoard,//初始化棋盘,绘制棋盘等操作
hideHover,//隐藏鼠标覆盖提示
getHoverPosition,//获取鼠标覆盖的位置
onBoardClick,//触发棋盘点击事件(棋盘自己调用该方法)
bindBoardClick,//绑定棋盘点击事件
unbindBoardClick//取消绑定棋盘点击事件
}
var boardConfig = {
cls : 'board',
bgLeftTopCls : 'board-lt',
bgTopCls : 'board-t',
bgRightTopCls : 'board-rt',
bgLeftCls : 'board-l',
bgMiddleCls : 'board-m',
bgRightCls : 'board-r',
bgLeftDownCls : 'board-ld',
bgDownCls : 'board-d',
bgRightDownCls : 'board-rd',
hoverCls : 'board-hover'
};
var ChessBoard = function(rows, cols, ele){
this.rows = rows;
this.cols = cols;
this.ele = $(ele);
this.initBoard();
};
ChessBoard.prototype = {
initBoard : function(){//初始化棋盘
var me = this, rows = me.rows, cols = me.cols, $ele = me.ele, bc = boardConfig;
$ele.empty();
$ele.addClass('chessboard');
var $hover = $('').addClass(bc.hoverCls);
me.eleHover = $hover;
$hover.hide().appendTo($ele);
var row = 0, col = 0, maxRow = rows - 1, maxCol = cols - 1;
var cls = bc.bgMiddleCls, $dom;
for(row = 0; row < rows; row++){
for(col = 0; col < cols; col++){
$dom = $('').addClass(bc.cls);
if(row == 0){
switch(col){
case 0:
cls = bc.bgLeftTopCls;
break;
case maxCol:
cls = bc.bgRightTopCls;
break;
default:
cls = bc.bgTopCls;
}
}else if(row == maxRow){
switch(col){
case 0:
cls = bc.bgLeftDownCls;
break;
case maxCol:
cls = bc.bgRightDownCls;
break;
default:
cls = bc.bgDownCls;
}
}else{
switch(col){
case 0:
cls = bc.bgLeftCls;
break;
case maxCol:
cls = bc.bgRightCls;
break;
default:
cls = bc.bgMiddleCls;
}
}
$dom.addClass(cls);
$dom.attr({row:row, col:col});//便于Dom查找,增加DOM的row与col属性.
$ele.append($dom);
(function(row, col, $dom, me){
$dom.click(function(event){
if($(event.target).is('.' + bc.cls)){
me.onBoardClick(row, col, $dom, event);
}
});
})(row, col, $dom, me);
}
}
var $child = $ele.children().eq(0), cw = $child.outerWidth(), ch = $child.outerHeight();
$ele.css({width:cw * col + 'px', height: ch * row + 'px'});
$ele.unbind('mouseleave').bind('mouseleave',function(event){
$hover.hide();
$hover.removeData('target');
});
$ele.children('.' + bc.cls).bind('mouseenter', function(event){//绑定落子点鼠标覆盖事件
var $dom = $(this);
$hover.show().offset($dom.offset());
$hover.data('target', $dom);
});
$hover.bind('mouseleave click mouseout dblclick', function(event){//代理落子点事件
var $tar = $hover.data('target');
if($tar && $tar.trigger){
event.target = $tar.get(0);
$tar.trigger(event);
}
});
},
hideHover : function(){//隐藏hover显示
this.eleHover.hide();
},
getHoverPosition : function(){//返回[行号,列号], 如果没有hover则返回false
var $tar = this.eleHover.data('target');
if($tar && $tar.attr){
return [$tar.attr('row'), $tar.attr('col')];
}else{
return false;
}
},
onBoardClick : function(row, col, $dom, event){//棋盘落子点被点击
var me = this;
event.stopPropagation();
event.preventDefault();
me.ele.triggerHandler('boardClick', [row, col, $dom, event]);
},
bindBoardClick : function(callback){//绑定棋盘落子点点击事件
this.ele.bind('boardClick', callback);
},
unbindBoardClick : function(callback){//取消绑定棋盘落子点点击事件
this.ele.unbind('boardClick', callback);
}
};
五子棋游戏
var chessConfig = {
cls : 'chess',//棋子元素默认样式
blackChess : {
name : '黑棋',
cls : 'black',
lastCls : 'black-last',
flag : 1
},
whiteChess : {
name : '白棋',
cls : 'white',
lastCls : 'white-last',
flag : 2
},
defFlag : 0,
direction : {//棋子排列方向
heng : [0, 1],//横向
shu : [1, 0],//竖向
pie : [-1, 1],//撇
na : [1, 1]//捺
}
};
为了提高兴趣与验证棋盘的可用性,我决定先测试下落子功能。落子功能很简单,仅需绑定棋盘点击事件,然后在事件中对应的DOM元素内插入个棋子就可以。具体代码如下:
var board = new ChessBoard(15, 15, $('#board'));
board.bindBoardClick(function(event, row, col, $dom, ev){
var $chess = $('').addClass(chessConfig.cls).addClass(chessConfig.blackChess.cls);
$dom.empty().append($chess);
});
通过上面5行代码就可将棋子添加到棋盘中显示了.
五子棋游戏是在row*col大小的棋盘上使用黑子、白子来展开的对战游戏。其中每个落子点有3个状态(空、黑子、白子),每次落子后都要检查落子后选手是否赢得游戏,游戏的胜利规则为落子后该子所在点的横竖撇捺四个方向上相同的棋子数量》=5即可赢得游戏。
首先,根据棋盘大小我们建立一个row*col的二维数组,用于存储整个棋盘的状态信息,默认棋盘状态为0(chessConfig.defFlag)。棋子信息为chessConfig.xxxChess.flag,目前我们只有blackChess(黑棋)和whiteChess(白棋)两个类型的棋子。
其次,每次落子后需要进行连子计算,分别计算4个方向上的连子数量。已落子点为原点建立直角坐标系不难发现,4个方向上棋子的坐标与原点坐标都是递增(从左向右看)或递减(从右向左看)变化。根据这个规则我们分别定义了4个方向上的单步递增量chessConfig.direction,这样我们在计算某个方向上的连子数量时仅传入落子点的坐标row,col,棋子的标记,棋盘状态信息(二维数组),方向递增量即可计算出该方向上的连子数量(注意除了正向数量还要加上反向数量,反向递增量仅需把递增量个坐标*-1即可)。
最终,每次落子后计算完是否胜利,如果胜利则提示游戏结束XXX胜利,否则需要切换下棋方,比如之前是黑棋落子,经判断没有胜利则下次落子则应为白棋落子。
var FiveChess = function(conf){
this.conf = conf;
var row = this.row = conf.row || 15;
var col = this.col = conf.col || 15;
var $board = this.$board = $(conf.board);
this.chessDatas = new Array(row);
this.chessBoard = new ChessBoard(row, col, $board);
this.initFiveChess();
};
FiveChess.prototype = {
userFirst : true,//是否用户先手
useBlack : true,//用户是否使用黑色棋子
userChess : chessConfig.blackChess,//用户棋子信息
rivalChess : chessConfig.whiteChess,//对手棋子信息
userTurn : true,//是否轮到用户下棋
chessBoard : null,//棋盘对象
lastChesses : {},//最后下棋信息
errors : {
putChess : []
},
initFiveChess : function(){
var me = this, row = me.row, col = me.col, $board = me.$board;
me.userFirst = true;//黑棋先下
me.useBlack = true;//当前用户使用黑棋
},
initGame : function(){
var me = this;
me.chessBoard.bindBoardClick(me.getBoardClickListener());
me.userChess = me.useBlack ? chessConfig.blackChess : chessConfig.whiteChess;
me.rivalChess = me.useBlack ? chessConfig.whiteChess : chessConfig.blackChess;
me.userTurn = me.userFirst;
me.chessBoard.initBoard();
for(var i = 0; i < me.row; i++){
me.chessDatas[i] = new Array(me.col);
for(var j = 0; j < me.col; j++){
me.chessDatas[i][j] = chessConfig.defFlag;
}
}
},
gameStart : function(){
var me = this;
me.gameOver();
me.initGame();
},
gameOver : function(){
var me = this;
me.chessBoard.unbindBoardClick(me.getBoardClickListener());
},
serviceNext : function(){
var me = this;
me.userTurn = !me.userTurn;
},
checkWin : function(row, col, chess, chessDatas){
var me = this, chessDatas = me.chessDatas;
var fx1 = me.chessLineNums(row, col, chess.flag, chessDatas, chessConfig.direction.heng, true),
fx2 = me.chessLineNums(row, col, chess.flag, chessDatas, chessConfig.direction.shu, true),
fx3 = me.chessLineNums(row, col, chess.flag, chessDatas, chessConfig.direction.pie, true),
fx4 = me.chessLineNums(row, col, chess.flag, chessDatas, chessConfig.direction.na, true),
maxLineNum = Math.max(fx1, fx2, fx3, fx4);//获取四个方向上最大连子数量
if(maxLineNum >= 5){
alert(chess.name + '赢了!');
me.gameOver();
return true;
}
return false;
},
getBoardClickListener : function(){//绑定事件处理函数的作用域(对游戏自身的引用).
var me = this;
if(!me.onBoardClick){
me.onBoardClick = function(event, row, col, $dom, ev){
var chessDatas = me.chessDatas,
chess = me.userTurn ? me.userChess : me.rivalChess;
var putResult, isWin;
putResult = me.putChess(row, col, chess);
if(putResult){
isWin = me.checkWin(row, col, chess, chessDatas);
if(!isWin){
me.serviceNext();
}
}
};
}
return me.onBoardClick;
},
onBoardClick : undefined,
putChess : function(row, col, chess){
var me = this, chessDatas = me.chessDatas;
var selector = 'div.{0}[row="{1}"][col="{2}"]'.format([boardConfig.cls, row, col]);
$dom = me.chessBoard.ele.find(selector);
if(chessDatas[row][col] == chessConfig.defFlag){
var $chess = $('').addClass(chessConfig.cls).addClass(chess.lastCls);//添加棋子并标记最后落子样式
$dom.append($chess);
chessDatas[row][col] = chess.flag;
var $prevLastChess = me.lastChesses[chess.flag];
if($prevLastChess && $prevLastChess.removeClass){//移除上一次标记的最后落子样式
$prevLastChess.removeClass(chess.lastCls).addClass(chess.cls);
}
me.lastChesses[chess.flag] = $chess;
return 1;
}else{
return 0;
}
},
chessLineNums : function(row, col, chessType, arr, direction, checkReverse){//下子坐标row, 下子坐标Y, 下子类型, 棋子数组, 方向[行,列], 是否检查反向
var w = arr[0].length, h = arr.length;
var dRow = direction[0], dCol = direction[1];
var nums = 1;
var srow = row, scol = col, multiple = 1;
while(true){
srow += dRow;//下一方向X坐标
scol += dCol;//下一方向Y坐标
if(srow < w && scol < h && srow >= 0 && scol >= 0){//坐标是否在棋盘内
var type = arr[srow][scol];
if(type == chessType){//下一方向棋子类型相同
nums++;
}else{//下一方向棋子类型不同,退出
break;
}
}else{
break;
}
multiple++;
}
if(checkReverse){
nums = nums + this.chessLineNums(row, col, chessType, arr, [-dRow, -dCol], false) - 1;//加上反向的数据
}
return nums;
}
};
程序源码及资源文件下载地址:http://download.csdn.net/detail/zyb134506/6928955