失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Visual C++实现黑白棋游戏实战三:核心算法设计与实现(附源码和资源 可用于大作业)

Visual C++实现黑白棋游戏实战三:核心算法设计与实现(附源码和资源 可用于大作业)

时间:2022-05-19 15:23:39

相关推荐

Visual C++实现黑白棋游戏实战三:核心算法设计与实现(附源码和资源 可用于大作业)

需要源码和资源请点赞关注收藏后评论区留言私信~~~

在前面的博客中已经讲解了黑白棋游戏的菜单和各种对话框的实现,下面将对黑白棋游戏的核心算法的设计和实现进行讲解

一、棋盘窗口类的设计

黑白棋的棋盘窗口类,主要负责显示游戏中的棋盘,棋子和棋子个数,同时还要管理绘图,输入以及输出等内容,其主要有如下几个模块

1:新游戏处理模块

开始新游戏处理模块,主要是把棋盘数据进行初始化,并让棋盘窗口进行重绘

2:鼠标输入处理模块

利用截取窗口上鼠标输入的信息,得到当前玩家使用鼠标左键落子的坐标,将坐标进行重新转换得到棋盘数组中的序号,把当前棋子颜色保存到该数组中

3:延时处理模块

得到当前系统的时钟计时

做一个消息循环,有API函数组成

每循环以此,得到当前系统的时钟计时,并于延时相加得到超时时间

当超时时间到达时,退出循环,延时成功

4:悔棋处理模块

实现悔棋处理模块,需要通过如下几个步骤

判断当前落子步数,如果小于2,说明游戏还没有开始,不能悔棋

重新初始化当前棋盘数组,并把落子步骤数组的数据复制出来

根据当前落子步骤数组的数据,将棋盘的棋子位置进行恢复

5:绘图处理模块

绘图处理模块主要通过遍历当前棋盘数组中的数据,根据每一行或者列的棋子类型的不同,进行相应的棋子绘图即可

下面是棋盘窗口类的实现

棋盘窗口类CChessBoard的实现,可以分为如下几个步骤

首先声明棋盘窗口类,包括构造函数,析构函数,悔棋函数,翻转动画函数,绘图函数以及鼠标输入处理函数等等,代码如下

#ifndef __CHESS_BOARD_H__#define __CHESS_BOARD_H__#include "DataStruct.h"#define COL_WIDTH 45#define ROW_WIDTH 45class CChessBoard : public CWnd{private:CBitmap m_bitBlackChess, m_bitWhiteChess;CBitmap m_bitChessBoard;CBitmap m_motive[8]; intm_iMotiveNumber;boolm_bPlayMotive;intm_iMotivex, m_iMotivey;// Constructionpublic:board_type m_oChessBoard;CChessBoard();public:void NewGame();void MoveBack();void PlayMotive(int row, int col, UINT8 obcolor);public:virtual BOOL Create(RECT &rect, CWnd * pParentWnd, UINT nID);public:virtual ~CChessBoard();protected://{{AFX_MSG(CChessBoard)afx_msg void OnPaint();afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);//}}AFX_MSGafx_msg void OnComRun(WPARAM wParam, LPARAM lParam);afx_msg void OnTranChess(WPARAM wParam, LPARAM lParam);DECLARE_MESSAGE_MAP()};#endif

实现棋盘窗口类的构造函数,析构函数,窗口建立函数,绘图函数等等,实现类代码如下

