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 45

class CChessBoard : public CWnd
{
private:
	CBitmap  m_bitBlackChess, m_bitWhiteChess;
	CBitmap  m_bitChessBoard;
	CBitmap  m_motive[8]; 
	int      m_iMotiveNumber;
	bool     m_bPlayMotive;
	int      m_iMotivex, m_iMotivey;
	// Construction
public:
	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_MSG
	afx_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_FILE
static char THIS_FILE[] = __FILE__;
#endif

UINT8 g_bStart = 0;

/
// CChessBoard
CChessBoard::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_MAP
	ON_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_CLIENTEDGE

		   return 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 8
typedef 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_NONE     0x00
#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_PASS     1
#define   COMPUTER_PASS 2
#define   GAME_OVER     4

#define  WM_TRANCHESS  (WM_USER+10)
/*可一次吃掉的最多的子的个数*/
#define MAX_AFFECTED_PIECES 19

extern 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;
}

 

 

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

猜你喜欢

转载自blog.csdn.net/jiebaoshayebuhui/article/details/129411337
今日推荐