斗地主的制作与实现(报告 完整源代码)毕业设计(论文)word格式.doc
游戏设计概论课程设计报告题 目 斗地主的制作与实现 专 业 班 级 学 号 姓 名 同组人员 一、课程设计的内容、要求完成一个网络版的斗地主。对于联网斗地主,要实现以下功能:1, 能够联网对战,能及时出牌并能与主机一起更新游戏状态。2, 能正确判断胜负和游戏的开始与结束。3, 能认清地主和农民,能正确记分。4, 整个游戏界面如下图所示。上面中间是游戏地主最后拿的三张牌,左右分别是玩家的牌,下面中间是自己的牌。在游戏的右下方有2个按钮,分别是出牌和过牌。5,这个游戏是用鼠标进行操作的,单击自己的牌可以把它们选中,当轮到自己出牌时,就可以单击出牌按钮,出自己已经选中的牌,或者单击过牌按钮,放弃出牌机会。游戏开始时会有选地主的过程,可以单击出牌选择自己是地主,也可以单击过牌选择放弃,如果所有的玩家都选择放弃,则重新发牌。二、所采用的数据结构没有.三、主要模块(或函数)及其功能本游戏的所有模块如下图所示:其中主要模块功能:Managers模块:这是本游戏的核心模块之一。函数:void GameStart();/游戏初始化,发牌。void SendCard();/发牌void CardsInfo(Card ca,int &num,int &min,int &type);/用来得到牌的信息。到底是炸弹,连牌,还是什么的。void PlayS(int min2,int type2,int num2);/设置与发出声音,用来通知玩家所打的牌的大小与种类。void Updata();/根据游戏状态的改变,从而设置游戏界面的状态。Card模块:记录了一张牌的所有信息,包括大小、类型、是否被玩家选中。CProgramView模块:编辑游戏界面函数:void DrawCardOut(int k);void DrawLeft();/绘出剩下的地主的牌,当地主还没决定的时候画的是牌的背面,决定地主/后画出地主的牌void DrawPlayername();/根据传入的字符串,绘出玩家的名称void OnPass();/点击过牌按钮后的响应函数void OnSendCard();/画出当前玩家所出的牌int SelectNum(int num,int mx,int my);/判断玩家点牌后具体点的是哪一张void DrawOtherCard();/画出另外两个玩家还剩下的牌,以及/他们刚刚出牌void DrawMyCard();/画出当前玩家手中牌void OnCancel();/点击退出按钮后的响应函数void OnOK();/点击确定按钮后的响应函数virtual CProgramView();CString PlayerName3;/用来存放三个玩家姓名的字符串void PrintAll();/整体重画CChat模块:基本对话框的聊天模块.CNet模块:网络控制CServer模块:网络主机配置NetControl模块:用于设置网络各方面的问题。函数: void StartCards();void SendGetReady();void SendCards(Card *c,int action);NetControl(CProgramView *p);void Broadcast(char * pMsg);void SendMsg(CNet *pSend, char const *pMsg);/发送消息void OnSorc();/网络配置void OnSet();/设置问题void OnOpen();/已经打开网络配置void FetchMsg(CNet *pRequest);/网络信息处理CNet* ConnectServer();/网络连接四、主要模块(或函数)的算法思想和程序框图流程图如下图所示:走牌用户操作退出游戏过牌出牌单击牌放弃接受地主准备退出游戏DoMsg(int num,int action)PlayerCardsxy.Click改变谁是地主PlayerMain过牌出牌CheckCard()什么都不做改变OutCards与OutPlayerCardsInfo(Card ca,int &num,int &min,int &type)OutPlayerOutCards不能走牌能走牌判断是否能走牌这个函数是用来得到牌的信息,到底是炸弹,连牌,还是什么。Managers模块:Managers.h文件#include "Card.h"#include "NetControl.h"/ Added by ClassView/#include "ProgramView.h"class Managers public:bool m_CardSound;void ClearCard();CProgramView * m_pView;int t;/重新计时NetControl *pControl;void PlayS(int min2,int type2,int num2); bool DoMsg(int num,int action);void GameStart();/游戏初始化,发牌。bool MainComputer;/主机,是主机则是true 不是则为false;Managers();virtual Managers();Card PlayCards320;/最多20张牌int PlayerAc;/这台机器上的玩家int PlayerMain;/地主Card OutCards20;/走的牌,一次也最多出20张牌int OutPlayer;/走牌的玩家Card Sendleft4;/记录剩下的3张牌int Game_State;void Updata();/ -2游戏没开始/ -1游戏准备中,选地主/ 0-2 游戏玩家出牌/ 3-5 对应0-2玩家胜利int Power;/计算当前游戏如果农民胜利则每人得的分int PlayerMaininfo;/记录有几个玩家选择放弃地主 如果都放弃则游戏结束void SendCard();/发牌void SortCard(Card ca);int p3;void CardsInfo(Card ca,int &num,int &min,int &type);private:/void SortCard(Card ca);/void SendCard();/发牌bool CheckCard(int pl);Card PreOut20;Managers.cpp文件/#include "stdafx.h"#include "program.h"#include "Managers.h"#include "mmsystem.h"#include "ProgramView.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE=_FILE_;#define new DEBUG_NEW#endif/ Construction/Destruction/Managers:Managers()Game_State=-2;PlayerAc = -1;t=30;p0=p1=p2=0;Power=10;m_CardSound = true;Managers:Managers()void Managers:GameStart()int k;for(k=0;k<20;k+)PlayCards1k.Click=false;for(k=0;k<3;k+)Sendleftk.Click=true;if(MainComputer)/Power=10;OutCards0.Num=16;PlayerMaininfo=0;SendCard();PlayerMain=rand()%3;Game_State=-1;else/GetCard();bool Managers:CheckCard(int pl)Card *Ca=PlayCardspl;/用Ca 指针来代替Playerpl,使程序简单容易理解int i,j;int num1,min1,type1;int num2,min2,type2;j=0;for(i=0;i<20 && Cai.Num<=15;i+)if(Cai.Click)PreOutj=Cai;j+;if(j=0)return false;else if(j!=20)PreOutj.Num=16;/表示此后的都没有牌了Ca=PreOut;/现在把CA指到这里CardsInfo(Ca,num2,min2,type2);if(OutCards0.Num=16)/上次没有人走牌if(type2=4 && num2=4)| type2=5)PlaySound("res炸弹.wav",NULL,SND_ASYNC |SND_NODEFAULT);if(type2 != 5)PlaySound("reszhadan.wav",NULL,SND_ASYNC |SND_NODEFAULT);else PlaySound("reswangzha.wav",NULL,SND_ASYNC |SND_NODEFAULT);PlayS(min2,type2,num2);if(type2!=0)return true;if(OutPlayer=PlayerAc)/上次走牌的人 OutPlayer就是自己,那么自己可以随便走什么/改变if(type2=4 && num2=4)| type2=5)PlaySound("res炸弹.wav",NULL,SND_ASYNC |SND_NODEFAULT);if(type2 != 5)PlaySound("reszhadan.wav",NULL,SND_ASYNC |SND_NODEFAULT);else PlaySound("reswangzha.wav",NULL,SND_ASYNC |SND_NODEFAULT);PlayS(min2,type2,num2);if(type2!=0)return true;if(type2=0)return false;CardsInfo(OutCards,num1,min1,type1);if(type1=5)/双鬼return false;if(type2=5)PlaySound("res炸弹.wav",NULL,SND_ASYNC |SND_NODEFAULT); PlaySound("reswangzha.wav",NULL,SND_ASYNC |SND_NODEFAULT);return true;if(type1=4 && type2=4)if(num2=num1)if (min1<min2)if (num1=4)PlaySound("res炸弹.wav",NULL,SND_ASYNC |SND_NODEFAULT);PlaySound("reszhadan.wav",NULL,SND_ASYNC |SND_NODEFAULT);PlayS(min2,type2,num2);return true;elsereturn false;if(num2=4 && type2=4)PlaySound("res炸弹.wav",NULL,SND_ASYNC |SND_NODEFAULT); PlaySound("reszhadan.wav",NULL,SND_ASYNC |SND_NODEFAULT);return true;if(type2=type1)if(num1=num2)if(min1<min2)PlayS(min2,type2,num2);return true;elsereturn false;elsereturn false;return false;void Managers:SendCard()int i,j,k;bool Cards55=false;/对应54张扑克,其中Cards54做初始化用 必须为true;/其中0-51为4个1-13 52是小王 53是大王Cards54=true;for(j=0;j<17;j+)for(i=0;i<3;i+)Card &Ca=PlayCardsij;k=54;/保证必进入While循环进行随机while(Cardsk)k=rand()%54;Cardsk=true;Ca.Click=false;if(k=53 | k=52)Ca.Num=k-38;Ca.Pow=k-38;Ca.Type=4;Ca.Click=false;elseCa.Click=false;Ca.Num=k%13+1;Ca.Type=k/13;if(Ca.Num=1 | Ca.Num=2)Ca.Pow=Ca.Num+11;elseCa.Pow=Ca.Num-2;i=0;for(k=0;k<54;k+)if(!Cardsk)Card &Ca=Sendlefti;i+;if(k=53 | k=52)Ca.Num=k-38;Ca.Pow=k-38;Ca.Type=4;Ca.Click=true;elseCa.Num=k%13+1;Ca.Type=k/13;Ca.Click=true;if(Ca.Num=1 | Ca.Num=2)Ca.Pow=Ca.Num+11;elseCa.Pow=Ca.Num-2;Sendlefti.Num=16;PlayCards017.Num=16;PlayCards117.Num=16;PlayCards217.Num=16;/* 函数名称:DoMSG(int num/第几的一张牌int action)/行为* 函数介绍:消息响应* 输入参数:action含义 1点牌 3放弃出牌 4出牌(或接受当地主)* 输出参数:bool* 返回值 :true 表示响应成功 false为失败*/bool Managers:DoMsg(int num, int action)if(action=1)/唯一用得到前面1个参数的地方Card &Ca=PlayCardsPlayerAcnum;if(Ca.Click)Ca.Click=false;elseCa.Click=true;PlaySound("resgive.wav",NULL,SND_ASYNC |SND_NODEFAULT);return true;if(Game_State=-1 && PlayerMain=PlayerAc)if(action=4)PlayCardsPlayerAc17=Sendleft0;PlayCardsPlayerAc18=Sendleft1;PlayCardsPlayerAc19=Sendleft2;SortCard(PlayCardsPlayerAc);Game_State=PlayerAc;OutPlayer=PlayerAc;/改变PlaySound("res叫地主.wav",NULL,SND_ASYNC |SND_NODEFAULT);t=30;return true;if(action=3)PlayerMain=(PlayerMain+1)%3;PlayerMaininfo+;if(PlayerMaininfo = 3)Game_State=-2;PlayerMaininfo=0;m_pView->m_ok.Invalidate();m_pView->m_ok.ShowWindow(SW_SHOW);m_pView->m_sendcard.ShowWindow(SW_HIDE);m_pView->m_pass.ShowWindow(SW_HIDE);m_pView->m_isReady = false;m_pView->isDrawed = false;PlaySound("res不叫.wav",NULL,SND_ASYNC |SND_NODEFAULT);t=30;return true;if(Game_State!=PlayerAc)return false;else if(action=4)if(CheckCard(Game_State)t=30;/能出牌当然出牌咯int i,j;for(i=0;i<20 && PreOuti.Num<=15;i+)OutCardsi=PreOuti;if(i!=20)OutCardsi.Num=16;j=0;Card *Ca=PlayCardsGame_State;for(i=0;i<20 && Cai.Num<=15;i+)if(!Cai.Click)Caj=Cai;j+;OutPlayer=Game_State;Caj.Num=16;Updata();return true;elsereturn false;else if(action=3 && OutPlayer!=PlayerAc)t=30;Game_State=(Game_State+1)%3;/直接更改游戏玩家PlaySound("res不要.wav",NULL,SND_ASYNC |SND_NODEFAULT);return true;return false;void Managers:Updata()int j;Card *ca=PlayCardsGame_State;for(j=0;j<20 && caj.Num<=15;j+);if(j!=0)Game_State=(Game_State+1)%3;else/改变if(Game_State = PlayerMain)MessageBox(GetActiveWindow(),"地主胜利!","胜利",0);PlaySound("reswin1.wav",NULL,SND_ASYNC |SND_NODEFAULT);pPlayerAc += Power;elseMessageBox(GetActiveWindow(),"农民胜利!","胜利",0);PlaySound("reswin.wav",NULL,SND_ASYNC |SND_NODEFAULT);for(int i = 0 ;i <= 2;i+)if(i != PlayerMain)pi += Power;m_pView->m_ok.Invalidate();m_pView->m_ok.ShowWindow(SW_SHOW);m_pView->m_sendcard.ShowWindow(SW_HIDE);m_pView->m_pass.ShowWindow(SW_HIDE);m_pView->m_isReady = false;m_pView->isDrawed = false;m_pView->GetParent()->GetMenu()->GetSubMenu(8)->EnableMenuItem(IDM_READY,MF_ENABLED);Game_State+=3;Power = 10;ClearCard();/m_pView->Invalidate();/* 函数名称:CardsInfo(Card ca,int &num,int &min,int &type)* 函数介绍:把ca中的牌信息反应出来* 输入参数:ca需要反应的牌信息* 输出参数:int &num,int &max,int &type* 返回值 :num表示有多少张牌,min表示起作用的牌中值最小的一个的大小例如3个5带一个8,那么起作用的就是5 min=5,num=3,type表示类型 1:3带1、2 2:4带2(4带2、两对) 3:连子 4:全相同 5:双鬼炸弹 6:飞机7:三顺 0:错牌!不能走出去的*/void Managers:CardsInfo(Card ca,int &num,int &min,int &type)int i,k,n;int same10=0;int nsame10=0;for(n=0;n<20 && can.Num<=15;n+);type=0;num=n;if(n>=5)for(k=0;k<n-1;k+)/连牌if(cak.Pow!=cak+1.Pow-1)break;if(k=n-1 && cak.Pow<=12)/牌不能连到2 2的pow就是13min=ca0.Pow;type=3;return;i=-1;for(k=0;k<n;k+)if(i<0 | cak.Pow!=samei)i+;if(i>2)break;samei=cak.Pow;nsamei+;if(i>=2)int tem;for(tem=0;tem<i;tem+)if(nsametem!=2 | sametem!=sametem+1-1)break;if(nsametem=2 && sametem <= 12)tem+;if(tem>i)/三顺type=7;min=same0;return;int ty1=0,ty2=0,num3=0;int kmin=0;for(tem=0;tem<=i;tem+)/飞机if(nsametem=1)ty1+;else if(nsametem=2)ty2+;else if(nsametem=3 && kmin=0)kmin=sametem;for(;tem<i;tem+)if(nsametem=3 && sametem=sametem+1-1)num3+;else break;if(tem=i && nsametem=3)num3+;if(sametem>12)break;elsebreak;if(ty1=0 | ty2=0) && tem>i)if(num3=ty1 | num3=ty2 | ty1=ty2)&& num3!=0)type=6;min=kmin;return ;if(i<2)/3带n 或 4带nint maxnum=-1;for(k=0;k<=2;k+)if(nsamek>maxnum)maxnum=nsamek;i=k;if(maxnum=3 && n=5)type=1;min=samei;return;else if(maxnum=4)type=2;min=samei;return;else if (n>2)i=-1;for(k=0;k<n;k+)if(i<0 | cak.Pow!=samei)i+;if(i>2)break;samei=cak.Pow;nsamei+;if(i<=2)int maxnum=-1;for(k=0;k<=2;k+)if(nsamek>maxnum)maxnum=nsamek;i=k;if(maxnum=3 && n=4)type=1;min=samei;return;else if(maxnum=3 && n=3)type=4;min=samei;return;else if(maxnum=4 && n=4)type=4;min=samei;return;else if (n=2)if(ca0.Pow=ca1.Pow)type=4;min=ca0.Pow;else if(ca0.Pow=14 && ca1.Pow=15)type=5;min=ca1.Pow;else if (n=1)min=ca0.Pow;type=4;void Managers:SortCard(Card ca)int i,j;Card temp;for(i=0;i<20 && cai.Num<=15;i+)for(j=i+1;j<20 && caj.Num<=15;j+)if(cai.Pow>caj.Pow)temp=cai;cai=caj;caj=temp;void Managers:PlayS(int min2,int type2,int num2)if(m_CardSound)if(1 = type2)if( 4 = num2)PlaySound("ressandaiyi.wav",NULL,SND_ASYNC |SND_NODEFAULT);else PlaySound("ressandaiyidui.wav",NULL,SND_ASYNC |SND_NODEFAULT);else if(2 = type2)if( 6 = num2)PlaySound("ressidaier.wav",NULL,SND_ASYNC |SND_NODEFAULT);else PlaySound("ressidailiangdui.wav",NULL,SND_ASYNC |SND_NODEFAULT);else if(3 = type2 )PlaySound("resshunzi.wav",NULL,SND_ASYNC |SND_NODEFAULT);else if(4 = type2)if(1 = num2)switch(min2)case 1:PlaySound("res出牌3.wav",NULL,SND_ASYNC |SND_NODEFAULT);break;case 2:PlaySound("res出牌4.wav",NULL,SND_ASYNC |SND_NODEFAULT);break;case 3:PlaySound("res出牌5.wav",NULL,SND_ASYNC |SND_NODEFAULT);break;case 4:PlaySound("res出牌6.wav",NULL,SND_ASYNC |SND_NODEFAULT);break;case 5:PlaySound("res出牌7.wav",NULL,SND_ASYNC |SND_NODEFAULT);break;case 6:PlaySound("res出牌8.wav",NULL,SND_ASYNC |SND_NODEFAULT);brea