// ChessBoard1.cpp : implementation file//#include "stdafx.h"#include "ChessBoard.h"#include "resource.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endifUINT8 g_bStart = 0;/// CChessBoardCChessBoard::CChessBoard(){m_iMotiveNumber=0;m_iMotivex = m_iMotivey=0;m_bPlayMotive = FALSE; init_board(&m_oChessBoard);}CChessBoard::~CChessBoard(){}BEGIN_MESSAGE_MAP(CChessBoard, CWnd)//{{AFX_MSG_MAP(CChessBoard)ON_WM_PAINT()ON_WM_LBUTTONDOWN()ON_WM_CREATE()//}}AFX_MSG_MAPON_MESSAGE(UM_COMRUN, OnComRun)ON_MESSAGE(WM_TRANCHESS, OnTranChess)END_MESSAGE_MAP()////延时函数//void delay(INT32 millisecond){clock_t start = clock();do{ MSG msg;if (::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) { if ( !AfxGetApp()->PumpMessage()) { ::PostQuitMessage(0); return;} } }while(clock()<start+millisecond);}////悔棋函数//void CChessBoard::MoveBack(){if(cur_step<2){//如果当前步骤下于2,说明没有开始游戏return;}UINT8 comside = computer_side;UINT8 step = cur_step;INT16 movearray[64];//把下棋步骤数组中的数据复制到移动数组中memcpy(movearray, step_array, 64*sizeof(INT16));init_board(&m_oChessBoard);computer_side = comside;UINT8 col= CHESS_BLACK;for(int i=0; i<step-2; i++, col = ~col & 3){do_move_chess(&m_oChessBoard, movearray[i], col, 0);}OnPaint();Invalidate(); }////改变棋子接口函数//void CChessBoard::OnTranChess(WPARAM wParam, LPARAM lParam){int row = wParam/10-1;int col = wParam%10-1;CRect r(col*COL_WIDTH+22, row*ROW_WIDTH+22,col*COL_WIDTH+COL_WIDTH+22, row*ROW_WIDTH+ROW_WIDTH+22);m_bPlayMotive = FALSE; OnPaint(); InvalidateRect(&r);if((lParam>>16) !=0)PlayMotive(row, col, UINT8(lParam));}////由电脑人工智能下棋//void CChessBoard::OnComRun(WPARAM wParam, LPARAM lParam){computer_play(&m_oChessBoard, m_hWnd); UINT16 wscore, bscore;get_chess_score(&m_oChessBoard, wscore, bscore);GetParent()->SendMessage(UM_RECALC, WPARAM(wscore|0x80000000), LPARAM(bscore)); }////新游戏//void CChessBoard::NewGame(){if(cur_step >0){if(MessageBox("开始新游戏吗?", "黑白棋", MB_YESNO|MB_ICONQUESTION) == IDYES){g_bStart = 1;init_board(&m_oChessBoard);Invalidate(); }}}////窗口建立函数//BOOL CChessBoard::Create(RECT &rect, CWnd *pParentWnd, UINT nID){CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS|CS_HREDRAW|CS_VREDRAW,0, (HBRUSH)CBrush(RGB(0,0,255)), 0);rect.right = rect.left + 380+3;rect.bottom = rect.top +380+3;if(!CWnd::CreateEx(WS_EX_CLIENTEDGE, szClassName, _T(""),WS_CHILD|WS_VISIBLE|WS_TABSTOP, rect, pParentWnd, nID, NULL)) //WS_EX_CLIENTEDGEreturn FALSE;UpdateWindow();m_bitBlackChess.LoadBitmap(IDB_BLACKCHESS);m_bitChessBoard.LoadBitmap(IDB_CHESSBOARD);m_bitWhiteChess.LoadBitmap(IDB_WHITECHESS);m_motive[0].LoadBitmap(IDB_WHITECHESS); m_motive[1].LoadBitmap(IDB_TURN1);m_motive[2].LoadBitmap(IDB_TURN2); m_motive[3].LoadBitmap(IDB_TURN3); m_motive[4].LoadBitmap(IDB_TURN4); m_motive[5].LoadBitmap(IDB_TURN5); m_motive[6].LoadBitmap(IDB_TURN6); m_motive[7].LoadBitmap(IDB_BLACKCHESS); return TRUE;}////播放棋子翻动动画//void CChessBoard::PlayMotive(int row, int col, UINT8 obcolor){m_iMotivex = col*COL_WIDTH+24;m_iMotivey = row*COL_WIDTH+24;CRect r(m_iMotivex, m_iMotivey, m_iMotivex+COL_WIDTH, m_iMotivey +ROW_WIDTH);m_bPlayMotive = TRUE; if(obcolor == CHESS_BLACK){//把棋子从白面向黑面翻转for(m_iMotiveNumber =0; m_iMotiveNumber<8; m_iMotiveNumber++){OnPaint();InvalidateRect(&r); delay(50);}}else{//把棋子从黑面向白面翻转for(m_iMotiveNumber =7; m_iMotiveNumber>=0; m_iMotiveNumber--){OnPaint();InvalidateRect(&r); delay(50);}}m_bPlayMotive = FALSE;}////窗口绘图函数//void CChessBoard::OnPaint() {CPaintDC dc(this); CDC imgdc;imgdc.CreateCompatibleDC(&dc);imgdc.SelectObject(&m_bitChessBoard); dc.BitBlt(0, 0, 380, 380, &imgdc,0,0,SRCCOPY); if(m_bPlayMotive){imgdc.SelectObject(&m_motive[m_iMotiveNumber]); dc.BitBlt(m_iMotivex, m_iMotivey, 39, 39, &imgdc, 0, 0, SRCCOPY);return;}for(int i=0; i<BOARD_ROWS; i++){for(int j=0; j<BOARD_COLS; j++){if(m_oChessBoard.board[i+1][j+1] == CHESS_BLACK){imgdc.SelectObject(&m_bitBlackChess); dc.BitBlt(j*COL_WIDTH+24, i*ROW_WIDTH+24, 39, 39, &imgdc,0,0,SRCCOPY);}else if(m_oChessBoard.board[i+1][j+1] == CHESS_WHITE){imgdc.SelectObject(&m_bitWhiteChess); dc.BitBlt(j*COL_WIDTH+24, i*ROW_WIDTH+24, 39, 39, &imgdc,0,0,SRCCOPY);}}}}////鼠标左键响应函数//void CChessBoard::OnLButtonDown(UINT nFlags, CPoint point) {BYTE row = (point.y-22)/ROW_WIDTH+1;BYTE col = (point.x-22)/COL_WIDTH+1;if(do_move_chess(&m_oChessBoard, row*10+col, ~computer_side&3, m_hWnd)){UINT16 wscore, bscore;get_chess_score(&m_oChessBoard, wscore, bscore);GetParent()->SendMessage(UM_RECALC, WPARAM(wscore), LPARAM(bscore)); PostMessage(UM_COMRUN);}else{MessageBeep(MB_OK);}CWnd::OnLButtonDown(nFlags, point);}int CChessBoard::OnCreate(LPCREATESTRUCT lpCreateStruct) {if (CWnd::OnCreate(lpCreateStruct) == -1)return -1;EndWaitCursor();return 0;}

