注:以下程序为本人原创,写的不好,若有好的建议,望留言告知。而若能帮助一二访客,幸甚!

上回用Python 写黑白棋,后来想添加个最小最大规则搜索博弈树的算法,没能实现,于是想先用Win32 写一个,再改编成Python版的。

于是有该程序。

1.游戏界面和框架

游戏框架由打砖块游戏改编而来。

/** BlackWhite: 实现一个简单的黑白棋游戏* 孤舟钓客(guzhoudiaoke@126.com)* 2012-12-01*//* INCLUDES *******************************************************************************/
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include "resource.h"/* DEFINES ********************************************************************************/
// defines for windows
#define WINDOW_CLASS_NAME       TEXT("WIN32CLASS")
#define WINDOW_WIDTH            640
#define WINDOW_HEIGHT           480// states for game loop
#define GAME_STATE_INIT         0
#define GAME_STATE_START_LEVEL  1
#define GAME_STATE_RUN          2
#define GAME_STATE_SHUTDOWN     3
#define GAME_STATE_EXIT         4// block defines
#define NUM_CELL_ROWS           8
#define NUM_CELL_COLUMNS        8#define CELL_SIZE              50
#define PEACE_SIZE              48#define BOARD_LEFT                35
#define BOARD_TOP               35
#define BOARD_RIGHT             435
#define BOARD_BOTTOM            435// color defines
#define COLOR_WHITE             1
#define COLOR_BLACK             16
#define COLOR_NONE              0// these read the keyboard asynchronously
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)/* basic unsigned types *******************************************************************/
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned char  UCHAR;
typedef unsigned char  BYTE;/* FUNCTION DECLARATION *******************************************************************/
int Game_Init(void *parms = NULL);
int Game_Shutdown(void *parms = NULL);
int Game_Main(void *parms = NULL);void Draw_Board(void);/* GLOBALS *********************************************************************************/
HWND        main_window_handle  = NULL;                // save the window handle
HINSTANCE   main_instance       = NULL;                // save the instanceHBITMAP     h_bitmap_board      = NULL;
BITMAP      bitmap_board;
HBITMAP     h_bitmap_black      = NULL;
BITMAP      bitmap_black;
HBITMAP     h_bitmap_white      = NULL;
BITMAP      bitmap_white;int            cxBitmapBoard       = 0;
int         cyBitmapBoard       = 0;int            game_state          = GAME_STATE_INIT; // starting state
int         game_board[NUM_CELL_ROWS][NUM_CELL_COLUMNS];/* WINDPROC ********************************************************************************/
LRESULT CALLBACK WindowProc(HWND    hwnd,UINT   msg,WPARAM  wparam,LPARAM   lparam)
{// this is the main message handler of the systemPAINTSTRUCT   ps;HDC          hdc;switch (msg){case WM_CREATE:return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);Draw_Board();EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;default:break;}return DefWindowProc(hwnd, msg, wparam, lparam);
}/* WINMAIN ********************************************************************************/
int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE hprevinstance,LPSTR lpcmdline,int ncmdshow)
{WNDCLASS   winclass;HWND       hwnd;MSG            msg;/* CS_DBLCLKS Specifies that the window should be notified of double clicks with * WM_xBUTTONDBLCLK messages* CS_OWNDC标志,属于此窗口类的窗口实例都有自己的DC(称为私有DC),私有DC仅属于该窗口实例,* 所以程序只需要调用一次GetDC或BeginPaint获取DC,系统就为窗口初始化一个DC,并且保存程序* 对其进行的改变。ReleaseDC和EndPaint函数不再需要了,因为其他程序无法访问和改变私有DC。*/winclass.style            = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;winclass.lpfnWndProc = WindowProc;winclass.cbClsExtra       = 0;winclass.cbWndExtra        = 0;winclass.hInstance     = hinstance;winclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);winclass.hCursor     = LoadCursor(NULL, IDC_ARROW);winclass.hbrBackground   = (HBRUSH)GetStockObject(BLACK_BRUSH);winclass.lpszMenuName    = NULL;winclass.lpszClassName  = WINDOW_CLASS_NAME;// register the window classif (!RegisterClass(&winclass))return 0;// Create the window, note the use of WS_POPUPhwnd = CreateWindow(WINDOW_CLASS_NAME,           // classTEXT("黑白棋"),              // titleWS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,200,                     // initial x100,                        // initial y    WINDOW_WIDTH,               // initial widthWINDOW_HEIGHT,              // initial heightNULL,                      // handle to parentNULL,                        // handle to menuhinstance,                 // instanceNULL);                       // creation parmsif (! hwnd)return 0;ShowWindow(hwnd, ncmdshow);UpdateWindow(hwnd);// save the window handle and instance in a globalmain_window_handle = hwnd;main_instance       = hinstance;// perform all game console specific initializationGame_Init();// enter main event loopwhile (1){if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){// test if this is a quit msgif (msg.message == WM_QUIT)break;TranslateMessage(&msg); // translate any accelerator keysDispatchMessage(&msg); // send the message to the window proc}Game_Main(); // main game processing goes hereSleep(30);}// shutdown game and release all resourcesGame_Shutdown();// return to windows like thisreturn (msg.wParam);
}void SetWindowSizeAndPos()
{// 获取程序窗口大小及客户区大小RECT rcWindow, rcClient;GetWindowRect(main_window_handle, &rcWindow);GetClientRect(main_window_handle, &rcClient);// 非客户区的宽、高int cxNonClient, cyNonClient;cxNonClient = rcWindow.right - rcWindow.left - (rcClient.right - rcClient.left);cyNonClient = rcWindow.bottom- rcWindow.top  - (rcClient.bottom- rcClient.top);// 修改后的窗口大小int cxWindow, cyWindow;cxWindow = cxNonClient + cxBitmapBoard;cyWindow = cyNonClient + cyBitmapBoard;// 显示位置(居中显示)int xScreen, yScreen;xScreen = GetSystemMetrics(SM_CXSCREEN)/2 - cxWindow/2;yScreen = GetSystemMetrics(SM_CYSCREEN)/2 - cyWindow/2;// 设置窗口位置和大小MoveWindow(main_window_handle, xScreen, yScreen, cxWindow, cyWindow, TRUE);
}/* DRAW FUNCTION **************************************************************************/
int Draw_Rectangle(int x1, int y1, int x2, int y2, int color)
{// this function uses Win32 API to draw a filled rectangleHBRUSH       hbrush;HDC          hdc;RECT        rect;SetRect(&rect, x1, y1, x2, y2);hbrush = CreateSolidBrush(color);hdc = GetDC(main_window_handle);FillRect(hdc, &rect, hbrush);ReleaseDC(main_window_handle, hdc);DeleteObject(hbrush);return 1;
}int DrawText_GUI(TCHAR *text, int x, int y, int color)
{HDC    hdc;hdc = GetDC(main_window_handle);SetTextColor(hdc, color);              // set the colors for the text upSetBkMode(hdc, TRANSPARENT);           // set background mode to transparent so black isn't copiedTextOut(hdc, x, y, text, lstrlen(text));// draw the textReleaseDC(main_window_handle, hdc);     // release the dcreturn 1;
}/* GAME PROGRAMMING CONSOLE FUNCTIONS *****************************************************/
void Init_Blocks(void)
{// initialize the block fieldfor (int row = 0; row < NUM_CELL_ROWS; row++)for (int col = 0; col < NUM_CELL_COLUMNS; col++)game_board[row][col] = COLOR_NONE;game_board[3][3] = COLOR_WHITE;game_board[4][4] = COLOR_WHITE;game_board[3][4] = COLOR_BLACK;game_board[4][3] = COLOR_BLACK;
}void Draw_Board(void)
{// this function draws all the blocks in row major formHDC hdc, hdcMem_board, hdcMem_black, hdcMem_white;hdc = GetDC(main_window_handle);hdcMem_board = CreateCompatibleDC(hdc);SelectObject(hdcMem_board, h_bitmap_board);hdcMem_black = CreateCompatibleDC(hdc);SelectObject(hdcMem_black, h_bitmap_black);hdcMem_white = CreateCompatibleDC(hdc);SelectObject(hdcMem_white, h_bitmap_white);// 绘制棋盘BitBlt(hdc, 0, 0, cxBitmapBoard, cyBitmapBoard, hdcMem_board, 0, 0, SRCCOPY);//BitBlt(hdc, 0, 0, CELL_SIZE, CELL_SIZE, hdcMem_white, 0, 0, SRCCOPY);// 绘制棋子int cur_x = BOARD_LEFT;int cur_y = BOARD_TOP;for (int y = 0; y < NUM_CELL_ROWS; y++){cur_x = BOARD_LEFT;for (int x = 0; x < NUM_CELL_COLUMNS; x++){if (game_board[y][x] == COLOR_WHITE){BitBlt(hdc, cur_x+1, cur_y+1, PEACE_SIZE, PEACE_SIZE, hdcMem_white, 0, 0, SRCCOPY);}else if (game_board[y][x] == COLOR_BLACK){BitBlt(hdc, cur_x+1, cur_y+1, PEACE_SIZE, PEACE_SIZE, hdcMem_black, 0, 0, SRCCOPY);}cur_x += CELL_SIZE;}cur_y += CELL_SIZE;}DeleteDC(hdcMem_board);DeleteDC(hdcMem_white);DeleteDC(hdcMem_black);
}int Game_Init(void *parms)
{// this function is where you do all the initialization for your gamereturn 1;
}int Game_Shutdown(void *parms)
{// this function is where you shutdown your game and release all resources // that you allocatedreturn 1;
}int Game_Main(void *parms)
{// this is the workhorse of your game it will be called continuously in real-time// this is like main() in C all the calls for you game go here!TCHAR  buffer[80];// what state is the game in?if (game_state == GAME_STATE_INIT){h_bitmap_board = LoadBitmap(main_instance, MAKEINTRESOURCE(IDB_BOARD));GetObject(h_bitmap_board, sizeof(BITMAP), &bitmap_board);h_bitmap_black = LoadBitmap(main_instance, MAKEINTRESOURCE(IDB_BLACK));GetObject(h_bitmap_black, sizeof(BITMAP), &bitmap_black);h_bitmap_white = LoadBitmap(main_instance, MAKEINTRESOURCE(IDB_WHITE));GetObject(h_bitmap_white, sizeof(BITMAP), &bitmap_white);cxBitmapBoard = bitmap_board.bmWidth;cyBitmapBoard = bitmap_board.bmHeight;SetWindowSizeAndPos();// transition to start level stategame_state = GAME_STATE_START_LEVEL;}else if (game_state == GAME_STATE_START_LEVEL){// get a new level ready to runInit_Blocks();               // initialize the blocksgame_state = GAME_STATE_RUN;// transition to run state}else if (game_state == GAME_STATE_RUN){// draw the infowsprintf(buffer, TEXT("FREAKOUT            Score %d              Level %d"), 1, 2);//Draw_Rectangle(8, WINDOW_HEIGHT-26, WINDOW_WIDTH, WINDOW_HEIGHT, BACKGROUND_COLOR);//DrawText_GUI(buffer, 8, WINDOW_HEIGHT-26, RGB(255, 255, 128));// check if user is trying to exitif (KEY_DOWN(VK_ESCAPE)){// send message to windows to exitPostMessage(main_window_handle, WM_DESTROY, 0, 0);// set exit stategame_state = GAME_STATE_SHUTDOWN;}if (KEY_DOWN(VK_LBUTTON)){POINT point;   //POINT结构体类型,包含x、y属性GetCursorPos(&point);ScreenToClient(main_window_handle, &point);int row = 0, col = 0;if (point.x > BOARD_LEFT && point.x < BOARD_RIGHT &&point.y > BOARD_TOP  && point.y < BOARD_BOTTOM){col = (point.x-BOARD_LEFT) / CELL_SIZE;row = (point.y-BOARD_TOP)  / CELL_SIZE;Draw_Board();wsprintf(buffer, TEXT("(%d, %d)"), row, col);MessageBox(main_window_handle, buffer, TEXT("HAHA"), MB_OK);}}}else if (game_state == GAME_STATE_SHUTDOWN){// in this state shut everything down and release resources// switch to exit stategame_state = GAME_STATE_EXIT;}return 1;
}

