简单的五子棋-人人对战

1新建工程
项目->VC++->MFC->MFC应用程序->输入名称->单文档->Window套接字->完成





2资源编辑
视图->其他窗口->资源试图
添加黑白位图以表示棋盘上面的棋子
IDB_BLACK
IDB_WHITE


黑白鼠标Cursor以替换当前鼠标
IDC_CURSOR1
IDC_CURSOR2


黑白图标Icon以显示在状态栏供以提示
IDI_BLACK
IDI_WHITE
3构造函数
//在MainFrm.h文件里面,将“// 控件条嵌入成员”前面的代码改为“public: CStatusBar m_wndStatusBar;”
//然后在五子棋测试View.h里面添加变量函数

//两个鼠标
HCURSOR hcursorwhite;
HCURSOR hcursorblack;
//棋盘数组
int wzq[19][19];
//colorwhite TRUE时baiqi下
bool colorwhite;
//棋子位图
CBitmap m_bmblack;
CBitmap m_bmwhite;

4棋盘大小设置
由于我们的棋盘大小是一定的,不能改变大小的,是应该符合要求的,在如下函数(MainFrm.cpp文件中)添加设置窗口大小的语句;

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    if( !CMDIFrameWndEx::PreCreateWindow(cs) )
        return FALSE;
    // TODO: 在此处通过修改
    //  CREATESTRUCT cs 来修改窗口类或样式
    cs.dwExStyle = cs.dwExStyle|WS_EX_TOPMOST;
    cs.style = WS_SYSMENU|WS_OVERLAPPED|WS_MINIMIZEBOX;
    //设置窗口的大小
    cs.cx = 450;
    cs.cy = 500;
    return TRUE;
}

5初始化变量
在构造函数(五子棋测试View.cpp文件中)里面添加初始代码

C五子棋测试View::C五子棋测试View()
{
    // TODO: 在此处添加构造代码
    //load鼠标图像和棋子位图
    hcursorblack = AfxGetApp()->LoadCursor(IDC_BLACK);
    hcursorwhite = AfxGetApp()->LoadCursor(IDC_WHITE);
    m_bmwhite.LoadBitmap(IDB_WHITE);
    m_bmblack.LoadBitmap(IDB_BLACK);
    //清理棋盘
    //数组值为0时表示没有棋子
    for (int i = 0; i < 19; i++)
    {
        for (int j = 0; j < 19; j++)
        {
            wzq[i][j] = 0;
        }
    }
    //白棋先下
    colorwhite = true;
}

6画棋盘
在OnDraw(CDC* pDC)函数中画棋盘,由于游戏中有可能重画棋盘,而那时棋盘上面有棋子,所以,我们在这个函数里面必须有画棋子的语句
数组的值为1表示白棋,-1表示黑棋

void C五子棋测试View::OnDraw(CDC* pDC)
{
    C五子棋测试Doc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;
    // TODO: 在此处为本机数据添加绘制代码
    CBrush mybrush1;
    mybrush1.CreateSolidBrush(RGB(192,192,192));
    CRect myrect1(0,0,1200,800);
    pDC->FillRect(myrect1,&mybrush1);
    //画棋盘框线
    CPen mypen;
    CPen* myoldPen;
    mypen.CreatePen(PS_SOLID,1,RGB(0,0,0));
    myoldPen = pDC->SelectObject(&mypen);
    for (int i = 0; i < 19; i++)
    {
        pDC->MoveTo(40,40+i*20);
        pDC->LineTo(400,40+i*20);
        pDC->MoveTo(40+i*20,40);
        pDC->LineTo(40+i*20,400);
    }
    //重画时显示的棋子
    CDC Dc;
    if (Dc.CreateCompatibleDC(pDC) == FALSE)
    {
        AfxMessageBox(_T("Can't creat DC"));
    }
    for (int n = 0; n < 19; n++)
    {
        for (int m = 0; m < 19; m++)
        {
            if (wzq[n][m] == 1)
            {
                //显示白棋
                Dc.SelectObject(m_bmwhite);
                pDC->BitBlt(n*20+32,m*20+32,160,160,&Dc,0,0,SRCCOPY);
            }
            else if(wzq[n][m] == -1)
            {
                //显示黑棋
                Dc.SelectObject(m_bmblack);
                pDC->BitBlt(n*20+32,m*20+32,160,160,&Dc,0,0,SRCCOPY);
            }
        }
    }
}

