黑白棋游戏设计与开发毕业设计毕业论文任务书毕业论文开题报告黑白棋游戏设计摘要:本次毕业设计是基于c++语言,在vs2005平台上进行的单机版游戏开发,通过本次游戏的开发,有益于进一步掌握对c++语言的应用,加深对计算机游戏的制作理念的理解,对我今后参与更加复杂的游戏项目积累宝贵的经验!黑白棋(Reversi、Othello),也叫苹果棋,翻转棋,是一个经典的策略性游戏。
黑白棋是19世纪末英国人发明的。
直到上个世纪70年代一个日本人将其发展,借用莎士比亚名剧奥赛罗(Othello)为这个游戏重新命名,也就是现在大家玩的黑白棋。
为何借用莎士比亚名剧呢?是因为奥赛罗是莎士比亚一个名剧的男主角。
他是一个黑人,妻子是白人,因受小人挑拨,怀疑妻子不忠一直情海翻波,最终亲手把妻子杀死。
后来真相大白,奥赛罗懊悔不已,自杀而死。
黑白棋就是借用这个黑人白人斗争的故事而命名。
关键词:c++编程;windows编程;MFC技术论文目录第一章引言 (1)1.1 c++简介 (1)1.2 window编程介绍 (1)1.3 Visual studio开发平台简介 (1)第二章毕业业设计的目的及要求 (2)2.1毕业设计的目的 (2)2.2毕业设计的开发环境 (2)2.3毕业设计所需的知识 (2)2.4毕业设计要求 (2)第三章毕业设计的容 (3)3.1程序功能介绍 (3)3.2程序整体设计说明 (4)3.3程序源代码及注释 (7)第四章结束语 (23)第一章引言1.1 c++语言介绍C++这个词在中国大陆的程序员圈子中通常被读做“C加加”,而西方的程序员通常读做“C plus plus”,“CPP”。
它是一种使用非常广泛的计算机编程语言。
C++是一种静态数据类型检查的,支持多重编程式的通用程序设计语言。
它支持过程化程序设计、数据抽象、面向对象程序设计、制作图标等等泛型程序设计等多种程序设计风格。
1.2 windows编程介绍Windows是微软推出的图形界面的操作系统,有windows2000、XP、Vista、win7等各种版本。
是一种抢占式多任务的操作系统,是PC上的主流操作系统,在PC操作市场占绝大多数网络游戏的客户端、服务器端都可在上运行。
Windows程序由一个或多个的窗口组成,通常是多个窗口。
窗口一般是一个矩形的区域,由windows对窗口进行统一管理。
任何的文字、图形都写窗口中显示。
windows 系统设计为事件驱动的操作系统,事件在windows定义为消息,消息为应用程序和应用程序间、应用程序和操作系统间提供了信息传递的渠道。
消息队列,由Windows维持。
1.3 Visual studio开发平台简介Visual Studio是微软公司推出的开发环境,visual Studio可以用来应用创建windows平台下的windows程序和网络应用程序,也可以用来创建网络服务、智能设备应用程序和Office插件。
目前已经开发到10.0版本,也就是Visual Studio 2010.我们本次毕业设计的开发平台就是其中的Visual Studio 2005。
第二章毕业设计的目的与要求2.1 毕业设计的目的通过对黑白棋游戏规则的研究,做出一款适合在windows系统下进行人机对弈的黑白棋程序出来,是本课题的研究总目标。
具体需要研究一下几方面:1.黑白棋游戏规则;2.黑白棋游戏技巧;3.友好的用户界面;4.单人模式的人工智能算法;5.黑白棋游戏的常用功能(难度选择)。
2.2 毕业设计的开发环境开发语:c++语言;开发平台:windows操作系统中的vs2005平台。
2.3 毕业设计所需的知识熟悉c++语言及vs2005开发平台。
2.4 毕业设计要求1. 分析毕业设计题目的要求2. 写出详细设计说明3. 编写程序代码,调试程序使其能正确运行4. 设计完成的软件要便于操作和使用。
第三章毕业设计的容3.1程序功能介绍1.显示游戏的界面功能游戏界面上包括游戏等级选择、重新开始、游戏帮助的控件对游戏进行相应的设置。
2.接受鼠标输入功能能够接受玩家的鼠标输入功能,把棋子放入指定的位置。
3.根据规则翻转相应的棋子无论是电脑或是玩家在棋盘中落下棋子后,能够根据游戏的规则,把横向、纵向及对角直线上的棋子全部翻转过来,变成最后落下棋子的颜色,并对棋子数量进行增减。
4.游戏胜负判断功能双方都没有棋子可下时棋局结束,以棋子数目来计算胜负,棋子多的一方获胜。
在棋盘还没下满时,如果一方的棋子应经被对方吃光,则棋局结束。
将对手棋子吃光的一方获胜。
5.实现人机对战模式支持人工智能,根据游戏等级的不同,进行不同的算法深度搜索,找出最合理的位置进行落子操作。
6.人工智能的等级设置能够指定当前电脑人工智能的等级。
根据不同的等级,其算法搜索深度不同。
7.游戏提供帮助说明在游戏界面中,提供一个控件。
以方便对本游戏不了解的玩家对游戏进行操作和使用。
黑白棋功能架构:3.2程序整体设计说明1.设计思路本游戏设计是采用MFC对话框模式进行设计开发。
所以从此游戏设计采用模块化思想,即程序整体设计分为五个模块来实现。
主界面对话框类:主要负责主界面及棋盘、棋子的显示及棋盘窗口对象的创建和调用。
棋盘窗口类:主要负责接受玩家鼠标输入的棋子位置及棋子翻转动画等处理。
人工智能算法类:主要负责电脑的人工智能算法的处理。
规则处理类:主要负责棋子数据的统计、落子位置有效及胜负判断等处理。
帮助对话框类:主要负责帮助提示的显示及其他辅助消息。
2.程序结构(流程图)3.程序结果人执黑棋,电脑执白棋,轮流走棋,每方都试图在游戏结束前让自己棋子的数目多于对方。
按鼠标左键可以摆放棋子。
下到最后谁的棋子多,谁赢。
3.3 程序源代码及注释1、游戏对话框中控件的设置消息与成员函数映射:BEGIN_MESSAGE_MAP(COthelloDlg, CDialog)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_BN_CLICKED(IDC_HELP, &COthelloDlg::OnBnClickedButtonHelp)ON_WM_QUERYDRAGICON()//}}AFX_MSG_MAPON_BN_CLICKED(IDC_RADIO1, &COthelloDlg::OnBnClickedRadio1)ON_BN_CLICKED(IDC_RADIO2, &COthelloDlg::OnBnClickedRadio2)ON_BN_CLICKED(IDC_RADIO3, &COthelloDlg::OnBnClickedRadio3)ON_BN_CLICKED(IDC_START, &COthelloDlg::OnGameStart) END_MESSAGE_MAP()帮助控件的实现:void COthelloDlg::OnBnClickedButtonHelp(){// TODO: 在此添加控件通知处理程序代码HelpDlg helpDlg;helpDlg.DoModal();}“低”空间的实现:void COthelloDlg::OnBnClickedRadio1() {// TODO: 在此添加控件通知处理程序代码g_iGameLevel = LEVEL_LOW;}“中”控件的实现:void COthelloDlg::OnBnClickedRadio2() {// TODO: 在此添加控件通知处理程序代码g_iGameLevel = LEVEL_NOR;}“高”控件的实现:void COthelloDlg::OnBnClickedRadio3() {// TODO: 在此添加控件通知处理程序代码g_iGameLevel = LEVEL_HIGH;}游戏开始控件的实现:void COthelloDlg::OnGameStart(){// TODO: 在此添加控件通知处理程序代码GameStart();}void COthelloDlg::GameStart(){m_nBlackCount = 2;m_nWhiteCount = 2;m_chess.NewGame();}2、棋盘窗口的设计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)ON_WM_PAINT()ON_WM_LBUTTONDOWN()ON_WM_CREATE()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);}改变棋子接口函数LRESULT 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));return 0;}由电脑人工智能下棋LRESULT 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));return 0;}新游戏void CChessBoard::NewGame(){if(cur_step >0){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;}3、人工智能的实现#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};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;/*检查在个方向上是否能吃掉对方的棋子,并记录被吃掉棋子的下标*/ 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];}}/* α-β裁减的判断在考虑轮到棋手下棋的一个亲节点及轮到对手下棋的一个子节点时,如果该子节点的数值已经小于或等于其亲节点的回溯值,那么就不需要对该节点或者其后续节点做更多的处理了。