2. 游戏规则

下面目标实现一个两边都由玩家落子的程序。

是否能够落子的判断:

/* 游戏规则 ***********************************************************************************/
void Copy_Board(int dst[NUM_CELL_ROWS][NUM_CELL_COLUMNS], int src[NUM_CELL_ROWS][NUM_CELL_COLUMNS])
{for (int y = 0; y < NUM_CELL_ROWS; y++)for (int x = 0; x < NUM_CELL_COLUMNS; x++)dst[y][x] = src[y][x];
}BOOL Is_On_Board(int pos_row, int pos_col)
{if (pos_row >= 0 && pos_row < NUM_CELL_ROWS &&pos_col >= 0 && pos_col < NUM_CELL_COLUMNS)return TRUE;return FALSE;
}BOOL Is_Valid_Move(int color, int pos_row, int pos_col, vector<int> &tiles_to_flip)
{// 如果该位置不在棋盘上或者该位置已有棋子,则一定非法if (!Is_On_Board(pos_row, pos_col) || game_board[pos_row][pos_col] != COLOR_NONE)return FALSE;// 对方的颜色int other_color;if (color == COLOR_BLACK)other_color = COLOR_WHITE;elseother_color = COLOR_BLACK;// 检查需要翻转的棋子tiles_to_flip.clear();for (int i = 0; i < 8; i++){int row = pos_row + move_dir[i][1]; int col = pos_col + move_dir[i][0];while (Is_On_Board(row, col) && game_board[row][col] == other_color){row += move_dir[i][1]; col += move_dir[i][0];}if (! Is_On_Board(row, col))continue;if (game_board[row][col] == color){while (1){row -= move_dir[i][1]; col -= move_dir[i][0];if (row == pos_row && col == pos_col)break;tiles_to_flip.push_back(col + row*NUM_CELL_COLUMNS);}}}if (tiles_to_flip.empty())return FALSE;return TRUE;
}BOOL Make_Move(int color, int row, int col)
{vector<int> tiles_to_flip;vector<int>::iterator iter;if (! Is_Valid_Move(color, row, col, tiles_to_flip))return FALSE;game_board[row][col] = color;for (iter = tiles_to_flip.begin(); iter != tiles_to_flip.end(); iter++){int r = *iter / 8, c = *iter % 8;game_board[r][c] = color;}return TRUE;
}void Get_Current_Score()
{black_score = 0;white_score = 0;for (int y = 0; y < NUM_CELL_ROWS; y++)for (int x = 0; x < NUM_CELL_COLUMNS; x++){if (game_board[y][x] == COLOR_BLACK)black_score++;else if (game_board[y][x] == COLOR_WHITE)white_score++;}
}BOOL Get_Valid_Moves(int color, vector<int> &valid_moves)
{valid_moves.clear();vector<int> tiles_to_flip;for (int y = 0; y < NUM_CELL_ROWS; y++){for (int x = 0; x < NUM_CELL_COLUMNS; x++){if (Is_Valid_Move(color, y, x, tiles_to_flip))valid_moves.push_back(x + y*8);}}if (valid_moves.empty())return FALSE;return TRUE;
}

