m_pGame->Init(); }
这之后,就可以利用OO的继承和多态性特点来使m_pGame指针使用相同的调用来完成不同的工作了,事实上,COneGame::Init和CTwoGame::Init都是不同的。 5.2.8 胜负的判断——Win
这是游戏中一个极其重要的算法,用来判断当前棋盘的形势是哪一方获胜。其详细内容请参见“主要算法”一节。
6 游戏模式类——CGame
此类负责对游戏模式进行管理,以及在不同的游戏模式下对不同的用户行为进行不同的响应。由于并不需要CGame本身进行响应,所以将其设计为了一个纯虚类,它的定义如下: class CGame { protected:
CTable *m_pTable; public:
list< STEP > m_StepList; // 落子步骤 public:
CGame( CTable *pTable ) : m_pTable( pTable ) {}// 构造函数 virtual ~CGame();// 析构函数 virtual void Init() = 0; // 初始化工作
// 处理胜利后的情况,CTwoGame需要改写此函数完成善后工作
virtual void Win( const STEP& stepSend );
// 发送己方落子
virtual void SendStep( const STEP& stepSend ) = 0;
virtual void ReceiveMsg( MSGSTRUCT *pMsg ) = 0; // 接收对方消息 virtual void Back() = 0; // 发送悔棋请求 };
6.1 主要成员变量说明
6.1.1 棋盘指针——m_pTable
由于在游戏中需要对棋盘以及棋盘的父窗口——主对话框进行操作及UI状态设
- 8 -
置,故为CGame类设置了这个成员。当对主对话框进行操作时,可以使用m_pTable->GetParent()得到它的窗口指针。 6.1.2 落子步骤——m_StepList
好的棋类程序必须要考虑悔棋功能,所以需要为游戏类设置一个落子步骤列表。而人机对弈和网络对弈中都需要这个功能,故将这个成员直接设置到基类CGame中。另外,考虑到使用的简便性,这个成员使用了C++标准模板库(Standard Template Library,STL)中的std::list,而不是MFC的CList。
6.2 主要成员函数说明
6.2.1 悔棋操作——Back
在不同的游戏模式下,悔棋的行为是不一样的。
(1) 人机对弈模式下,计算机是完全允许玩家悔棋的,但是出于对程序负荷的考虑,只允许玩家悔当前的两步棋(计算机一步,玩家一步)。
(2) 双人网络对弈模式下,悔棋的过程为:首先由玩家向对方发送悔棋请求(悔棋消息),然后由对方决定是否允许玩家悔棋,在玩家得到对方的响应消息(允许或者拒绝)之后,才进行悔棋与否的操作。 6.2.2 初始化操作——Init
对于不同的游戏模式而言,也就有不同的初始化方式。对于人机对弈模式而言,初始化操作包括以下几个步骤:
(1) 设置网络连接状态m_bConnected为FALSE。 (2) 设置主界面计算机玩家的姓名。 (3) 初始化所有的获胜组合。
(4) 如果是计算机先走,则占据天元(棋盘正中央)的位置。 网络对弈的初始化工作暂为空,以供以后扩展之用。 6.2.3 接收来自对方的消息——ReceiveMsg
这个成员函数由CTable棋盘类的Receive成员函数调用,用于接收来自对方的消息。在人机对弈游戏模式下,所能接收到的是本地模拟的落子消息MSG_PUTSTEP;在网络对弈游戏模式下,这个成员函数则负责从套接字读取对方发过来的数据,然后将这些数据解释为自定义的消息结构,并回到CTable::Receive来进行处理。 6.2.4 发送落子消息——SendStep
在玩家落子结束后,要向对方发送自己落子的消息。对于不同的游戏模式, 发送的目标不同:
- 9 -
(1) 在人机对弈游戏模式,直接把落子的信息(坐标、颜色)发送给COneGame类相应的计算函数。
(2) 在网络对弈游戏模式,则把落子消息发送给套接字,并由套接字转发给对方。
6.2.5 胜利后的处理——Win
这个成员函数主要针对CTwoGame网络对弈模式。在玩家赢得棋局后,这个函数游戏端经由CTable::Win来判定双方胜败。
7 消息机制
Windows系统拥有自己的消息机制,在不同事件发生的时候,系统可以提供不同的响应方式。五子棋程序模仿Windows系统实现了自己的消息机制,主要为网络对弈服务,以响应不同的网络消息。
7.1 消息机制的架构
当继承CAsyncSocket的套接字类CFiveSocket收到消息时,会触发CFiveSocket::OnReceive事件,在这个事件中调用CTable::Receive,CTable::Receive开始按照自定义的消息格式接收套接字发送的数据,并对不同的消息类型进行分发处理。
当CTable获得了来自网络的消息之后,就可以使用一个switch结构来进行消息的分发了。消息机制过程如图3:
CFiveSocket 网络数据 CFiveSocket::OnReceive 调用 CTable::Receive 分发处理 CFiveSocket::Receive - 10 -
图3 自定义的消息机制
Figure 3 defined mechanism for information
7.2 各种消息说明
网络间传递的消息,都遵循以下一个结构体的形式: typedef struct _tagMsgStruct { // 摘自Messages.h UINT uMsg; // 消息ID int x; // 落子信息 int y; int color;
TCHAR szMsg[128]; // 消息内容
} MSGSTRUCT;
随着uMsg表示消息ID,x 、y表示落子的坐标 ,color表示落子的颜色 ,szMsg随着uMsg的不同而有不同的含义。 7.2.1 落子消息——MSG_PUTSTEP
表明对方落下了一个棋子,其中x、y和color成员有效,szMsg成员无效。在人机对弈游戏模式下,亦会模拟发送此消息以达到程序模块一般化的效果。 7.2.2 聊天消息——MSG_CHAT
表明对方发送了一条聊天信息,szMsg表示对方的信息,其余成员无效。接到这个信息后,会将对方聊天的内容显示在主对话框的聊天记录窗口内。如图4:
- 11 -
图4 聊天窗口 Figure 4 chat window
7.2.3 悔棋消息——MSG_BACK
表明对方请求悔棋,除uMsg成员外其余成员皆无效。接到这个消息后,会弹出MessageBox询问是否接受对方的请求(如图5所示),并根据玩家的选择回返MSG_AGREEBACK或MSG_REFUSEBACK消息。另外,在发送这个消息之后,主界面上的某些元素将不再响应用户的操作。
图5 请求悔棋 Figure 5 calculate request
7.2.4 同意悔棋消息——MSG_AGREEBACK
表明对方接受了玩家的悔棋请求,除uMsg成员外其余成员皆无效。接到这个消息后,将进行正常的悔棋操作。
7.2.5 拒绝悔棋消息——MSG_REFUSEBACK
表明对方拒绝了玩家的悔棋请求(如图6所示),除uMsg成员外其余成员皆无效。接到这个消息后,整个界面将恢复发送悔棋请求前的状态。
- 12 -