标准的麻将(联网的) 会分为服务器模块, 客户端模块, 逻辑模块. 服务器模块必须由逻辑模块支持, 客户端模块很少用到逻辑模块,大多是从服务器发来的判断结果,必要时还是会用到逻辑模块. 因为逻辑模块需要共用, 所以里面都是调用的接口,返回服务器和客户端需要的结果值.
举个例子
客户端触发开始发生给服务端 > 服务端开始游戏 > 通过逻辑模块得到整理好的牌堆 > 服务端通过牌堆和规则计算拿到各个玩家的手牌 > 把手牌数据发到指定客户端 > 客户端根据发来的牌数据显示牌 > 用户打牌 > 客户端把用户想打的牌发送给服务端 > 服务端通过逻辑模块得出该牌能否打并计算是否有吃碰杠之类的操作, 如果能打(如果有操作牌也是这样) > 把这张能打的牌发给客户端 > 客户端得到该牌并显示出牌动画
所以客户端只负责接受需要绘制的消息, 那些逻辑一般都交给服务端. 客户端的逻辑一般就是判断服务器发来的是哪种消息, 根据不同消息绘制界面.
现在想想 服务端模块, 客户端模块, 逻辑模块 哪个最难?
最难的应该是逻辑模块, 其次是客户端, 最简单是服务端. 服务端最简单是因为他只负责发送数据,那些逻辑判断只要调用逻辑模块, 当然还要存储所有玩家的牌数据. 客户端的话因为也可能要绘制各个玩家牌,所以也要存储玩家的牌数据.
项目内容每增加一点, 服务端只要添加一个值来存储增加的内容, 而逻辑和客户端就要改很多东西.
在我们这个项目中,因为没有服务端, 所以我们让客户端模块直接和逻辑模块进行交互(虽然客户端只是控制台窗口), 首先应该把游戏逻辑模块单独出来, 放在逻辑类中. 客户端模块应该按照面向对象的方式组织
该项目中的客户端比较简单, 只有一个玩家对象和牌堆对象
目前这些对象中的属性和方法都很少.
添加了几个类和文件
- //
- // CMD_Sparrow.h
- // MajiangLogicTest
- //
- // Created by TinyUlt on 14-8-17.
- // Copyright (c) 2014年 TinyUlt. All rights reserved.
- //
- #ifndef MajiangLogicTest_CMD_Sparrow_h
- #define MajiangLogicTest_CMD_Sparrow_h
- #define MAX_REPERTORY 144
- typedef unsigned char BYTE;
- typedef unsigned short WORD;
- //数组维数
- #ifndef CountArray
- #define CountArray(Array) (sizeof(Array)/sizeof(Array[0]))
- #endif
- //逻辑掩码
- #define MASK_COLOR 0xF0 //花色掩码
- #define MASK_VALUE 0x0F //数值掩码
- #define MAX_INDEX 42 //最大索引
- #define MAX_COUNT 14 //最大数目
- #define GAME_PLAYER 4 //游戏人数
- #endif
复制代码- //
- // CPile.h
- // MajiangLogicTest
- //
- // Created by TinyUlt on 14-8-17.
- // Copyright (c) 2014年 TinyUlt. All rights reserved.
- //
- #ifndef __MajiangLogicTest__CPile__
- #define __MajiangLogicTest__CPile__
- #include <iostream>
- #include "CMD_Sparrow.h"
- class CPile
- {
- public:
- CPile();
- private:
- //牌堆
- BYTE m_cardPile[MAX_REPERTORY];
-
- //摸牌位置
- BYTE m_currentIndex;
-
- public:
- //得到牌堆数据
- BYTE* getCardPile();
-
- //取一张牌
- BYTE takeOnCard();
-
- //根据索引获取牌的数据
- BYTE getCardDataByIndex(BYTE index);
-
- //得到当前牌堆中牌的数量
- BYTE getCount();
- };
- #endif /* defined(__MajiangLogicTest__CPile__) */
复制代码- //
- // CPile.cpp
- // MajiangLogicTest
- //
- // Created by TinyUlt on 14-8-17.
- // Copyright (c) 2014年 TinyUlt. All rights reserved.
- //
- #include "CPile.h"
- CPile::CPile()
- {
- m_currentIndex = 0;
- }
- BYTE* CPile::getCardPile()
- {
- return m_cardPile;
- }
- BYTE CPile::getCardDataByIndex(BYTE index)
- {
- return m_cardPile[index];
- }
- BYTE CPile::takeOnCard()
- {
- return m_cardPile[m_currentIndex++];
- }
- BYTE CPile::getCount()
- {
- return MAX_REPERTORY - m_currentIndex;
- }
复制代码- //
- // CUser.h
- // MajiangLogicTest
- //
- // Created by TinyUlt on 14-8-17.
- // Copyright (c) 2014年 TinyUlt. All rights reserved.
- //
- #ifndef __MajiangLogicTest__CUser__
- #define __MajiangLogicTest__CUser__
- #include <iostream>
- #include "CMD_Sparrow.h"
- class CUser
- {
- public:
- //存储牌数据的容器
- BYTE m_cardsIndexStore[MAX_INDEX];
-
- };
- #endif /* defined(__MajiangLogicTest__CUser__) */
复制代码- //
- // CGameLogic.h
- // MajiangLogicTest
- //
- // Created by TinyUlt on 14-8-17.
- // Copyright (c) 2014年 TinyUlt. All rights reserved.
- //
- #ifndef __MajiangLogicTest__CGameLogic__
- #define __MajiangLogicTest__CGameLogic__
- #include <iostream>
- #include "CMD_Sparrow.h"
- class CGameLogic
- {
- //变量定义
- protected:
- static const BYTE m_cbCardDataArray[MAX_REPERTORY]; //扑克数据
-
- public:
- static const char* m_cbCardWordArray[MAX_INDEX]; //中文扑克
- public:
- //混乱扑克
- static void RandCardData(BYTE cbCardData[],BYTE cbMaxCount);
- //混乱扑克2
- static void RandAppointCardData(BYTE cbCardData[],BYTE cbMaxCount,BYTE OriginalData[]/*源牌堆数据*/);
- //扑克转换(索引->牌值)
- static BYTE SwitchToCardData(BYTE cbCardIndex);
- //扑克转换(牌型->索引)
- static BYTE SwitchToCardIndex(BYTE cbCardData);
- //扑克转换
- static BYTE SwitchToCardData(BYTE cbCardIndex[MAX_INDEX]/*传入统计所有牌数量的表格*/, BYTE cbCardData[MAX_COUNT]/*传出手牌数据*/);
- //删除扑克
- static bool RemoveCard(BYTE cbCardIndex[MAX_INDEX], BYTE cbRemoveCard);
- //根据中文牌,得到牌索引
- static int getIndexByWord(const char* ch);
- //根据牌索引,得到中文牌
- static const char* getWordByIndex(int index);
- //根据牌型值,得到中文牌
- static const char* getWordByData(BYTE cbCardData);
- //轮转玩家
- static int changePlayer(int id);
- };
- #endif /* defined(__MajiangLogicTest__CGameLogic__) */
复制代码- //
- // CGameLogic.cpp
- // MajiangLogicTest
- //
- // Created by TinyUlt on 14-8-17.
- // Copyright (c) 2014年 TinyUlt. All rights reserved.
- //
- #include "CGameLogic.h"
- //扑克数据
- const BYTE CGameLogic::m_cbCardDataArray[MAX_REPERTORY]=
- {
- 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, //万子
- 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, //万子
- 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, //万子
- 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, //万子
- 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19, //同子
- 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19, //同子
- 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19, //同子
- 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19, //同子
- 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29, //索子
- 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29, //索子
- 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29, //索子
- 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29, //索子
-
- 0x31,0x32,0x33,0x34, //风牌
- 0x31,0x32,0x33,0x34, //风牌
- 0x31,0x32,0x33,0x34, //风牌
- 0x31,0x32,0x33,0x34, //风牌
- 0x41,0x42,0x43, //箭牌
- 0x41,0x42,0x43, //箭牌
- 0x41,0x42,0x43, //箭牌
- 0x41,0x42,0x43, //箭牌
-
- 0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58, //花牌
-
- };
- const char* CGameLogic::m_cbCardWordArray[MAX_INDEX]=
- {
- "一万","二万","三万","四万","五万","六万","七万","八万","九万",
- "一筒","二筒","三筒","四筒","五筒","六筒","七筒","八筒","九筒",
- "一索","二索","三索","四索","五索","六索","七索","八索","九索",
- "东", "南", "西", "北", "中", "发", "白",
- "春", "夏", "秋", "冬", "梅", "兰", "竹", "菊"
- };
- void CGameLogic::RandCardData(BYTE cbCardData[],BYTE cbMaxCount)
- {
- //混乱准备
- BYTE cbCardDataTemp[CountArray(m_cbCardDataArray)];//为什么直接用MAX_REPERTORY? 因为这样无耦合
- memcpy(cbCardDataTemp,m_cbCardDataArray,sizeof(m_cbCardDataArray));//拷贝一份到临时牌数组中
-
- //混乱扑克(关键的核心打乱代码)
- BYTE cbRandCount=0,cbPosition=0;
- do
- {
- cbPosition=rand()%(cbMaxCount-cbRandCount);
- cbCardData[cbRandCount++]=cbCardDataTemp[cbPosition];
- cbCardDataTemp[cbPosition]=cbCardDataTemp[cbMaxCount-cbRandCount];
- } while (cbRandCount<cbMaxCount);
-
- return;
-
- }
- //混乱扑克2
- void CGameLogic::RandAppointCardData(BYTE cbCardData[],BYTE cbMaxCount,BYTE OriginalData[]/*源牌堆数据*/)
- {
- //混乱扑克
- BYTE cbRandCount=0,cbPosition=0;
- do
- {
- cbPosition=rand()%(cbMaxCount-cbRandCount);
- cbCardData[cbRandCount++]=OriginalData[cbPosition];
- OriginalData[cbPosition]=OriginalData[cbMaxCount-cbRandCount];
- } while (cbRandCount<cbMaxCount);
-
- return;
- }
- //扑克转换(索引->牌值)
- BYTE CGameLogic::SwitchToCardData(BYTE cbCardIndex)
- {
- //assert(cbCardIndex<42);
- if(cbCardIndex<31) return ((cbCardIndex/9)<<4)|(cbCardIndex%9+1);
- if(cbCardIndex>=31&&cbCardIndex<=33) return(((cbCardIndex/7)<<4)+cbCardIndex%10 );
- if(cbCardIndex>33) return(cbCardIndex+0x2F);
- //assert(false);
- return 0;
- }
- //扑克转换(牌型->索引)
- BYTE CGameLogic::SwitchToCardIndex(BYTE cbCardData)
- {
- // ASSERT(IsValidCard(cbCardData));
- if((cbCardData&MASK_COLOR)<=0x30)
- return (((cbCardData&MASK_COLOR)>>4)*9+(cbCardData&MASK_VALUE)-1);
- if((cbCardData&MASK_COLOR)==0x40)
- return (31+(cbCardData&MASK_VALUE)-1);
- if((cbCardData&MASK_COLOR)==0x50)
- return (34+(cbCardData&MASK_VALUE)-1);
- //ASSERT(false);
- return 0;
- }
- //扑克转换
- BYTE CGameLogic::SwitchToCardData(BYTE cbCardIndex[MAX_INDEX]/*传入统计所有牌数量的表格*/, BYTE cbCardData[MAX_COUNT]/*传出手牌数据*/)
- {
- //转换扑克
- BYTE cbPosition=0;
- for (BYTE i=0;i<MAX_INDEX;i++)
- {
- if (cbCardIndex[i]!=0)
- {
- for (BYTE j=0;j<cbCardIndex[i];j++)
- {
- // ASSERT(cbPosition<MAX_COUNT);
- cbCardData[cbPosition++]=SwitchToCardData(i);
- }
- }
- }
-
- return cbPosition;//返回手牌数
- }
- //根据中文牌,得到牌索引
- int CGameLogic::getIndexByWord(const char* ch)
- {
- for (int i = 0; i < MAX_INDEX; i++)
- {
- if (!strcmp(ch,m_cbCardWordArray[i]))
- {
- return i;
- }
- }
- return -1;
- }
- const char* CGameLogic::getWordByIndex(int index)
- {
- return m_cbCardWordArray[index];
- }
- const char* CGameLogic::getWordByData(BYTE cbCardData)
- {
- return getWordByIndex(SwitchToCardIndex(cbCardData));
- }
- //删除扑克
- bool CGameLogic::RemoveCard(BYTE cbCardIndex[MAX_INDEX], BYTE cbRemoveCard)
- {
-
- //效验扑克
- //ASSERT(IsValidCard(cbRemoveCard));
- BYTE cbRemoveIndex=SwitchToCardIndex(cbRemoveCard);
- //ASSERT(cbCardIndex[cbRemoveIndex]>0);
-
- //删除扑克
- if (cbCardIndex[cbRemoveIndex]>0)
- {
- cbCardIndex[cbRemoveIndex]--;
- return true;
- }
-
- //失败效验
- // ASSERT(FALSE);
-
- return false;
- }
- //轮转玩家
- int CGameLogic::changePlayer(int id)
- {
- id += 1;
- if (id>=GAME_PLAYER) {
- id = 0;
- }
-
- return id;
- }
复制代码- //
- // main.cpp
- // MajiangLogicTest
- //
- // Created by TinyUlt on 14-8-16.
- // Copyright (c) 2014年 TinyUlt. All rights reserved.
- //
- /*
- 最基础的麻将逻辑
-
- 根据自己需要 设置麻将人数GAME_PLAYER
-
- 基本流程:
-
- 初始化牌堆,
- 判断庄家
- 玩家0摸13张牌
- 玩家1摸13张牌
- 玩家2摸13张牌
- 玩家3摸13张牌
- 庄家摸一张牌
- 庄家出牌
- 闲家摸牌
- 闲家出牌
- 闲家摸牌
- 闲家出牌
- 闲家摸牌
- 闲家出牌
- 庄家摸一张牌
- 庄家出牌
- 闲家摸牌
- 闲家出牌
- .......
- 牌堆数为0 结束
-
- 其他功能 比如 碰吃杠听胡等等 先不实现
-
- */
- #include <iostream>
- #include "CGameLogic.h"
- #include "CPile.h"
- #include "CUser.h"
- using namespace std;
- //混乱扑克
- int main(int argc, const char * argv[])
- {
- // insert code here...
-
- //创建摸牌堆
- CPile _pile;
-
- //创建玩家
- CUser _users[GAME_PLAYER];
-
- //出牌的次数
- BYTE _outCardTime = 0;
-
- //庄家用户索引
- BYTE _bankerId = rand()%GAME_PLAYER;
-
- /*第一种混乱法*/
- //得到牌堆数据
- CGameLogic::RandCardData(_pile.getCardPile(), MAX_REPERTORY);
-
- //输出牌堆数据
- cout<<"混乱初始牌堆"<<endl;
-
- for (int i = 0 ; i < MAX_REPERTORY; i++)
- {
- cout<<hex<<"0x"<<int(_pile.getCardDataByIndex(i))<<" ";
- }
- cout<<endl<<endl;
-
- //初始化玩家的牌数据
- for (int j = 0; j < GAME_PLAYER; j++)
- {
- for ( int i = 0; i < MAX_COUNT-1; i++)
- {
- BYTE _cardValue = _pile.takeOnCard();//得到牌堆中的牌
- int _index = CGameLogic::SwitchToCardIndex(_cardValue);//得到该牌对应的索引
- _users[j].m_cardsIndexStore[_index]++;//该牌型加一
- }
- }
- cout<<"-----------------------------------"<<endl;
-
- for (int j = 0; j< GAME_PLAYER; j++)
- {
- cout<<"玩家"<<j<<"的牌数据:"<<endl;
- for (int i = 0; i< MAX_INDEX; i++)
- {
- cout<<hex<<CGameLogic::getWordByIndex(i)<<"(0x"<<int(CGameLogic::SwitchToCardData(i))<<"):"<<dec<<(int)_users[j].m_cardsIndexStore[i]<<" ";//输出手牌中所有牌型对应的数量
- }
- cout<<endl<<endl;
- }
- cout<<"-----------------------------------"<<endl;
-
- for (int j = 0; j< GAME_PLAYER; j++)
- {
- cout<<"玩家"<<j<<"的手牌:"<<endl;
-
- BYTE _handCardData[MAX_COUNT];
- int _handandsCount = (int)CGameLogic::SwitchToCardData(_users[j].m_cardsIndexStore,_handCardData);
-
- for (int i = 0; i< _handandsCount; i++)
- {
- cout<<CGameLogic::getWordByData(_handCardData[i])<<"(0x"<<hex<<(int)_handCardData[i]<<") ";
- }
- cout<<endl<<endl;
- }
- cout<<"-----------------------------------"<<endl;
-
-
- //初始化工作做好后, 开始麻将的出牌逻辑循环
-
- cout<<"庄家: 玩家"<<(int)_bankerId<<endl;
- int _currentPlayer = _bankerId;
-
- while (_pile.getCount()>0)
- {
- //出牌次数
- cout<<"共出牌次数:"<<dec<<(int)_outCardTime<<endl;
-
- //准备出牌的玩家先从牌堆中取牌
- BYTE _cardValue = _pile.takeOnCard();//得到牌堆中的牌
- int _index = CGameLogic::SwitchToCardIndex(_cardValue);//得到该牌对应的索引
- _users[_currentPlayer].m_cardsIndexStore[_index]++;//该牌型加一
- cout<<"从牌堆中取牌:"<<CGameLogic::getWordByData(_cardValue)<<endl;
- cout<<"牌堆剩余:"<<dec<<(int)_pile.getCount()<<endl;
-
- //显示手牌
- {
- BYTE _handCardData[MAX_COUNT];
- int _handandsCount = (int)CGameLogic::SwitchToCardData(_users[_currentPlayer].m_cardsIndexStore,_handCardData);
- cout<<"玩家"<<_currentPlayer<<"的手牌有"<<dec<<_handandsCount<<"张:"<<endl;
- for (int i = 0; i< _handandsCount; i++)
- {
- cout<<CGameLogic::getWordByData(_handCardData[i])<<"(0x"<<hex<<(int)_handCardData[i]<<") ";
- }
- cout<<endl;
- }
-
- //输入要出的牌
- recin:
- char ch[20];
- cout<<"玩家"<<_currentPlayer<<"要出的牌(比如 三万): ";
- cin>>ch;
- int _outCardIndex = CGameLogic::getIndexByWord(ch);
- if (_outCardIndex == -1)
- {
- cout<<"输入错误,请重新输入"<<endl;
- goto recin;
- }
-
- //根据索引得到麻将牌型值
- BYTE _outCardValue = CGameLogic::SwitchToCardData(_outCardIndex);
-
- //根据牌型值删除手中的牌
- bool _b = CGameLogic::RemoveCard(_users[_currentPlayer].m_cardsIndexStore, _outCardValue);
- if (_b)
- {
- cout<<"出牌成功,轮到下家"<<endl;
- _outCardTime++;
- }
- else
- {
- cout<<"输入错误,请重新输入"<<endl;
- goto recin;
- }
-
- //显示手牌
- {
- BYTE _handCardData[MAX_COUNT];
- int _handandsCount = (int)CGameLogic::SwitchToCardData(_users[_currentPlayer].m_cardsIndexStore,_handCardData);
- cout<<"出牌后玩家"<<_currentPlayer<<"的手牌有"<<dec<<_handandsCount<<"张:"<<endl;
- for (int i = 0; i< _handandsCount; i++)
- {
- cout<<CGameLogic::getWordByData(_handCardData[i])<<"(0x"<<hex<<(int)_handCardData[i]<<") ";
- }
- cout<<endl;
- }
-
- cout<<"-----------------------------------"<<endl;
- _currentPlayer = CGameLogic::changePlayer(_currentPlayer);
- }
- return 0;
- }
复制代码 |