玩家控制落子(现在黑白方都由玩家控制):

 else if (game_state == GAME_STATE_RUN){// draw the infoGet_Current_Score();if (turn_color == COLOR_BLACK)wsprintf(buffer, TEXT("轮到黑棋走了,当前比分黑棋:%d,白棋:%d"), black_score, white_score);elsewsprintf(buffer, TEXT("轮到白棋走了,当前比分黑棋:%d,白棋:%d"), black_score, white_score);DrawText_GUI(buffer, 8, cyBitmapBoard-16, RGB(255, 255, 128));// check if user is trying to exitif (KEY_DOWN(VK_ESCAPE)){// send message to windows to exitPostMessage(main_window_handle, WM_DESTROY, 0, 0);// set exit stategame_state = GAME_STATE_SHUTDOWN;}if (turn_color == player_color && KEY_DOWN(VK_LBUTTON)){POINT point;GetCursorPos(&point);ScreenToClient(main_window_handle, &point);int row = 0, col = 0;if (point.x > BOARD_LEFT && point.x < BOARD_RIGHT &&point.y > BOARD_TOP  && point.y < BOARD_BOTTOM){row = (point.y-BOARD_TOP)  / CELL_SIZE; col = (point.x-BOARD_LEFT) / CELL_SIZE;if (Make_Move(player_color, row, col)){Draw_Board();current_num++;// 产生对方的所有走法,若对方有合法走法则交换走棋方,否则不交换// 产生对方的所有走法,若对方有合法走法则交换走棋方,否则不交换vector<int> valid_moves;if (Get_Valid_Moves(computer_color, valid_moves))turn_color = computer_color;}}}if (turn_color == computer_color && KEY_DOWN(VK_LBUTTON)){POINT point;GetCursorPos(&point);ScreenToClient(main_window_handle, &point);int row = 0, col = 0;if (point.x > BOARD_LEFT && point.x < BOARD_RIGHT &&point.y > BOARD_TOP  && point.y < BOARD_BOTTOM){row = (point.y-BOARD_TOP)  / CELL_SIZE; col = (point.x-BOARD_LEFT) / CELL_SIZE;if (Make_Move(computer_color, row, col)){Draw_Board();current_num++;// 产生对方的所有走法,若对方有合法走法则交换走棋方,否则不交换vector<int> valid_moves;if (Get_Valid_Moves(player_color, valid_moves))turn_color = player_color;}}}if (current_num == NUM_CELL_ROWS*NUM_CELL_COLUMNS-4)game_state = GAME_STATE_GAMEOVER;}else if (game_state == GAME_STATE_GAMEOVER){Get_Current_Score();if (black_score == white_score)wsprintf(buffer, TEXT("和棋!32:32"));else if ((player_color == COLOR_BLACK && black_score > white_score) || (player_color == COLOR_WHITE && white_score > black_score))wsprintf(buffer, TEXT("恭喜你赢了!黑棋:%d,白棋:%d"), black_score, white_score);elsewsprintf(buffer, TEXT("不好意思,你输了!黑棋:%d,白棋:%d"), black_score, white_score);MessageBox(main_window_handle, buffer, TEXT("游戏结束"), MB_OK);game_state = GAME_STATE_SHUTDOWN;}else if (game_state == GAME_STATE_SHUTDOWN){// in this state shut everything down and release resources// switch to exit stategame_state = GAME_STATE_EXIT;}

3. 最简单的AI

下面先实现一个类似于贪心的算法,即程序每次选择得分最多的地方落子,但由于角上的特殊性,让程序优先选择角上落子。

// 设黑方为最大者,白方为最小者,黑棋的分为正,白棋分为负
int Get_Board_Score(int board[NUM_CELL_ROWS][NUM_CELL_COLUMNS])
{int score = 0;for (int y = 0; y < NUM_CELL_ROWS; y++){for (int x = 0; x < NUM_CELL_COLUMNS; x++){if (board[y][x] == COLOR_BLACK)score++;else if (board[y][x] == COLOR_WHITE)score--;}}return score;
}BOOL Is_On_Corner(int y, int x)
{if ( (x == 0 && y == 0) || (x == 7 && y == 0) || (x == 7 && y == 7)|| (x == 0 && y == 7) )return TRUE;return FALSE;
}/* 电脑AI算法**************************************************************/
void Get_Computer_Move_Greedy(int computer_color)
{vector<int> valid_moves;vector<int>::iterator iter;Get_Valid_Moves(computer_color, valid_moves);for (iter = valid_moves.begin(); iter != valid_moves.end(); iter++){if (Is_On_Corner(*iter / 8, *iter % 8)){best_move_row = *iter / 8;best_move_col = *iter % 8;return;}}int best_score, score;best_score = computer_color == COLOR_BLACK ? -BEST_SCORE : BEST_SCORE;for (iter = valid_moves.begin(); iter != valid_moves.end(); iter++){int copy_board[NUM_CELL_ROWS][NUM_CELL_COLUMNS];Copy_Board(copy_board, game_board);Make_Move(copy_board, computer_color, *iter/8, *iter%8);score = Get_Board_Score(copy_board);if ( (computer_color == COLOR_BLACK && score > best_score) ||(computer_color == COLOR_WHITE && score < best_score) ){best_move_row = *iter/8;best_move_col = *iter%8;best_score = score;}}
}

控制流程:

 else if (game_state == GAME_STATE_RUN){// draw the infoGet_Current_Score();if (turn_color == COLOR_BLACK)wsprintf(buffer, TEXT("轮到黑棋走了,当前比分黑棋:%d,白棋:%d"), black_score, white_score);elsewsprintf(buffer, TEXT("轮到白棋走了,当前比分黑棋:%d,白棋:%d"), black_score, white_score);DrawText_GUI(buffer, 8, cyBitmapBoard-16, RGB(255, 255, 128));// check if user is trying to exitif (KEY_DOWN(VK_ESCAPE)){// send message to windows to exitPostMessage(main_window_handle, WM_DESTROY, 0, 0);// set exit stategame_state = GAME_STATE_SHUTDOWN;}if (turn_color == player_color && KEY_DOWN(VK_LBUTTON)){POINT point;GetCursorPos(&point);ScreenToClient(main_window_handle, &point);int row = 0, col = 0;if (point.x > BOARD_LEFT && point.x < BOARD_RIGHT &&point.y > BOARD_TOP  && point.y < BOARD_BOTTOM){row = (point.y-BOARD_TOP)  / CELL_SIZE; col = (point.x-BOARD_LEFT) / CELL_SIZE;if (Make_Move(game_board, player_color, row, col)){Draw_Board();current_num++;// 产生对方的所有走法,若对方有合法走法则交换走棋方,否则不交换vector<int> valid_moves;if (Get_Valid_Moves(computer_color, valid_moves))turn_color = computer_color;}}}if (turn_color == computer_color && KEY_DOWN(VK_LBUTTON)){Get_Computer_Move_Greedy(computer_color);if (Make_Move(game_board, computer_color, best_move_row, best_move_col)){Draw_Board();current_num++;// 产生对方的所有走法,若对方有合法走法则交换走棋方,否则不交换vector<int> valid_moves;if (Get_Valid_Moves(player_color, valid_moves))turn_color = player_color;}}if (current_num == NUM_CELL_ROWS*NUM_CELL_COLUMNS-4)game_state = GAME_STATE_GAMEOVER;}

4. 博弈树搜索

上面的程序电脑AI已经具备了一定的棋力,但我试过仔细下,还是能赢电脑的(我是超级新手),而会下的就能比较轻松的赢电脑。

分析原因也很简单,就是电脑只想了一步棋。就如同玩家只看当前一步而不顾后果下棋一样。

只想一步,选最高分来走棋,由于电脑考虑的严密性,使得程序具有了一定的棋力。但只看一步,就容易给玩家留下可乘之机,比如电脑走了一步高分棋,却导致一个角被玩家占了,这一步棋就是臭棋。而我玩的时候虽然一般情况下只考虑一步,但到了角上,略微仔细考虑些,不给电脑占角的可乘之机,或是故意诱导电脑失角,就可以轻松赢棋。

而要让电脑具有更高的棋力,最简单的办法就是更深入的搜索。

棋类游戏一般可以定义成一棵博弈树,一个节点代表一个局面,例如:

最小最大原理:

将红黑双方,一个看做最大者(如黑方)一个看做最小者(如白方)。最大者追求获得一个最高分局面,最小者追求获得一个最低分局面。两方相互博弈,并假设对方一定会采取最优的走法。

如此以来,一方走棋就不能只看当前一步走法,而需要考虑自己走这一步棋,对方会怎样应对。如下图所示:

假设当前局面下,最大者有两种走法A和B,将导致两个局面的诞生。

假设若最大者采取走法A,最小者会有Aa,Ab两种应对走法;假设最大者采取走法B,则最小者会有Ba,Bb两种走法应对。

则,最大者若走走法A,则最小者一定会选择走法Ab,于是两层搜索,走法A的分数是1分;

而若最大者走走法B,则最下者一定会选择走法Bb,于是两层搜索,走法B的分数是3分;

而最大者一定会尽量选择较大的分数走棋,于是会选择走法B.于是最大者按照走法B走棋

我实在是不擅长叙述。说的乱七八糟的。

记得本科毕设做中国象棋的时候,见过一个很好的比喻。

假设甲乙两人博弈:乙有两个包A和B,A中有物品Aa(价值10),Ab(价值1),B中有物品Ba(价值6),Bb(价值3)。

现在由甲来选一个包,而由乙来从这个包中选一个物品送给甲。

甲当然希望得到一个最好的物品,而乙很吝啬,希望给甲一个最坏的物品。

当然乙不会干涉甲选哪个包,而甲选定包后,选哪个物品由乙做主。

则甲一定想得到价值10的物品,但若甲选A包,乙一定会把价值1的物品给甲,因为乙很吝啬。

于是甲考虑到选B包的话,乙会给他价值为3的Bb,虽然不如人意,但这也是甲能做出的最好选择了。

于是甲选B包,乙从B包中选Bb送给甲。

这就是最小-最大原理。

博弈的结果由两方共同决定。

而一局棋双方要下几十步,搜到最底层的可能不是很大。

下面尝试实现一个按最小最大原理搜索depth层的AI走法:

// 电脑AI走法,使用最大最小原理进行博弈树搜索
int Get_Computer_Move_MaxMin(int color, int board[NUM_CELL_ROWS][NUM_CELL_COLUMNS], int depth)
{// 若已搜到要求的最底层,返回盘面分值if (depth == 0)return Get_Board_Score(board);int best_score, score, best_row, best_col, other_color;best_score = color == COLOR_BLACK ? -BEST_SCORE : BEST_SCORE;other_color = 17 - color;// 获取所有合法走法vector<int> valid_moves;if (! Get_Valid_Moves(color, valid_moves)){Get_Valid_Moves(other_color, valid_moves);color = other_color;other_color = 17 - color;}for (vector<int>::iterator iter = valid_moves.begin(); iter != valid_moves.end(); iter++){// 拷贝一份棋盘int copy_board[NUM_CELL_ROWS][NUM_CELL_COLUMNS];Copy_Board(copy_board, board);// 执行这个走法Make_Move(copy_board, color, *iter/8, *iter%8);min_max_search_depth++;score = Get_Computer_Move_MaxMin(other_color, copy_board, depth-1);min_max_search_depth--;if ( (color == COLOR_BLACK && score > best_score) ||(color == COLOR_WHITE && score < best_score) ){best_row = *iter/8;best_col = *iter%8;best_score = score;}}if (min_max_search_depth == 0){best_move_row = best_row;best_move_col = best_col;}return best_score;
}void Get_Computer_Best_Move_AI(int color)
{min_max_search_depth = 0;Get_Computer_Move_MaxMin(color, game_board, MIN_MAX_SEARCH_DEPTH);
}

暂时不去深究这些AI算法,以后有空再全面、深入学习~

上面的程序是自己实现的,没有参考别人的程序,而搜索三层程序棋力一般,所以不保证其正确性!

Windows游戏设计(三)- 黑白棋游戏 - 使用Win32 SDK相关推荐

  1. c语言程序设计黑白棋游戏,C语言课程设计_黑白棋游戏

    C语言课程设计_黑白棋游戏 C语言课程设计_黑白棋游戏 #include "graphics.h" /*图形系统头文件*/ #define LEFT 0x4b00 /*光标左键值* ...

  2. c语言课程设计之黑白棋游戏,c语言课程设计黑白棋游戏.doc

    您所在位置:网站首页 > 海量文档 &nbsp>&nbsp学术论文&nbsp>&nbsp大学论文 c语言课程设计黑白棋游戏.doc26页 本文档一共被 ...

  3. Windows游戏设计(二)- 打砖块游戏 - 使用Win32 SDK

    注:以下内容为学习笔记,多数是从书本.资料中得来,只为加深印象,及日后参考.然而本人表达能力较差,写的不好.因非翻译.非转载,只好选原创,但多数乃摘抄,实为惭愧.但若能帮助一二访客,幸甚! 前一阵子学 ...

  4. c语言设计黑白棋游戏,C语言黑白棋游戏[转载]

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 else if(k==75&&x>100) {CoverBlock(x,y);x-=10;PicBlock(x,y);} else  ...

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

    需要源码和资源请点赞关注收藏后评论区留言私信~~~ 在前面的博客中已经讲解了黑白棋游戏的菜单和各种对话框的实现,下面将对黑白棋游戏的核心算法的设计和实现进行讲解 一.棋盘窗口类的设计 黑白棋的棋盘窗口 ...

  6. 基于Python的Reversi黑白棋游戏设计与开发

    摘 要 黑白棋是一个相当易学,而且很受欢迎的游戏.近年来,随着机器性能的提高和相关理论的完善,人工智能这一领域变得越来越重要,在实际生活中的运用也越来越广泛.为了学习简单的人工智能,我决定用Pytho ...

  7. 【Python案例】基于Pygame黑白棋游戏(附源码)

    有没有小火伴是特别喜欢玩五子棋的,我记得我初中是特别喜欢的.于是,我今天就用Python给大家写了一个黑白棋游戏.代码放在下面了. 01.绘制棋盘 Python学习交流Q群:906715085### ...

  8. html5怎么判断迷宫输赢,HTML5 实现黑白棋游戏|附代码

    2)初始化游戏界面 游戏开始时,init()对保存棋盘上的棋子信息的qizi数组初始化,同时在棋盘上显示初始的4个棋子. function init(){             initLevel( ...

  9. C++ 黑白棋游戏的实现

    时光荏苒..眨眼间半年过去了.半年间我的人生轨迹经历了重大的改变.我最终还是决定把我在去年暑假里实现的这个C++的黑白棋游戏发布在我的CSDN博客上.这或许是我写的最后一个C++程序了. 记得当时,7 ...

最新文章

  1. 2022-2028中国空中互联网系统市场现状及未来发展趋势报告
  2. python 12 socket 编程
  3. MySQL root密码重置 报错:mysqladmin: connect to server at 'localhost' failed的解决方案
  4. 建立apk定时自动打包系统第三篇——代码自动更新、APP自动打包系统
  5. 一致性哈希算法 应用
  6. ECS开放批量创建实例接口RunInstances
  7. 迭代器模式(Iterator)
  8. iOS开发之加速开发使用的28个第三方库、优秀第三方库集合
  9. jQuery和react实现二维码
  10. 如何将FLex AIR运行环境与AIR程序一起打包
  11. Django 工作流程
  12. Android基于腾讯云的小直播开发步骤
  13. MySQL触发器写入Sqlseever_sqlserver 触发器实例
  14. java毕业设计大数据在线考试系统在线阅卷系统及大数据统计分析源码+系统+数据库+lw文档+调试运行
  15. Markdown Viewer 插件安装(使用谷歌浏览器查看md文件格式
  16. v$active_session_history的wait_time和time_waited 列(转)
  17. 浅尝webSocket
  18. matlab读取wav文件出错,MATLAB读取wav文件
  19. 什么是图片的DPI?如何修改图片DPI值?
  20. R语言绘图之ggplot2包

热门文章

  1. EasyUI-获取Tree得到选取实心圆点的id核心代码
  2. Mac如何查看与更高切换输入法的快捷键
  3. .misc 可爱的故事
  4. 860. 柠檬水找零
  5. Git---如何生成patch文件
  6. 足不出户办理ETC:支付宝ETC服务
  7. 文本相似性计算--MinHash和LSH算法
  8. 五. 防护设计学习笔记
  9. 企业财务报表的分析方法(The analysis of financial statements)
  10. java 判断日期是否在今天之后_java——判断日期是否在今天之前