7设置鼠标
棋盘画好了,下来就是下棋,但是鼠标并没有像我们上面所说的那样变成白棋
添加消息函数:类视图-> C五子棋测试View ->右击->类向导->消息->WM_SETCURSOR->OnSetCursor


BOOL C五子棋测试View::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
   // TODO: 在此添加消息处理程序代码和/或调用默认值
   if (nHitTest == HTCLIENT)
   {
       //白棋下,显示白棋鼠标
       if (colorwhite)
       {
           //调用主框架里面的状态栏
           CMainFrame*pFrm=(CMainFrame*)AfxGetApp()->m_pMainWnd;  
       CStatusBar*pStatus=&pFrm->m_wndStatusBar;
           if (pStatus)
           {
               pStatus->GetStatusBarCtrl().SetIcon(0,AfxGetApp()->LoadIcon(IDB_WHITE));
               pStatus->SetPaneText(0,_T("白棋下"));
           }
           SetCursor(hcursorwhite);
       }
       //显示黑棋鼠标
       else
       {
           SetCursor(hcursorblack);
           CMainFrame*pFrm = (CMainFrame*)AfxGetApp()->m_pMainWnd;
           CStatusBar*pStatus = &pFrm->m_wndStatusBar;
           if (pStatus)
           {
               //显示图像
               pStatus->GetStatusBarCtrl().SetIcon(0,AfxGetApp()->LoadIcon(IDB_WHITE));
               //显示文字
               pStatus->SetPaneText(0,_T("黑棋下"));
           }
       }
       return 1;
   }
   return CView::OnSetCursor(pWnd, nHitTest, message);
}

8下棋操作
这就涉及到OnLButtonDown(UINT nFlags,CPoint point)和OnLButtonUp(UINT nFlags,CPoint point)两个函数了,要用一个还是两个?用down函数是在鼠标按下时放下棋子,可是,要是我们按下后意识到按错了怎么办,那就改用up函数,表示当鼠标键松开时放下棋子
添加消息函数:类视图-> C五子棋测试View ->右击->类向导->消息->WM_LBUTTONUP->OnLButtonUp


void C五子棋测试View::OnLButtonUp(UINT nFlags, CPoint point)
{
   // TODO: 在此添加消息处理程序代码和/或调用默认值
   // TODO: 在此添加消息处理程序代码和/或调用默认值
   // TODO: 在此添加消息处理程序代码和/或调用默认值
   CDC*pDC = GetDC();
   CDC Dc;
   if (Dc.CreateCompatibleDC(pDC) == FALSE)
   
       AfxMessageBox(_T("Can't create DC"));
       //是否在棋盘内
       if (point.x > 30&&point.x < 410&&point.y > 30&&point.y <410)
       {
           int px = (point.x-30)/20;
           int py = (point.y-30)/20;
           //是否已经有棋子
           if (colorwhite&&wzq
               [px][py] == 0)
           {
               Dc.SelectObject(m_bmwhite);
               pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);
               //表示白棋存在
               wzq[px][py] = 1;
               //检查是否结束
               over(point);
               //换黑棋下
               colorwhite = false;
           }
           else if(wzq[px][py] == 0)
           {
               Dc.SelectObject(m_bmblack);
               pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);
               //表示黑棋存在
               wzq[px][py] = -1;
               //检查是否结束
               over(point);
               //换白棋下
               colorwhite = true;
           }
       
   }
   
   CView::OnLButtonUp(nFlags, point);
}

9是否结束
用over()函数判断是否结束,是则结束并重新开始,否则接着把鼠标变成对方棋子,表示对方下棋
Over()函数利用刚下棋的位置为中心,检查它各个方向上的连续五个棋子是否同色,是则结束并重新开始
利用连续五个相同的棋子的值相加,去过他们的绝对值为5,则说明是同色
添加函数:类视图-> C五子棋测试View ->右击->添加->添加函数


