一步一步实现五子棋2

原创 2013年12月04日 17:40:58

上一章我们实现了棋盘的绘制,现在来实现用鼠标下棋的功能吧。

首先添加一个Engine类,然后添加若干方法和成员,代码如下:

#ifndef FIVE_ENGINE_H

#define FIVE_ENGINE_H

 

#include <vector>

 

using namespacestd;

 

// 游戏状态

enum GAME_STATUS

{

   GAME_READY = 0,   // 游戏准备

   GAME_WAITING,     // 电脑等待中

   GAME_THINKING,    // 电脑思考中

   GAME_OVER         // 游戏结束

};

 

// 棋盘交叉点属性

enum CHESS_TYPE

{

   CHESS_SPACE = 0,    // 无棋子

   CHESS_BLACK,        // 此处有黑子

   CHESS_WHITE         // 此处有白子

};

 

// 棋盘交叉点结构 

struct CHESS_POINT

{

   int x;        // 水平方向位置

   int y;        // 垂直方向位置

   int type;     // 棋子类型

};

 

class Engine

{

public:

   Engine(void);

   ~Engine(void);

 

   void Init();

   void StartGame();

 

   // 用户落子

   BOOL UserDown(int x,inty);

 

   vector<CHESS_POINT*>getChessList() {returnm_chessList;}

   

private:

   int m_gameState;    // 游戏状态

   int m_userType;     // 用户持子类型 1表示持黑子 2表示持白子

   int m_AIType;       // 电脑持子类型

   CHESS_POINT m_chessTable[15][15];   // 棋盘状态

   vector<CHESS_POINT*>m_chessList;   // 已落子列表

};

#endif

 

以上基本满足了对棋盘和棋子状态的保存。接下来就要添加鼠标事件来完成落子了。

切换到资源视图,右键对话框,选择属性,右侧面板会出现属性框,点击消息,添加WM_LBUTTONUP事件,然后添加相应的处理代码:

void CFiveDlg::OnLButtonUp(UINTnFlags,CPointpoint)

{

   // TODO: 在此添加消息处理程序代码和/或调用默认值

 

   int x= (point.x+m_blockSize / 2 -m_left)/m_blockSize;

   int y= (point.y+m_blockSize / 2 -m_top)/m_blockSize;

 

   if (m_engine->UserDown(x,y))

   {

       // 重绘

       Invalidate();

   }

 

   CDialog::OnLButtonUp(nFlags,point);

}

 

Engine::UserDown的实现如下:

BOOL Engine::UserDown(intx,inty )

{

   // 不在棋盘上,返回

   if ((x> 14) || (x < 0) || (y > 14) || (y< 0))

   {

       return FALSE;

   }

 

   // 该位置有子,返回

   if (m_chessTable[x][y].type !=CHESS_SPACE)

   {

       return FALSE;

   }

 

   m_chessTable[x][y].type =m_userType;

   m_chessList.push_back(&m_chessTable[x][y]);

 

   return TRUE;

}

 

绘制棋子的函数要做相应的修改:

void CFiveDlg::DrawChess(Graphics *g)

{

   if ((m_whiteImg==NULL) || (m_blackImg==NULL))

   {

       return;

   }

 

   int chessLeft= m_left - m_blockSize/ 2;

   int chessTop= m_top - m_blockSize/ 2;

   int chessSize= m_blockSize;

 

   // 从m_engine中获取棋子信息,然后一个一个绘制

   vector<CHESS_POINT*>chessList =m_engine->getChessList();

   vector<CHESS_POINT*>::iteratoritor =chessList.begin();

   while (itor!=chessList.end())

   {

       if ((*itor)->type ==CHESS_WHITE)

       {

            g->DrawImage(m_whiteImg,chessLeft + (*itor)->x * m_blockSize,chessTop + (*itor)->y *m_blockSize,chessSize,chessSize);

       }

       else if((*itor)->type=CHESS_BLACK)

       {

            g->DrawImage(m_blackImg,chessLeft + (*itor)->x * m_blockSize,chessTop + (*itor)->y *m_blockSize,chessSize,chessSize);

       }

 

       itor++;

   }

}


  做好上面的一切后运行程序,看看效果吧。

看,已经能够用鼠标下棋了。可能有同学要问了,怎么都是黑子啊?不急不急,电脑现在智商为零,还不会下棋呢,后面我会让它越来越聪明的,人机对弈的日子不太遥远。

有一个问题,在运行时我发现每下一颗子,画面重绘时会有闪烁现象,我是绝对不能容忍这种伤眼睛的问题存在的,明天重点解决这个问题,不知道有没有高手能够提供解决方法。


继续昨天的问题,大家应该都想到了用双缓存就可以解决闪烁的问题,下面我对自己的绘制部分代码做下修改。

// 建立一块虚拟画布
        Bitmap bmp(m_width, m_height);
        Graphics bmpGraphics(&bmp);

        // 绘制棋盘
        DrawChessBoard(&bmpGraphics);


        // 绘制棋子
        DrawChess(&bmpGraphics);

        // 下面是在对话框上绘图了
        CPaintDC dc(this);
        Graphics graphics(dc.m_hDC);

        // 建立一个CacheBitmap用于快速绘图
        CachedBitmap cachedBmp(&bmp, &graphics);

        graphics.DrawCachedBitmap(&cachedBmp,0,0);  

  试一下效果,还是有闪烁现象抓狂。为了解决这个问题,在网上找了许多资料,经大神指点知道了原来调用Invalidate()之后会发出两个消息WM_ERASEBKGND和WM_PAINT。WM_ERASEBKGND消息会擦除背景,先擦除后描画导致了闪烁现象。解决方法是截获WM_ERASEBKGND,代码如下:

BOOL CFiveDlg::OnEraseBkgnd(CDC* pDC)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
   
//return CDialog::OnEraseBkgnd(pDC);

  return TRUE;

}

再试一下效果,真的一点都不闪了!!!除此之外,调用Invalidate(FALSE)进行重绘的效果也一样。




相关文章推荐

一步一步实现五子棋1

最近待业中,趁这段时间想实现一个VC版的五子棋软件,并且把每天的工作内容写出来当作对自己工作的总结,同时希望能达到抛砖引玉的效果,希望各位大侠多多指教。 首先建立一个MFC工程,如下图:       ...
  • ILSunny
  • ILSunny
  • 2013年12月04日 12:27
  • 879

Dagger2实现依赖注入之一步一步带你入门

一、首先你要知道什么是依赖? 想要理解Dagger2,首先你要理解一个概念,就是什么是依赖,懂的同学可以省过此段。这里给大家举个通俗易懂的例子,让你秒懂什么是依赖,你今天去办港澳通行证,出入境告诉...

带你一步步走入Paxos的世界 -- 序列2

在上一篇我们谈到了复制日志的问题,每个node上面存储日志序列,node之间保证日志完全一样。可能有人会疑问:为啥我要存储日志,直接存储最终的数据不就行了吗?复制状态机日志与状态机我们可以把一个变量x...

三思笔记之一步一步学Oracle part2

  • 2016年05月31日 14:46
  • 49.13MB
  • 下载

一步步走进Android MaterialDesign 之 ToolBar动画效果(2)

今天就学一个控件CollapsingToolbarLayout
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一步一步实现五子棋2
举报原因:
原因补充:

(最多只允许输入30个字)