二、人工智能模块的设计

人工智能模块,主要的设计思想是从棋盘的当前状态构造博弈树,到达终局状态时计算状态分,并回归到当前状态,以求出最有利的一步(部分回溯值最大的一个子节点),其要点如下

1:终局状态

棋局已结束

当前颜色已经无处可以下子

博弈树的深度已经到达指定的深度

2:博弈树的构造方法

采用深度优先的方法节省构造过程的内存使用,构造过程使用堆栈控件存储子节点,已完成构造的分支可以抛弃,以达到节省内容的目的,算法简单描述如下

如果当前棋局为终局状态,则返回状态分

从当前棋局的状态出发,找出一个可走的步数,尝试走它,新状态扩展为当前棋局的一个子节点,此子节点作为新的当前状态递归调用

3:构造过程的阿尔法和β裁剪

4:棋局的状态估值函数

此函数量化的方法描述棋局某一状态下某一方棋子的形式,对棋局形式的正确分析直接关系到电脑棋力的高低,考虑黑白棋的规则,棋局结束时,棋盘上哪一方的棋子多则哪一方获胜,比较好的方法是计算棋盘上所有不可能被对方吃掉的棋子作为状态分,棋盘上任一个棋子是否能被对方吃掉由其四个方法其他格子的状态决定,这四个方向分别是

左上到右下的斜线

右上到左小的斜线

水平方向

垂直方向

一个棋子如果在这四个方向都受到保护,则认为这个棋子不可能被对方吃掉

5:判断棋子是否受保护

6:状态分的计分方法

不可被吃掉的棋子计四分

三个方向上受保护的棋子计三分

两个方向上受保护的棋子计两分

一个方向上受保护的棋子计一分

7:棋盘的数据结构

每一个格子用一个字节记,低两位记录棋子颜色,高四位记录棋子在四个方向上是否受保护