void C五子棋测试View::over(CPoint point) {
   //获取鼠标指向数组位置,即中心位置 
   int x = (point.x - 30) / 20;  int y = (point.y - 30) / 20;
   //计算开始判断的坐标 xx,yy  
   int xx,yy; 
   if (x < 4)
       xx = 0;
   else        xx = x - 4; if (y < 4)
       yy = 0;
   else
       yy = y - 4;
   int i, j, a;
   //横向判断 
   for (i = xx; i < 15; i++)
   {
       a = 0;
       for (j = i; j < i + 5; j++)
       {
           a = a + wzq[j][y];
           //五个都是白棋 
           if (a == 5)
           {
               AfxMessageBox(_T("白棋胜!"));
               //重新开始 
               OnStart();
               return;
           }
           //五个都是黑棋 
           if (a == -5)
           {
               AfxMessageBox(_T("黑棋胜!"));
               OnStart();
               return;
           }
       }
   }
   //竖向判断 
   for (i = yy; i < 15; i++)
   {
       a = 0;
       for (j = i; j < i + 5; j++)
       {
           a = a + wzq[x][j];          
           if (a == 5)
           {
               AfxMessageBox(_T("白棋胜!"));
               OnStart();
               return;
           }
           if (a == -5)
           {
               AfxMessageBox(_T("黑棋胜!"));
               OnStart();
               return;
           }
       }
   }
   //向右下角 
//判断起点位置  
   if(x<y) 
   {
       if (xx == 0)
           yy = y - x;
   }
else {
if (yy == 0)
    xx = x - y;
}
//参数 over=1 时退出循环 
int over = 0;
do
{
   a = 0;
   for (i = 0; i < 5; i++)
   {
       if ((xx + i) < 19 || (yy + i) < 19)
       {
           a = a + wzq[xx + i][yy + i];             
           if (a == 5)
           {
               AfxMessageBox(_T("白棋胜!"));
               OnStart();
               return;
           }
           if (a == -5)
           {
               AfxMessageBox(_T("黑棋胜!"));
               OnStart();
               return;
           }
       }
       //到了边界       
       else 
       over = 1;
   }
   xx += 1;
   yy += 1;
} while (over == 0);
//向左下角   
if(y>(18-x)) 
{
   if (x > 13)
   {
       yy = y - (18 - x);      
       xx = 18;
   }
   else
   {
       yy = y - 4;
       xx = x + 4;
   }
}
       else
       {
       if (y < 5)
       {
           xx = x + y;
           yy = 0;
       }
       else
       {
           yy = y - 4;
           xx = x + 4;
       }
       }
       over = 0;
       do
       {
           a = 0;
           for (i = 0; i < 5; i++)
           {
               if ((xx - i) >= 0 || (yy + i) < 19)
               {
                   a = a + wzq[xx - i][yy + i];         
                   if (a == 5)
                   {
                       AfxMessageBox(_T("白棋胜!"));
                       OnStart();
                       return;
                   }
                   if (a == -5)
                   {
                       AfxMessageBox(_T("黑棋胜!"));
                       OnStart();
                       return;
                   }
               }
               //到了边界       
               else            
                   over=1; 
           }
           xx -= 1;
           yy += 1;
       } while (over == 0);

}

10清理棋盘
Onstart()函数用来清理棋盘,从新初始化棋盘,为下一盘棋做准备
添加函数:类视图-> C五子棋测试View ->右击->添加->添加函数


void C五子棋测试View::OnStart()
{
   //清理棋盘
   //数组值为0表示没有棋子
   for (int i = 0; i < 19; i++)
   {
       for (int j = 0; j < 19; j++)
       {
           wzq[i][j] = 0;
       }
       //白棋先下
       colorwhite = true;
       Invalidate();
   }
}

4、 运行调试与分析讨论
下面是小程序运行结果的主窗口,如图,包括棋盘和其他各个操作。开局是白色棋子先落棋,所以,鼠标呈现白色棋子样式。由于图片是截屏出来的,所以并未看到此效果。


下图是游戏中的状态,当落下棋子后,会进入over函数中,判断是否可以结束。如果结束。则清空棋盘,重新开始。如果不结束,则把鼠标变成对方的棋子,表示对方下棋。如此循环下去,直到游戏结束


下图是游戏结束的状态,当某一方棋子获胜后,会弹出一个窗口,显示某一方获胜。在分出胜负后,可以选择点击结束按钮,开始新的一局。


出现的问题:在调试过程中曾出现过棋盘显示出来了,但是无法落棋子的bug。究其原因还是因为在void C五子棋测试View::OnLButtonUp(UINT nFlags, CPoint point)函数中的
if (point.x > 30&&point.x < 410&&point.y > 30&&point.y <410)判断语句中将point.y <410写成了point.y <40,导致落棋位置找不到,无法在棋盘上显示。
缺陷:第一是没有保存功能,第二是只有人人对战,第三是下棋时没有悔棋功能。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容