五子棋游戏设计详解
五子棋游戏是大家耳熟能详的游戏,网上看到那些学习编程的都在制作这种游戏程序。我也来凑一下热闹。
游戏设计有几个要素: 一是界面设计,也就是画出棋盘;二是黑白双方如何下子,即时棋盘显示;三是程序要有悔棋功能;四是游戏过程记录,便于棋后复盘;五是游戏模式设置,双人对弈和人机模式。这五条是游戏的基本功能,其他功能可自行添加。
程序编制先定义全局变量:
Canvas cs; //画布,绘图时图形图像载体
//VC是 canvas , VB是 picture ,java 是 J Panel
int px,py; // piece X Y 棋子坐标
int dx,dy; //draw X Y 绘图坐标
int i,j,kn; // 用于循环
int pn[300]; // pn数组 0=无子 ,1=黑子,2=白子
// 棋子255个编码号,防下标越界,容错下标加大
int n; // 下子时获取 棋子编码号
int dn; //下子计数
int isDo; //游戏操作控制: 1=可下子,0=不可下
int B,W,k; //判断胜负:B黑棋 , W白棋
string cordp; //游戏记录字串
int mode; //模式设定:0=双人,1=人机
main () 主程序,初始化设置
board () 起始界面画棋盘,游戏时画已下的黑白子,更新屏幕输出
detect () 判断胜负
undo () 悔棋
chessAI () 人机模式 AI下子
程序开始初始化:
main () {
for (i=1;i<226;i++){ pn[i]=0; } //初始化pn
isDo=1; //游戏操作控制:1 =可下子,0=不可下子(结束标志)
s6="黑棋选子"; //s6 $显示状态,游戏开始,黑先手
px=320 ; py=320 ; //黑先手,首子定标天元位,标记坐标
mode=0 ; s7="游戏模式:双人对奕"; //游戏预设定双人对弈模式
board (); //绘出棋板,显示起始状态
}
绘制棋盘,下子更新,显示记录,游戏界面布局。此布局是手机屏幕竖版式,电脑版可用横版式。
绘制方法很简单,就是画方形和圆形,空心的实心的图形。
画按钮用画直线方法显示渐变色彩,增加艺术性。
选子屏幕显示效果,选定棋子标记,下子后状态显示,都要更新屏幕输出。下子后即时要判断胜负。
board (){
// 画 棋盘方格,中元,四点星 位,标记
cs.SetFillMode (1);//0不填色,1填色
cs.SetColor (255,250,250,250); //backcolor 背景色
cs.DrawRect (0,0,650,850);
cs.SetColor (255,110,110,110); //棋盘区域
cs.DrawRect (10,10,637,638);
cs.SetColor (255,220,150,50); //棋盘外框
cs.DrawRect (8,8,632,632);
cs.SetColor (255,250,180,100); //棋盘底板
cs.DrawRect (20,20,620,620);
cs.SetFillMode (0);//0不填色,1填色
for (i=1;i<15;i++){ //画出棋盘方格,有些立体感
for (j=1;j<15;j++){
cs.SetColor (255,150,150,150); //grid color
cs.DrawRect(i*40-1,j*40-1,i*40+39,j*40+39);
cs.SetColor (255,250,250,250); //grid color
cs.DrawRect(i*40,j*40,i*40+40,j*40+40);
} }
cs.SetColor (255,0,0,0); // 棋盘黑色框线
cs.DrawRect (21,21,620,620);
cs.DrawRect (20,20,620,620);
//draw flag points 画中元四星
cs.SetFillMode (1);//0不填色,1填色
cs.SetColor (255,240,240,240); //
cs.DrawCircle(160,160,5); //星
cs.DrawCircle(480,160,5);
cs.DrawCircle(160,480,5);
cs.DrawCircle(480,480,5);
cs.DrawCircle(320,320,5); //中元
//draw button 绘制按钮
cs.SetColor (255,250,250,250);
cs.DrawRect (100,900,301,960);
cs.DrawRect (400,900,601,960);
cs.SetColor (255,250,0,0);
for (i=1;i<60;i++){ //按钮渐变色
cs.SetColor (255,240-i*4,240-i*3,250-i*4);
cs.DrawLine (101,900+i,300,900+i);
cs.DrawLine (401,900+i,600,900+i);
}
//为了显示界面的艺术性绘制按钮图
cs.SetColor (255,0,120,0); //画按钮黑子图形
cs.DrawCircle (141,931,15);
cs.SetColor (255,0,0,0);
cs.DrawCircle (140,930,15);
cs.SetColor (255,220,220,220);
cs.DrawCircle (135,925,2);
cs.SetColor (255,0,120,0); //画按钮白子图形
cs.DrawCircle (442,932,15);
cs.SetColor (255,220,220,220);
cs.DrawCircle (440,930,15);
cs.SetColor (255,250,250,250);
cs.DrawCircle (435,925,2);
cs.SetColor(255,255,250,0);
cs.SetTextStyle (1);
cs.SetTextSize (32);
cs.DrawText ("黑子下",178,940); //按钮文字
cs.DrawText ("白子下",478,940);
//此处画出已下黑子白子
for (i=1;i<226;i++){
dx=(i-(i/15*15))*40;
dy=(i/15)*40+40;
if (dx==0){ dx=15*40; dy=dy-40; }
if (pn[i]==2){ //white 画白子
cs.SetColor (255,180,80,40); //piecesW
cs.DrawCircle(dx+2,dy+2,15);
cs.SetColor (255,220,220,220); //pieceW
cs.DrawCircle(dx,dy,15);
cs.SetColor (255,250,250,250); //pieceW
cs.DrawCircle(dx-5,dy-5,2); }
if (pn[i]==1){ //black 画黑子
cs.SetColor (255,220,100,40); //piecesB
cs.DrawCircle(dx+1,dy+1,15);
cs.SetColor (255,0,0,0); //pieceB
cs.DrawCircle(dx,dy,15);
cs.SetColor (255,200,200,200); //pieceB
cs.DrawCircle(dx-5,dy-5,2); }
} //draw chess pieces
cs.SetColor (255,240,0,0); //画刚落子标记红点
cs.DrawCircle(px,py,5);
//提示选子
cs.SetTextSize (42);
cs.SetColor(255,255,120,250);
cs.DrawRect (390,670,600,740);
cs.SetColor(255,255,250,0);
cs.DrawText (s6,415,720); //提示选子编号pn
cs.SetTextSize (36);
if (mode==0) cs.SetColor(255,205,200,80); //双人
if (mode==1) cs.SetColor(255,250,150,0); //人机
cs.DrawText (s7,20,680); //mode 显示当前游戏模式
cs.SetTextStyle (0);
cs.SetTextSize (16);
cs.SetColor(255,255,250,250);
cs.DrawText (cordp,665,dn*16); //界面显示记录
cs.Update (); //画布更新
detect (); //判断胜负
}//board ()
以上是游戏最重要部分的棋盘绘制
为防止落子误操作,选子后落子加缓冲按钮
cmdB (){ //黑棋下子 按钮
if (isDo==1){ //可下子
if (mode==0) black_do (); //双人对弈
if (mode==1) { //人机模式
black_do (); //人机,黑棋下子
chessAI (); //人机,机下子
// testAI (); 此处是机器智能下子设计检测调试
}
} //黑棋已下提示白棋选子
}
cmdW (){ //白棋下子 按钮
if (isDo==1){ //可下子
white_do ();
} //白棋已下提示黑棋选子
黑棋白棋走子,打印记录,绘出屏幕显示走子状态
testAI (); 此处是机器智能下子设计检测调试
black_do (){ //黑棋下子
pn[n]=1;
s6="白棋选子";
cordp=" B "+intToString (n); //走子记录
if (cordp != ss2) { //保证文本只打印一次
dn=dn+1; print dn; //打印走子序号
print cordp; ss2=cordp; } //打印走子记录文本
board (); //更新输出
testAI (); //此处研究测试智能下子,机器下子位标记黄点
}//black_do ()
white_do (){ //白棋下子
pn[n]=2;
s6="黑棋选子";
cordp="W "+intToString (n); //走子记录
if (cordp != ss2) {
dn=dn+1; print dn;
print cordp; ss2=cordp; }
}//white_do ()
下子后立即判断胜负
下面的算法是15行15列一次遍历搜索查找五连子
这方法有下标越界问题,12列起右搜索会搜到下行,四个方向搜索都有此问题。
但在实际下棋时不大会出错,我把下标加大了。
这个方法有个优点是,按行搜索和按列搜索都一样。
也可按棋子编码pn (1-225)遍历搜索。
detect (){
//detect Black chess 搜索黑棋判胜 ******
for (i=1;i<16;i++){
for (j=1;j<16;j++){
B=(i-1)*15+j ; //pn(B) number 黑子
//********
if (pn[B]==1) { //B 黑子
k=B; //替代一下用于计算,k打字方便
if ( pn[k+1]==1&&pn[k+2]==1&&pn[k+3]==1&&pn[k+4]==1){ goto showwin ; } //右
if ( pn[k+15]==1&&pn[k+30]==1&&pn[k+45]==1&&pn[k+60]==1){ goto showwin ; } //下
if ( pn[k+16]==1&&pn[k+32]==1&&pn[k+48]==1&&pn[k+64]==1){ goto showwin ; } //右斜
if ( pn[k+14]==1&&pn[k+28]==1&&pn[k+42]==1&&pn[k+56]==1){ goto showwin ; } //斜左
} //pn(B)
} } //next i, j
//detect White chess 搜索白棋判胜 ******
for (i=1;i<16;i++){
for (j=1;j<16;j++){
W=(i-1)*15+j ; //pn(W) number 白子
//********
if (pn[W]==2){
k=W; //替代一下用于计算
if ( pn[k+1]==2&&pn[k+2]==2&&pn[k+3]==2&&pn[k+4]==2){ goto showwin ; } //右
if ( pn[k+15]==2&&pn[k+30]==2&&pn[k+45]==2&&pn[k+60]==2){ goto showwin ; } //下
if ( pn[k+16]==2&&pn[k+32]==2&&pn[k+48]==2&&pn[k+64]==2){ goto showwin ; } //右斜
if ( pn[k+14]==2&&pn[k+28]==2&&pn[k+42]==2&&pn[k+56]==2){ goto showwin ; } //斜左
} //pn (W)
} } //next i, j
//*****************************
if (dn>100) { //暂定和棋
ss="( 和 棋 )"; goto showwin; }
//和棋是双方都无子可下,棋盘上有空格但没有好的走子点位也是和棋局
//一般来说,15行列棋盘60子内就分出胜负了,80子后棋子拥堵就没好点位了,高手对弈100子也分出胜负了吧
//故暂定100子后攻防双方都没好的下子点位,就算和棋吧
return;
//show win flag 如有五连, 屏幕判胜负显示
showwin:
cs.SetTextSize (60);
if (pn[k]==1) { ss="黑棋胜 ! !"; print ss; }
if (pn[k]==2) { ss="白棋胜 ! !"; print ss; }
cs.SetTextStyle (1);
cs.SetFillMode (1);//0不填色,1填色
cs.SetColor(255,180,90,40);
cs.DrawText (ss,204,504);
cs.SetColor(255,255,0,0);
cs.DrawText (ss,200,500);
cs.SetFillMode (0);//0不填色,1填色
cs.SetColor(255,250,250,0);
cs.DrawText (ss,200,500);
cs.Update ();
isDo=0; //判胜后设置结束标志,锁定棋盘
}//detect ()
正确的遍历搜索是:
向右搜索:i = 1-15 行 , j = 1-11 列
向下搜索:i = 1- 11行 , j = 1-15列
向右斜搜索:i = 1-11行 , j = 1-11列
向斜左搜索:i = 1-11行 , j = 5-15列
这样的四个方向搜索下标就不会出界越界。
部分代码如下所示:
for (i=1;i<=15;i++){ // B 黑子
for (j=1;j<=11;j++){
k=(i-1)*15+j ; //pn(B) number
if (pn[k]==1){
if ( pn[k+1]==1&&pn[k+2]==1&&pn[k+3]==1&&pn[k+4]==1){ goto showwin ; } //黑子向右搜索五连
} } }
开始游戏,重新开始游戏设置
gamestart (){
for (i=1;i<226;i++){ pn[i]=0; } //下子编码清空
setDisplay (1); //若关闭画布查看记录,则重新显示棋盘
dn=0; //计数清零
clearOutput(); //清除打印记录 print
cs.SetBackground(200,200,200); //清除界面下子记录
isDo=1; //可下子标志
px=320 ; py=320 ; //设置天元位
board (); //绘出棋盘
}//gamestart ()
程序悔棋设置,设置在按钮项,可选子连续清除,也可改为只悔刚下的棋子
n 是刚下子的编码,也可是另外选子的编码,pn 的编码设置为 0 ,即可清除
undo ( ) {//悔棋,可选子连续清除
if (isDo==1) {
pn[n]=0; //刚下子或选子的编码置0
print "悔 "+intToString (n); //记录中打印悔棋标记
board (); //更新输出
}
}
chessAI (){ //人机模式 AI下子 }
AI智能下子是五子棋游戏的重中之重,网上有许多论述,都很精彩。我个人认为分析下子权重计分来定下子位的方法不是最好的智能方法。只可算权宜之法。
电脑游戏有日版的游戏,机器人工智能水平较高。
我正在研究测试智能人机模式,还没达到精深程度,不敢拿出来献丑。敬请大家原谅,以后做出好的东西再和大家共享。
我在黑棋下子的 black_do () 中加了测试方法,下子后调用 testAI 查看机器下子位
的定位点,如:
testAI (); //此处研究测试智能下子,机器下子位标记黄点
}//black_do ()
些许经验分享,不足为大方达观之家笑耳。
(End)