#ifndef __DATA_STRUCT_H__#define __DATA_STRUCT_H__typedef unsigned char UINT8;typedef unsigned short UINT16; typedef short INT16;typedef char INT8;#define BOARD_COLS 8#define BOARD_ROWS 8typedef struct{UINT8 board[BOARD_ROWS+2][BOARD_COLS+2];}board_type;typedef UINT16 move_key_type; typedef struct{board_type board;UINT16 movepos;INT16 value;}tree_node_type;#define CHESS_NONE0x00#define CHESS_BLACK 0x01#define CHESS_WHITE 0x02#define CHESS_BORDER 0xFF#define BD_PROTECTED 0x80#define FD_PROTECTED 0x40#define H_PROTECTED 0x20#define V_PROTECTED 0x10#define THITHER_COLOR(color) ((~color)&0x03)#define INITIAL_VALUE (32767)#define STEP_MONMENT1 10#define STEP_MONMENT2 48#define LEVEL_LOW 0#define LEVEL_NOR 1#define LEVEL_HIGH 2//游戏难度等级extern UINT8 g_iGameLevel; //游戏难度等级extern UINT8 g_bStart;//游戏开始标志#define USER_PASS1#define COMPUTER_PASS 2#define GAME_OVER4#define WM_TRANCHESS (WM_USER+10)/*可一次吃掉的最多的子的个数*/#define MAX_AFFECTED_PIECES 19extern UINT8 computer_side;extern UINT8 cur_step;extern UINT16 step_array[64];extern INT16 calc_board_status(board_type *board_ptr, UINT8 obcolor);extern void init_board(board_type *board_ptr);extern void computer_play(board_type *board_ptr, HWND hwnd);extern UINT8 do_move_chess(board_type *board_ptr, UINT16 movepos, UINT8 obcolor, HWND hwnd);extern void get_chess_score(board_type *board_ptr, UINT16 &iWscore, UINT16 &iBscore);#endif

人工智能模块的实现如下

包含初始化游戏函数,游戏结束函数,调用电脑下棋接口函数以及得到当前各颜色棋子个数接口函数等等

