《C语言程序设计---五子棋 (1).doc》由会员分享,可在线阅读,更多相关《C语言程序设计---五子棋 (1).doc(22页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、 五子棋算法:任何一种棋类游戏其关键是对当前棋局是否有正确的评分,评分越准确则电脑的AI越高。五子棋游戏也是如此,但在打分之前,我们先扫描整个棋盘,把每个空位从八个方向上的棋型填入数组gStyle(2, 15, 15, 8, 2),其中第一个下标为1时表示黑棋,为2时表示白棋,第二和第三个下标表示(x,y),第四个下标表示8个方向,最后一个下标为1时表示棋子数,为2时表示空格数,如: gStyle(1,2,2,1,1)=3表示与坐标(2,2)在第1个方向上相邻的黑棋棋子数为3gstyle(1,2,2,1,2)=4表示与坐标(2,2)在第1个方向上的最近的空格数为4在定义方向时,也应该注意一定的
2、技巧,表示两个相反的方向的数应该差4,在程序中我是这样定义的:Const DIR_UP = 1Const DIR_UPRIGHT = 2Const DIR_RIGHT = 3Const DIR_RIGHTDOWN = 4Const DIR_DOWN = 5Const DIR_DOWNLEFT = 6Const DIR_LEFT = 7Const DIR_LEFTUP = 8这样我们前四个方向可以通过加四得到另一个方向的值。如果你还是不太明白,请看下面的图:-oo-ox*xx-图中的*点从标为(4,4),(打*的位置是空位),则:gStyle(2,4,4,1,1)=1在(4,4)点相邻的上方白棋
3、数为1gStyle(2,4,4,1,2)=2在(4,4)点的上方距上方白棋最近的空格数为2gStyle(1,4,4,3,1)=2在(4,4)点相邻的右方黑棋数为2gStyle(1,4,4,3,2)=1在(4,4)点的右方距右方黑棋最近的空格数为3.一旦把所有空点的棋型值填完,我们很容易地得出黑棋水平方向上点(4,4)的价值,由一个冲1(我把有界的棋称为冲)和活2(两边无界的棋称为活)组成的。对于而白棋在垂直方向上点(4,4)的价值是一个活1,而在/方向也是活1所以,只要我们把该点的对于黑棋和白棋的价值算出来,然后我们就取棋盘上各个空点的这两个值的和的最大一点作为下棋的点。然而,对各种棋型应该取
4、什么值呢?我们可以先作如下假设: Fn 表示先手n个棋子的活棋型,如:F4表示先手活四 Fn表示先手n个棋子的冲棋型,如:F4表示先手冲四 Ln 表示后手n个棋子的活棋型,如:L3表示后手活三 Ln表示后手n个棋子的冲棋型,如:L3表示后手冲三 . . . 根据在一行中的棋型分析,得到如下关系:L1=F1L2=F2=L1F1L2F2L3=F3L4F4=F4 从这个关系包含了进攻和防守的关系(当然,这个关系是由我定的,你可以自己定义这些关系)。对这些关系再进一步细化,如在一个可下棋的点,其四个方向上都有活三,也比不上一个冲四,所以我们可以又得到4*F3L4这个关系,同样,我们还可以得到其它的关系
5、,如:4*F2L3、4*L3F3.,这些的关系由于你的定法和我的定法制可能不一样,这样计算机的AI也就不一样,最后我们把分值最小的L1值定为1,则我们就得到了下面各种棋型的分值,由C语言表示为:F25=0,2,5,50,16000,0,10,30,750,16000;L25=0,1,5,50,3750,0,10,30,150,4000; F数组表示先手,第一个下标为0时表示冲型,第二个下标表示棋子数,则F2对应F02L数组表示后手,第一个下标为0时表示冲型,第二个下标表示棋子数,则L2对应F12Ok,棋型的分值关系确定好了以后,我们把每一个可下点的四个方向的棋型值相加(包括先手和后手的分值),
6、最后选择一个最大值,并把这一点作为计算机要下的点就OK了:)。后话:1、得到最大值也许不止一个点,但在我的程序中只选择第一个最大点,当然你可以用于个随机数来决定选择那一个最大值点,也可以对这些最大值点再作进一步的分析。2、在这个算法中我只考虑了周围有棋子的点,而其它点我没有考虑。3、可以再更进一步,用这个算法来预测以后的几步棋,再选择预测值最好的一步,这样电脑的AI就更高了4、这个算法没有考虑黑棋的禁手(双3、双四和多于五子的连棋)。因为在平时我下的五子棋是没有这些禁手的。 /*/* 本程序在Turbo C或Borland C下编译通过 */*五子棋 双人对战程序 */* */*/*/* 程序
7、中用到的库函数所在头文件应用 #include 命令包含进来 */#include #include #include #include #include /*/* 定义符号常量 */*定义画棋盘所需的制表符*/#define CROSSRU 0xbf /*右上角点*/#define CROSSLU 0xda /*左上角点*/#define CROSSLD 0xc0 /*左下角点*/#define CROSSRD 0xd9 /*右下角点*/#define CROSSL 0xc3 /*左边*/#define CROSSR 0xb4 /*右边*/#define CROSSU 0xc2 /*上边*/
8、#define CROSSD 0xc1 /*下边*/#define CROSS 0xc5 /*十字交叉点*/*定义棋盘左上角点在屏幕上的位置*/#define MAPXOFT 5#define MAPYOFT 2/*定义1号玩家的操作键键码*/#define PLAY1UP 0x1157/*上移-W*/#define PLAY1DOWN 0x1f53/*下移-S*/#define PLAY1LEFT 0x1e41/*左移-A*/#define PLAY1RIGHT 0x2044/*右移-D*/#define PLAY1DO 0x3920/*落子-空格键*/*定义2号玩家的操作键键码*/#def
9、ine PLAY2UP 0x4800/*上移-方向键up*/#define PLAY2DOWN 0x5000/*下移-方向键down*/#define PLAY2LEFT 0x4b00/*左移-方向键left*/#define PLAY2RIGHT 0x4d00/*右移-方向键right*/#define PLAY2DO 0x1c0d/*落子-回车键Enter*/*若想在游戏中途退出, 可按 Esc 键*/#define ESCAPE 0x011b/*定义棋盘上交叉点的状态, 即该点有无棋子 */*若有棋子, 还应能指出是哪个玩家的棋子 */#define CHESSNULL 0 /*没有棋子
10、*/#define CHESS1 O/*一号玩家的棋子*/#define CHESS2 X/*二号玩家的棋子*/*定义按键类别*/#define KEYEXIT 0/*退出键*/#define KEYFALLCHESS 1/*落子键*/#define KEYMOVECURSOR 2/*光标移动键*/#define KEYINVALID 3/*无效键*/*定义符号常量: 真, 假 - 真为1, 假为0 */#define TRUE 1#define FALSE 0/*/* 定义数据结构 */*棋盘交叉点坐标的数据结构*/struct point int x,y;/*/*自定义函数原型说明 */v
11、oid Init(void);int GetKey(void);int CheckKey(int press);int ChangeOrder(void);int ChessGo(int Order,struct point Cursor);void DoError(void);void DoOK(void);void DoWin(int Order);void MoveCursor(int Order,int press);void DrawCross(int x,int y);void DrawMap(void);int JudgeWin(int Order,struct point Cu
12、rsor);int JudgeWinLine(int Order,struct point Cursor,int direction);void ShowOrderMsg(int Order);void EndGame(void);/*/*/* 定义全局变量 */int gPlayOrder; /*指示当前行棋方 */struct point gCursor; /*光标在棋盘上的位置 */char gChessBoard1919;/*用于记录棋盘上各点的状态*/*/*/*主函数*/void main() int press; int bOutWhile=FALSE;/*退出循环标志*/ Ini
13、t();/*初始化图象,数据*/ while(1) press=GetKey();/*获取用户的按键值*/ switch(CheckKey(press)/*判断按键类别*/ /*是退出键*/ case KEYEXIT: clrscr();/*清屏*/ bOutWhile = TRUE; break; /*是落子键*/ case KEYFALLCHESS: if(ChessGo(gPlayOrder,gCursor)=FALSE)/*走棋*/ DoError();/*落子错误*/ else DoOK();/*落子正确*/ /*如果当前行棋方赢棋*/ if(JudgeWin(gPlayOrder,
14、gCursor)=TRUE) DoWin(gPlayOrder); bOutWhile = TRUE;/*退出循环标志置为真*/ /*否则*/ else /*交换行棋方*/ ChangeOrder(); ShowOrderMsg(gPlayOrder); break; /*是光标移动键*/ case KEYMOVECURSOR: MoveCursor(gPlayOrder,press); break; /*是无效键*/ case KEYINVALID: break; if(bOutWhile=TRUE) break; /*游戏结束*/ EndGame();/*/*界面初始化,数据初始化*/vo
15、id Init(void) int i,j; char *Msg= Player1 key:, UP-w, DOWN-s, LEFT-a, RIGHT-d, DO-space, , Player2 key:, UP-up, DOWN-down, LEFT-left, RIGHT-right, DO-ENTER, , exit game:, ESC, NULL, ; /* 先手方为1号玩家 */ gPlayOrder = CHESS1; /* 棋盘数据清零, 即棋盘上各点开始的时候都没有棋子 */ for(i=0;i19;i+) for(j=0;j19;j+) gChessBoardij=CHE
16、SSNULL; /*光标初始位置*/ gCursor.x=gCursor.y=0; /*画棋盘*/ textmode(C40); DrawMap(); /*显示操作键说明*/ i=0; textcolor(BROWN); while(Msgi!=NULL) gotoxy(25,3+i); cputs(Msgi); i+; /*显示当前行棋方*/ ShowOrderMsg(gPlayOrder); /*光标移至棋盘的左上角点处*/ gotoxy(gCursor.x+MAPXOFT,gCursor.y+MAPYOFT);/*画棋盘*/void DrawMap(void) int i,j; clrs
17、cr(); for(i=0;i19;i+) for(j=0;j19;j+) DrawCross(i,j);/*画棋盘上的交叉点*/void DrawCross(int x,int y) gotoxy(x+MAPXOFT,y+MAPYOFT); /*交叉点上是一号玩家的棋子*/ if(gChessBoardxy=CHESS1) textcolor(LIGHTBLUE); putch(CHESS1); return; /*交叉点上是二号玩家的棋子*/ if(gChessBoardxy=CHESS2) textcolor(LIGHTBLUE); putch(CHESS2); return; text
18、color(GREEN); /*左上角交叉点*/ if(x=0&y=0) putch(CROSSLU); return; /*左下角交叉点*/ if(x=0&y=18) putch(CROSSLD); return; /*右上角交叉点*/ if(x=18&y=0) putch(CROSSRU); return; /*右下角交叉点*/ if(x=18&y=18) putch(CROSSRD); return; /*左边界交叉点*/ if(x=0) putch(CROSSL); return; /*右边界交叉点*/ if(x=18) putch(CROSSR); return; /*上边界交叉点*
19、/ if(y=0) putch(CROSSU); return; /*下边界交叉点*/ if(y=18) putch(CROSSD); return; /*棋盘中间的交叉点*/ putch(CROSS);/*交换行棋方*/int ChangeOrder(void) if(gPlayOrder=CHESS1) gPlayOrder=CHESS2; else gPlayOrder=CHESS1; return(gPlayOrder);/*获取按键值*/int GetKey(void) char lowbyte; int press; while (bioskey(1) = 0) ;/*如果用户没有
20、按键,空循环*/ press=bioskey(0); lowbyte=press&0xff; press=press&0xff00 + toupper(lowbyte); return(press);/*落子错误处理*/void DoError(void) sound(1200); delay(50); nosound();/*赢棋处理*/void DoWin(int Order) sound(1500);delay(100); sound(0); delay(50); sound(800); delay(100); sound(0); delay(50); sound(1500);delay
21、(100); sound(0); delay(50); sound(800); delay(100); sound(0); delay(50); nosound(); textcolor(RED+BLINK); gotoxy(25,20); if(Order=CHESS1) cputs(PLAYER1 WIN!); else cputs(PLAYER2 WIN!); gotoxy(25,21); cputs( /); getch();/*走棋*/int ChessGo(int Order,struct point Cursor) /*判断交叉点上有无棋子*/ if(gChessBoardCur
22、sor.xCursor.y=CHESSNULL) /*若没有棋子, 则可以落子*/ gotoxy(Cursor.x+MAPXOFT,Cursor.y+MAPYOFT); textcolor(LIGHTBLUE); putch(Order); gotoxy(Cursor.x+MAPXOFT,Cursor.y+MAPYOFT); gChessBoardCursor.xCursor.y=Order; return TRUE; else return FALSE;/*判断当前行棋方落子后是否赢棋*/int JudgeWin(int Order,struct point Cursor) int i; f
23、or(i=0;i4;i+) /*判断在指定方向上是否有连续5个行棋方的棋子*/ if(JudgeWinLine(Order,Cursor,i) return TRUE; return FALSE;/*判断在指定方向上是否有连续5个行棋方的棋子*/int JudgeWinLine(int Order,struct point Cursor,int direction) int i; struct point pos,dpos; const int testnum = 5; int count; switch(direction) case 0:/*在水平方向*/ pos.x=Cursor.x-(
24、testnum-1); pos.y=Cursor.y; dpos.x=1; dpos.y=0; break; case 1:/*在垂直方向*/ pos.x=Cursor.x; pos.y=Cursor.y-(testnum-1); dpos.x=0; dpos.y=1; break; case 2:/*在左下至右上的斜方向*/ pos.x=Cursor.x-(testnum-1); pos.y=Cursor.y+(testnum-1); dpos.x=1; dpos.y=-1; break; case 3:/*在左上至右下的斜方向*/ pos.x=Cursor.x-(testnum-1); p
25、os.y=Cursor.y-(testnum-1); dpos.x=1; dpos.y=1; break; count=0; for(i=0;itestnum*2+1;i+)/*?i=0&pos.x=0&pos.y=testnum) return TRUE; else count=0; pos.x+=dpos.x; pos.y+=dpos.y; return FALSE;/*移动光标*/void MoveCursor(int Order,int press) switch(press) case PLAY1UP: if(Order=CHESS1&gCursor.y0) gCursor.y-;
26、break; case PLAY1DOWN: if(Order=CHESS1&gCursor.y0) gCursor.x-; break; case PLAY1RIGHT: if(Order=CHESS1&gCursor.x0) gCursor.y-; break; case PLAY2DOWN: if(Order=CHESS2&gCursor.y0) gCursor.x-; break; case PLAY2RIGHT: if(Order=CHESS2&gCursor.x18) gCursor.x+; break; gotoxy(gCursor.x+MAPXOFT,gCursor.y+MAP
27、YOFT);/*游戏结束处理*/void EndGame(void) textmode(C80);/*显示当前行棋方*/void ShowOrderMsg(int Order) gotoxy(6,MAPYOFT+20); textcolor(LIGHTRED); if(Order=CHESS1) cputs(Player1 go!); else cputs(Player2 go!); gotoxy(gCursor.x+MAPXOFT,gCursor.y+MAPYOFT);/*落子正确处理*/void DoOK(void) sound(500); delay(70); sound(600); d
28、elay(50); sound(1000); delay(100); nosound();/*检查用户的按键类别*/int CheckKey(int press) if(press=ESCAPE) return KEYEXIT;/*是退出键*/ else if ( ( press=PLAY1DO & gPlayOrder=CHESS1) | ( press=PLAY2DO & gPlayOrder=CHESS2) ) return KEYFALLCHESS;/*是落子键*/ else if ( press=PLAY1UP | press=PLAY1DOWN | press=PLAY1LEFT | press=PLAY1RIGHT | press=PLAY2UP | press=PLAY2DOWN | press=PLAY2LEFT | press=PLAY2RIGHT ) return KEYMOVECURSOR;/*是光标移动键*/ else return KEYINVALID;/*按键无效*/