#include "stdafx.h"#include "dataStruct.h"UINT8 computer_side = CHESS_BLACK;UINT8 max_depth = 4;UINT8 cur_depth = 0;UINT8 cur_step =0;UINT16 step_array[64];UINT8 g_iGameLevel = LEVEL_LOW; //游戏难度等级const UINT8 depth1[]={6, 7, 8};const UINT8 depth2[]={5, 6, 7};/*找出所有在水平方向受保护的obcolor方的棋子,并累计分数*/INT16 scan_horiz_aixes(board_type *board_ptr, UINT8 obcolor){/*扫描8个水平方向*/INT16 score=0;UINT8 *cur_ptr, *stop_ptr;UINT8 piece[4][2];UINT8 count=0, tmpscore;UINT8 bFull;for(UINT8 row=1; row<9; row++){tmpscore = (row == 1 || row == 8) ? 10:2;cur_ptr = &board_ptr->board[row][1];stop_ptr= &board_ptr->board[row][9];bFull = TRUE;count=0;while(cur_ptr < stop_ptr){if(*cur_ptr == obcolor){piece[count][0] = cur_ptr - &board_ptr->board[row][0];while(*cur_ptr == obcolor)cur_ptr ++;piece[count++][1] = cur_ptr - &board_ptr->board[row][0];}if(!*cur_ptr)bFull = FALSE; cur_ptr++;}while(count--){UINT8 nums = (piece[count][1]-piece[count][0]);if(bFull || piece[count][0]==1 || piece[count][1] == 9)score += nums;if(piece[count][0]==1 || piece[count][1] == 9)score += tmpscore;else if(!bFull && (piece[count][0] == 2 || piece[count][1] == 8) && (row == 1 || row == 8))score -= tmpscore;}}return score;}/*找出所有在垂直方向受保护的obcolor方的棋子,并累计分数*/INT16 scan_vertical_aixes(board_type *board_ptr, UINT8 obcolor){INT16 score=0;UINT8 *cur_ptr, *stop_ptr;UINT8 piece[4][2];UINT8 count=0, tmpscore;UINT8 bFull;for(UINT8 col=1; col<9; col++){tmpscore = (col == 1 || col == 8) ? 10:2;cur_ptr = &board_ptr->board[1][col];stop_ptr= &board_ptr->board[9][col];bFull = TRUE;count=0;while(cur_ptr < stop_ptr){if(*cur_ptr == obcolor){piece[count][0] = (cur_ptr - &board_ptr->board[0][col])/10;while(*cur_ptr == obcolor)cur_ptr += 10;piece[count++][1] = (cur_ptr - &board_ptr->board[0][col])/10;}if(!*cur_ptr)bFull = FALSE;cur_ptr += 10;}while(count--){UINT8 nums = (piece[count][1]-piece[count][0]);if(bFull || piece[count][0]==1 || piece[count][1] == 9)score += nums;if(piece[count][0]==1 || piece[count][1] == 9)score += tmpscore;else if(!bFull && (piece[count][0] == 2 || piece[count][1] == 8) && (col == 1 || col == 8))score -= (tmpscore<<1);}}return score;}/*找出所有在右上到左下方向受保护的obcolor方的棋子,并累计分数*/INT16 scan_fd_aixes(board_type *board_ptr, UINT8 obcolor){INT16 score =0;UINT8 *cur_ptr, *stop_ptr, *base_ptr;UINT8 piece[4][2];UINT8 count=0, tmpscore;UINT8 bFull;for(INT8 aixes = -5; aixes <= 5; aixes++){tmpscore = (aixes == 0) ? 10:2;if(aixes <=0){base_ptr = cur_ptr = &board_ptr->board[1][8+aixes];stop_ptr = &board_ptr->board[9+aixes][0];}else{base_ptr = cur_ptr = &board_ptr->board[aixes+1][8];stop_ptr= &board_ptr->board[9][aixes];}bFull = TRUE;count=0;while(cur_ptr < stop_ptr){if(*cur_ptr == obcolor){piece[count][0] = cur_ptr - board_ptr->board[0];while(*cur_ptr == obcolor)cur_ptr += 9;piece[count++][1] = cur_ptr- board_ptr->board[0];}if(!*cur_ptr)bFull = FALSE;cur_ptr += 9;}while(count--){UINT8 nums = (piece[count][1]-piece[count][0])/9;BOOL toborder = (piece[count][0] == base_ptr - board_ptr->board[0] || piece[count][1] == stop_ptr - board_ptr->board[0]);if(bFull || toborder)score += nums;if((aixes == 1 || aixes == -1) && toborder)score -= tmpscore;/*如果是这块棋到达边界*/else if(toborder)score += tmpscore;/*如果有棋在角边上,则扣分*/else if(!bFull && (piece[count][0] == 27 ||piece[count][1] == 81))score -= (tmpscore<<1);}}/*如果角边有棋子,扣分*/if(board_ptr->board[1][1] == obcolor)score += 10;else {if(board_ptr->board[1][2] == obcolor)score -=2;if(board_ptr->board[2][1] == obcolor)score -=2;if(board_ptr->board[2][2]== obcolor)score -=2;}if(board_ptr->board[8][8] == obcolor)score +=10;else{if(board_ptr->board[7][8] == obcolor)score -=2;if(board_ptr->board[8][7]== obcolor)score -=2;if(board_ptr->board[7][7]== obcolor)score -= 2;} return score;}/*找出所有在左上到右下方向受保护的obcolor方的棋子,并累计分数*/INT16 scan_bd_aixes(board_type *board_ptr, UINT8 obcolor){INT16 score =0;UINT8 *cur_ptr, *stop_ptr, *base_ptr;UINT8 piece[4][2];UINT8 count=0, tmpscore;UINT8 bFull;for(INT8 aixes = -5; aixes <= 5; aixes++){tmpscore = (aixes == 0) ? 10:2;if(aixes <=0){base_ptr = cur_ptr = &board_ptr->board[1-aixes][1];stop_ptr = &board_ptr->board[9][9+aixes];}else{base_ptr = cur_ptr = &board_ptr->board[1][aixes+1];stop_ptr= &board_ptr->board[9-aixes][9];}bFull = TRUE;count=0;while(cur_ptr < stop_ptr){if(*cur_ptr == obcolor){piece[count][0] = cur_ptr - board_ptr->board[0];while(*cur_ptr == obcolor)cur_ptr += 11;piece[count++][1] = cur_ptr- board_ptr->board[0];}if(!*cur_ptr)bFull = FALSE;cur_ptr += 11;} while(count--){UINT8 nums = (piece[count][1]-piece[count][0])/11;BOOL toborder = (piece[count][0] == base_ptr - board_ptr->board[0] || piece[count][1] == stop_ptr - board_ptr->board[0]);if(bFull || toborder)score += nums;/*如果角边有棋子,扣分*/if((aixes == 1 || aixes == -1) && toborder)score -= tmpscore;/*如果是这块棋到达边界*/else if(toborder)score += tmpscore;/*如果有棋在角边上,则扣分, 主对角线方向*/else if(!bFull && (piece[count][0] == 22 ||piece[count][1] == 88))score -= (tmpscore<<1);}}/*如果角边有棋子,扣分*/if(board_ptr->board[1][8] == obcolor)score += 10;else {if(board_ptr->board[1][7] == obcolor)score -=2;if(board_ptr->board[2][8] == obcolor)score -=2;if(board_ptr->board[2][7]== obcolor)score -=2;}if(board_ptr->board[8][1] == obcolor)score +=10;else{if(board_ptr->board[7][1] == obcolor)score -=2;if(board_ptr->board[8][2]== obcolor)score -=2;if(board_ptr->board[7][2]== obcolor)score -= 2;} return score;}INT16 sample_calc_board_status(board_type *board_ptr, UINT8 obcolor){INT16 score=0;UINT8 *ptr = &board_ptr->board[1][1];UINT8 *stop = &board_ptr->board[8][9]; UINT8 tmpcol = ~obcolor &0x03;while(ptr<stop){if(*ptr == obcolor)score++;else if(*ptr == tmpcol)score--;ptr++;}return score;}/*计算棋局board_ptr的状态分*/INT16 calc_board_status(board_type *board_ptr, UINT8 obcolor){INT16 score=0;score += scan_horiz_aixes(board_ptr, obcolor);score += scan_vertical_aixes(board_ptr, obcolor);score += scan_bd_aixes(board_ptr, obcolor);score += scan_fd_aixes(board_ptr, obcolor);UINT8 tmpcol = ~obcolor & 0x03 ;if(board_ptr->board[1][1] == tmpcol)score -= 44;if(board_ptr->board[8][8] == tmpcol)score -= 44;if(board_ptr->board[1][8] == tmpcol)score -= 44;if(board_ptr->board[8][1] == tmpcol)score -= 44;return score;}/*从start_pos出发找到一个可下子的点,返回受影响的子的个数,affected_list存放受影响的棋格的指针,第一个指针为落子的点*/const INT16 delta_array[8] = {-11, 11, -9, 9, -1, 1, -10, 10};INT16 find_move(board_type *board_ptr, INT16 start_pos, UINT8 obcolor, INT16 *affected_list){UINT8 *cel_ptr = board_ptr->board[0] + start_pos;UINT8 *stop_ptr = &board_ptr->board[8][9], *p;INT16 *aff_ptr = affected_list+1, *hold_aff;UINT8 aixes;UINT8 thithercolor = THITHER_COLOR(obcolor);while(1){/*找到一个空格子*/while(*cel_ptr) if(++cel_ptr>=stop_ptr)return 0;/*检查在8个方向上是否能吃掉对方的棋子,并记录被吃掉棋子的下标*/for(aixes =0;aixes<8; aixes++){hold_aff = aff_ptr;p = cel_ptr + delta_array[aixes];while(*p == thithercolor){*aff_ptr++ = p - board_ptr->board[0];p+= delta_array[aixes];}if(*p != obcolor)aff_ptr = hold_aff;}/*如果cel_ptr对应的点可以吃掉对方的子*/if(aff_ptr - affected_list > 1) {*affected_list = cel_ptr - board_ptr->board[0];return (aff_ptr - affected_list);}cel_ptr++;}}void init_board(board_type *board_ptr){memset(board_ptr, 0, sizeof(board_type));/*init boarder*/memset(board_ptr->board[0], 0xff, 10);memset(board_ptr->board[9], 0xff, 10);for(int i=0; i<9; i++){board_ptr->board[i][0] = board_ptr->board[i][9] =0xff;}/*init chess*/board_ptr->board[4][4] = board_ptr->board[5][5] = CHESS_WHITE;board_ptr->board[4][5] = board_ptr->board[5][4] = CHESS_BLACK;cur_step = 0;computer_side = CHESS_WHITE;} /*从棋盘的一个状态出发,扩展此结点,并返回此结点的部分回溯值*/void extend_node_one(tree_node_type *node_ptr, tree_node_type *parent_ptr,UINT8 obcolor){tree_node_type childnode;INT16 affected_list[MAX_AFFECTED_PIECES];INT16 start_pos = 11, num;num = find_move(&node_ptr->board, start_pos, obcolor, affected_list);/*如果是终局状态,则返回状态估值函数的值*/if(++cur_depth == max_depth || num==0 ){/*如果已方PASS但没到棋局结束,要扣分*/node_ptr->value = calc_board_status(&node_ptr->board, computer_side);if(!num){/*如果双方都没棋下*/if(!find_move(&node_ptr->board, 11, ~obcolor&0x03, affected_list)) return;if(obcolor == computer_side){node_ptr->value -= 15;return ;}node_ptr->value += 15;}return; }/*初始化回溯值*/ node_ptr->value = (obcolor == computer_side)? -INITIAL_VALUE : INITIAL_VALUE;memcpy(&childnode.board, &node_ptr->board, sizeof(board_type));while(num){while(num--)childnode.board.board[0][affected_list[num]] = obcolor;/*递归计算部分回溯值*/UINT8 depth = cur_depth;extend_node_one(&childnode, node_ptr, (~obcolor)&0x03);cur_depth = depth;/*如果此结点是棋手一方,则部分回溯值是子结点中最大的一个*/if(obcolor == computer_side){if(childnode.value > node_ptr->value){node_ptr->value = childnode.value; node_ptr->movepos = affected_list[0];}}/*如果是对手一方,部分回溯值是子结点中最小的一个*/ else{if(childnode.value < node_ptr->value){node_ptr->value = childnode.value;node_ptr->movepos = affected_list[0];}}/* α-β裁减的判断 在考虑轮到棋手下棋的一个亲节点及轮到对手下棋的一个子节点时,如果该子节点的数值已经小于或等于其亲节点的回溯值,那么就不需要对该节点或者其后续节点做更多的处理了。计算的过程可以直接返回到亲节点上。*//*在考虑轮到对手下棋的一个亲节点及轮到棋手下棋的一个子节点时,如果该子节点的部分回溯值已经大于或等于其亲节点的部分回溯值,那么就不需要对该子节点或者其后裔节点做更多的处理了。计算过程可以直接返回到亲节点上。*/if(parent_ptr){if(obcolor != computer_side){/*α裁减*/if(node_ptr->value <= parent_ptr->value)return;}else{/*β裁减*/if(node_ptr->value >= parent_ptr->value)return;}}/*找到下一个可落子的点*/start_pos = affected_list[0]+1;memcpy(&childnode.board, &node_ptr->board, sizeof(board_type));num = find_move(&childnode.board, start_pos, obcolor, affected_list);}return;}void extend_node_two(tree_node_type *node_ptr, tree_node_type *parent_ptr,UINT8 obcolor){tree_node_type childnode;INT16 affected_list[MAX_AFFECTED_PIECES];INT16 start_pos = 11, num;num = find_move(&node_ptr->board, start_pos, obcolor, affected_list);/*如果是终局状态,则返回状态估值函数的值*/if(!num){/*如果已方PASS但没到棋局结束,要扣分*/node_ptr->value = sample_calc_board_status(&node_ptr->board, computer_side);/*如果双方都没棋下*/if(!find_move(&node_ptr->board, 11, ~obcolor&0x03, affected_list)) return;if(obcolor == computer_side){node_ptr->value -= 10;return;}node_ptr->value += 10;return;}/*初始化回溯值*/ node_ptr->value = (obcolor == computer_side)? -INITIAL_VALUE : INITIAL_VALUE;memcpy(&childnode.board, &node_ptr->board, sizeof(board_type));while(num){while(num--)childnode.board.board[0][affected_list[num]] = obcolor;/*递归计算部分回溯值*/UINT8 depth = cur_depth;extend_node_two(&childnode, node_ptr, (~obcolor)&0x03);cur_depth = depth;/*如果此结点是棋手一方,则部分回溯值是子结点中最大的一个*/if(obcolor == computer_side){if(childnode.value > node_ptr->value){node_ptr->value = childnode.value; node_ptr->movepos = affected_list[0];}}/*如果是对手一方,部分回溯值是子结点中最小的一个*/ else{if(childnode.value < node_ptr->value){node_ptr->value = childnode.value;node_ptr->movepos = affected_list[0];}}/* α-β裁减的判断 在考虑轮到棋手下棋的一个亲节点及轮到对手下棋的一个子节点时,如果该子节点的数值已经小于或等于其亲节点的回溯值,那么就不需要对该节点或者其后续节点做更多的处理了。计算的过程可以直接返回到亲节点上。*//*在考虑轮到对手下棋的一个亲节点及轮到棋手下棋的一个子节点时,如果该子节点的部分回溯值已经大于或等于其亲节点的部分回溯值,那么就不需要对该子节点或者其后裔节点做更多的处理了。计算过程可以直接返回到亲节点上。*/if(parent_ptr){if(obcolor != computer_side){/*α裁减*/if(node_ptr->value <= parent_ptr->value)return;}else{/*β裁减*/if(node_ptr->value >= parent_ptr->value)return ;}}/*找到下一个可落子的点*/start_pos = affected_list[0]+1;memcpy(&childnode.board, &node_ptr->board, sizeof(board_type));num = find_move(&childnode.board, start_pos, obcolor, affected_list);}return;}void get_chess_score(board_type *board_ptr, UINT16 &iWscore, UINT16 &iBscore){iWscore =0; iBscore =0;for(INT16 i=1; i<=BOARD_ROWS; i++)for(INT16 j=1; j<=BOARD_COLS; j++){if(board_ptr->board[i][j] == CHESS_BLACK)iBscore++;else if(board_ptr->board[i][j] == CHESS_WHITE)iWscore++;}}void game_over(board_type *board_ptr, HWND hwnd){UINT16 wscore, bscore;char strcomwin[]="虽然你很历害,但我还是赢了你!";char struserwin[]="让你一次,下次你可没这么走运了!";char strdogfall[]="我没好好下,你才有机会平局!";char *text;get_chess_score(board_ptr, wscore, bscore);g_bStart = 0;if(computer_side == CHESS_WHITE){if(wscore > bscore){text = strcomwin;}else if(wscore <bscore){text = struserwin;}else{text = strdogfall;}}else{if(wscore > bscore)text = struserwin;else if(wscore <bscore)text = strcomwin;else text = strdogfall;}MessageBox(hwnd, text, "黑白棋", MB_OK|MB_ICONINFORMATION);}void computer_play(board_type *board_ptr, HWND hwnd){cur_depth =0;tree_node_type node;INT16 affected_list[MAX_AFFECTED_PIECES];start:memcpy(&node.board, board_ptr, sizeof(board_type));node.movepos =0;if(cur_step>= STEP_MONMENT2){extend_node_two(&node, NULL, computer_side);}else if(cur_step > STEP_MONMENT1){max_depth = depth2[g_iGameLevel];extend_node_one(&node, NULL, computer_side);}else {max_depth = depth1[g_iGameLevel];extend_node_one(&node, NULL, computer_side);}if(!do_move_chess(board_ptr, node.movepos, computer_side, hwnd)){if(!find_move(board_ptr, 11, (~computer_side)&0x03, affected_list)){game_over(board_ptr, hwnd);return;}else{MessageBox(hwnd,"我没棋下了,你再走一步!", "黑白棋", MB_OK|MB_ICONINFORMATION);return;}}else{ if(!find_move(board_ptr, 11, (~computer_side)&0x03, affected_list)){if(!find_move(board_ptr, 11, computer_side, affected_list)){game_over(board_ptr, hwnd); return;}else{MessageBox(hwnd ,"你没棋下了,我再走一步!", "黑白棋", MB_OK|MB_ICONINFORMATION);goto start;}}}} UINT8 do_move_chess(board_type *board_ptr, UINT16 movepos, UINT8 obcolor, HWND hwnd){INT16 affected_list[MAX_AFFECTED_PIECES];INT16 num = find_move(board_ptr, movepos, obcolor, affected_list);if(!num || affected_list[0] != movepos)return 0; for(int i=0; i<num; i++){board_ptr->board[0][affected_list[i]] = obcolor;if(hwnd)::SendMessage(hwnd, WM_TRANCHESS, WPARAM(affected_list[i]),LPARAM(i<<16|obcolor));}step_array[cur_step++] = movepos;return 1;}

创作不易 觉得有帮助请点赞关注收藏~~~

如果觉得《Visual C++实现黑白棋游戏实战三:核心算法设计与实现(附源码和资源 可用于大作业)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。