2天时间敲了一个斗地主代码,觉得自己代码极不规范,决定重构,目标是用严格的面向对象思维、规范的代码风格、规范的命名方式重写一份。原以为这只是1小时的工作,终却断断续续花了好几天查重写后的bug。虽然代码还是很挫,但相比之前已经有了一点点可读性。大学时学了c++就以为懂了面向对象,现在回想学到的都是些渣渣,面向对象重要的是思维而不是语言规则。为提高自己代码质量不断努力。
#include <iostream> #include <vector> #define PLAYERCOUNT 3 #define CARDSCOUNT 54 #define CURRENTPLAYER 0 #define VALUECOUNT 17 #define ERROR -1 using namespace std; const char toFigure[]="34567890JQKA 2YZ"; enum COLOR{ //花色显示ASCII: 3~6 eHEART=3,//红桃 eDIAMOND,//方片 eCLUB, //草花 eSPADE //黑桃 }; class Card; class CardsType; class CardGroup; class Player; class Landlords; class LastCards; bool makeChoice(string tip); bool cmp(Card* a,Card* b); class Card{ public: char figure; COLOR color; int value; Card(char _figure,COLOR _color){ figure=_figure; color=_color; value=calValue(); } int calValue(){ for(int i=0;toFigure[i];i++){ if(toFigure[i]==figure){ return i; } } return ERROR; } void print(){ assert(value!=ERROR); if(figure=='Z'){ cout<<"ZZ"; }else if(figure=='Y'){ cout<<"YY"; }else{ cout<<figure<<(char)color; } cout<<' '; } }; class CardsType{ public: //为了规范查找对应牌的方法 //统一为3个参数cnt1、isContinuous、cnt2 int typeId; string typeStr; int cnt1,cnt2; bool isContinuous; CardsType(){ typeId=ERROR; } bool operator ==(const CardsType& other)const{ return this->typeId==other.typeId; } void init(char* _typeStr,int _typeId,int _cnt1,bool _isContinuous,int _cnt2){ cnt1=_cnt1; isContinuous=_isContinuous; cnt2=_cnt2; typeStr=_typeStr; typeId=_typeId; } }; class CardGroup{ public: vector<Card*> cards; CardsType type; void calType(){ int i,n=cards.size(); //init(typeStr,typeId,cnt1,isContinuous,cnt2) if(n==0){ type.init("不出",14,0,0,0); return; } if(n==2&&cards[0]->value==15&&cards[1]->value==14){ type.init("王炸",0,0,0,0); return; } //统计同点数牌有多少张 int cntFlag[VALUECOUNT]={0}; for(i=0;i<n;i++){ cntFlag[cards[i]->value]++; } //统计点数最多和最少的牌 int maxCnt=0,minCnt=4; for(i=0;i<VALUECOUNT;i++){ if(maxCnt<cntFlag[i]){ maxCnt=cntFlag[i]; } if(cntFlag[i]&&minCnt>cntFlag[i]){ minCnt=cntFlag[i]; } } if(n==4&&maxCnt==4){ type.init("炸dan",1,4,0,0); return; } if(n==1){ type.init("单牌",2,1,0,0); return; } if(n==2&&maxCnt==2){ type.init("对子",3,2,0,0); return; } if(n==3&&maxCnt==3){ type.init("三张 ",4,3,0,0); return; } if(n==4&&maxCnt==3){ type.init("三带一",5,3,0,1); return; } if(n==5&&maxCnt==3&&minCnt==2){ type.init("三带一对",6,3,0,2); return; } if(n==6&&maxCnt==4){ type.init("四带二",7,4,0,1); return; } if(n==8&&maxCnt==4&&minCnt==2){ type.init("四带二",8,4,0,2); return; } if(n>=5&&maxCnt==1&&cards[0]->value==cards[n-1]->value+n-1){ type.init("顺子",9,1,1,0); return; } if(n>=6&&maxCnt==2&&minCnt==2&&cards[0]->value==cards[n-1]->value+n/2-1){ type.init("连对",10,2,1,0); return; } int fjCnt;//统计连续且大于3三张的牌 for(i=0;i<VALUECOUNT &&cntFlag[i]<3;i++); for(fjCnt=0;i<VALUECOUNT &&cntFlag[i]>=3;i++,fjCnt++); if(fjCnt>1){ if(n==fjCnt*3) type.init("飞机",11,3,1,0); else if(n==fjCnt*4) type.init("飞机",12,3,1,1); else if(n==fjCnt*5&&minCnt==2) type.init("飞机",13,3,1,2); } } void init(string inputStr, vector<Card*> &cardsHolded){ this->cards.clear(); //不出 if(inputStr=="N"){ this->calType(); return; } int i,j; //输入合法性判断 for(i=0;i<inputStr.size();i++){ bool find=false; for(j=0;toFigure[j];j++){ if(inputStr[i]==toFigure[j]){ find=true; break; } } if(find==false){ //输入字符不在toFigure中 return; } } //查找手中有没有这些牌 int visitFlag[20]={0}; for(i=0;i<inputStr.size();i++){ Card *find=NULL; for(j=0;j<cardsHolded.size();j++){ if(!visitFlag[j]&&cardsHolded[j]->figure==inputStr[i]){ visitFlag[j]=1; find=cardsHolded[j]; break; } } if(find){ this->cards.push_back(find); }else{ cout<<inputStr[i]; cout<<"没有找到\t"; this->cards.clear(); return; } }//end for(i=0;i<inputStr.size();i++) this->arrange(); } void init(vector<Card*> newCards){ this->cards=newCards; this->arrange(); } bool isCanBeat(CardGroup &cardGroup){ if(cardGroup.type.typeStr=="王炸"){ return false; }else if(this->type.typeStr=="王炸"){ return true; }else if(cardGroup.type==this->type &&this->type.typeStr=="炸dan"){ return value()>cardGroup.value(); }else if(cardGroup.type.typeStr=="炸dan"){ return false; }else if(this->type.typeStr=="炸dan"){ return true; }else if(cardGroup.type==this->type &&this->cards.size()==cardGroup.cards.size()){ return this->value()>cardGroup.value(); }else{ return false; } } int value(){ //计算牌组权值 int i; if(type.typeStr=="三带一"||type.typeStr=="三带一对"||type.typeStr=="飞机"){ for(i=2;i<cards.size();i++){ if(cards[i]->value==cards[i-2]->value){ return cards[i]->value; } } } if(type.typeStr=="四带二"){ for(i=3;i<cards.size();i++){ if(cards[i]->value==cards[i-3]->value){ return cards[i]->value; } } } return cards[0]->value; } void arrange(){ //整理:排序、计算类型 sort(this->cards.begin(),this->cards.end(),cmp); this->calType(); } }; class LastCards{ static LastCards *lastCards; public: Player *player; CardGroup cardGroup; static LastCards* inst(){//单例模式 if(lastCards==NULL){ lastCards=new LastCards(); } return lastCards; } vector<Card*> findCanBeatFrom(vector<Card*> &cardsHolded){ //查找能打得过的牌 int i,j,k,n=cardsHolded.size(),m=cardGroup.cards.size(); string typeStr=cardGroup.type.typeStr; vector<Card*> ret; if(typeStr=="王炸"||n<m){ //打不过,返回空数组 return ret; } int value=cardGroup.value(); //统计各点牌出现的次数 int cntFlag[VALUECOUNT]={0}; for(i=0;i<n;i++){ cntFlag[cardsHolded[i]->value]++; } int continuousCount=1; if(cardGroup.type.isContinuous){ continuousCount=m/(cardGroup.type.cnt1+cardGroup.type.cnt2); } bool findFirstFigure; //cout<<"continuousCount="<<continuousCount<<endl; for(i=value+1;i<VALUECOUNT;i++){ findFirstFigure=true; for(j=0;j<continuousCount;j++){ if(cntFlag[i-j]<cardGroup.type.cnt1){ findFirstFigure=false; break; } } if(findFirstFigure){ ret.clear(); int firstFigure=i; //cout<<"查找"<<cardGroup.type.cnt1<<"个"<<firstFigure+3<<endl; for(k=0,j=0;k<cardsHolded.size() &&j<continuousCount;k++){ if(cardsHolded[k]->value==firstFigure-j){ for(int kk=0;j>=0&&kk<cardGroup.type.cnt1;kk++){ ret.push_back(cardsHolded[k+kk]); } j++; } } if(cardGroup.type.cnt2>0){ int SecondFigures[5]; int SecondCount=continuousCount; if(cardGroup.type.typeStr=="四带二") SecondCount=2; bool findSecondFigure=true; for(j=0,k=-1;j<SecondCount &&findSecondFigure;j++){ findSecondFigure=false; for(k++;k<VALUECOUNT;k++){ SecondFigures[j]=k; if(cntFlag[k]>=cardGroup.type.cnt2 &&cntFlag[k]<cardGroup.type.cnt1){ findSecondFigure=true; break; } } } if(findSecondFigure){ //cout<<"查找SecondFigure "<<cardGroup.type.cnt2<<"个"<<SecondFigures[0]+3<<endl; //cout<<"SecondCount= "<<SecondCount<<endl; //for(i=0;i<SecondCount;i++)cout<<"SecondFigures["<<i<<"]="<<SecondFigures[i]<<endl; for(i=0;i<SecondCount;i++){ for(j=0;j<cardsHolded.size();){ if(cardsHolded[j]->value==SecondFigures[i]){ for(k=0;k<cardGroup.type.cnt2;k++){ //cout<<"添加"<<cardsHolded[j]->value+3<<endl; ret.push_back(cardsHolded[j+k]); } do{ j++; }while(j<cardsHolded.size()&&cardsHolded[j]->value==SecondFigures[i]); }else{ j++; } } } return ret; }//if(findSecondFigure) }//end if(cardGroup.type.cnt2>0) else{ return ret; } }//end if(findFirstFigure) }//end for(i=value+1;i<VALUECOUNT;i++) ret.clear(); //没牌打得过时查找有没有炸dan if(typeStr!="炸dan"){ for(i=cardsHolded.size()-1;i>=3;i--){ if(cardsHolded[i]->value==cardsHolded[i-3]->value){ for(j=0;j<4;j++){ ret.push_back(cardsHolded[i-j]); } break; } } } return ret; }//end vector<Card*> findCanBeatFrom() }; LastCards* LastCards::lastCards = NULL; class Player{ public: string name; vector<Card*> cards; void arrange(){ sort(cards.begin(),cards.end(),cmp); } void print(){ cout<<this->name<<":\t"; for(int i=0;i<cards.size();i++){ cards[i]->print(); } cout<<"["<<cards.size()<<"]\n"; } vector<Card*> tip(){ //提示功能,使自己最小一张连最长 CardGroup ret; string temp; int j,k,m=cards.size(); for(j=0;j<m;j++){ temp=""; for(k=j;k<m;k++){ temp+=cards[k]->figure; } ret.init(temp,cards); if(ret.type.typeId!=ERROR){ return ret.cards; } } ret.cards.clear(); return ret.cards; } void chupai(CardGroup &cardGroup){ //出牌 cout<<this->name<<":\t"; cout<<cardGroup.type.typeStr<<' '; for(int i=0;i<cardGroup.cards.size();i++){ cardGroup.cards[i]->print(); this->cards.erase(find(this->cards.begin(),this->cards.end(),cardGroup.cards[i])); } cout<<"\t["<<this->cards.size()<<"]\n"; if(cardGroup.type.typeStr!="不出"){ //记录到 LastCards 中 LastCards::inst()->player=this; LastCards::inst()->cardGroup.init(cardGroup.cards); } } }; class Landlords{ Player *player[PLAYERCOUNT]; bool finished,youWin,landlordWin; int landlordIndex; Card *cards[CARDSCOUNT]; public: Landlords(){ int i,j,k; for(i=0;i<PLAYERCOUNT;i++){ this->player[i]=new Player(); } //54张牌初始化 for(k=i=0;i<14;i++){ if(toFigure[i]==' '){ continue; } for(COLOR color=eHEART;color<=eSPADE;color=(COLOR)(color+1)){ this->cards[k++]=new Card(toFigure[i],color); } } this->cards[k++]=new Card('Y',eSPADE); this->cards[k]=new Card('Z',eHEART); } ~Landlords(){ for(int i=0;i<PLAYERCOUNT;i++){ delete this->player[i]; } for(int i=0;i<CARDSCOUNT;i++){ delete this->cards[i]; } } void init(){ player[CURRENTPLAYER]->name="Bice"; player[1]->name="玩家2"; player[2]->name="玩家3"; finished=false; youWin=false; landlordWin=false; //抢地主 landlordIndex=ERROR; while(landlordIndex==ERROR){ srand((int)time(0)); shuffle(); landlordIndex=chooseLandlord(); } cout<<player[landlordIndex]->name<<"\t成为地主\n\n"; this->add3Cards(); LastCards::inst()->player=player[landlordIndex]; } void startGame(){ string inputSrt; CardGroup inputCards; for(int iTurns=landlordIndex;!finished;iTurns++){ if(iTurns>=PLAYERCOUNT){ iTurns=0; } if(iTurns==CURRENTPLAYER){ cout<<endl; player[iTurns]->print(); cout<<"输入提示:Z=大王 Y=小王 0=10 输入可无序 例如:JKQ0A9\n请出牌:\t"; do{ cin>>inputSrt; inputCards.init(inputSrt,player[iTurns]->cards); }while(check(&inputCards)==false); }else{ if(player[iTurns]==LastCards::inst()->player){ //若是上次出牌的是自己,启用提示功能 inputCards.init(player[iTurns]->tip()); }else{ //查找能打得过上家的牌 inputCards.init(LastCards::inst()->findCanBeatFrom(player[iTurns]->cards)); } } player[iTurns]->chupai(inputCards);//出牌 if(player[iTurns]->cards.size()==0){ //玩家手中没牌了,游戏结束 finished=true; landlordWin=iTurns==landlordIndex; if(landlordWin){ youWin=landlordIndex==CURRENTPLAYER; }else{ youWin=landlordIndex!=CURRENTPLAYER; } } } cout<<"\n_________________________ "<<(youWin?"You Win!":"You Lose!")<<" _________________________\n\n"; } void add3Cards(){ cout<<"地主3张牌:\t"; for(int i=PLAYERCOUNT*17;i<CARDSCOUNT;i++){ this->cards[i]->print(); player[landlordIndex]->cards.push_back(cards[i]); } cout<<endl; player[landlordIndex]->arrange(); } int chooseLandlord(){ cout<<"\n_________________________ 抢地主 _________________________\n\n"; int first=-1,last,cnt=0,i,j=rand()%PLAYERCOUNT; bool decision; for(i=0;i<PLAYERCOUNT;i++,j==2?j=0:j++){ if(j==CURRENTPLAYER){ decision=makeChoice("是否抢地主?(Y=抢/N=不抢):"); }else{ decision=rand()%2; } if(decision){ cnt++; last=j; if(first==-1){ first=j; } cout<<this->player[j]->name<<"\t抢地主\n"; }else{ cout<<this->player[j]->name<<"\t没有抢\n"; } } if(cnt==0){ cout<<"没人抢,重新发牌\n"; return ERROR; } if(cnt==1){ //第一轮只有一人抢地主 return first; } else{ //最后一次争抢 if(first==CURRENTPLAYER){ decision=makeChoice("是否抢地主?(Y=抢/N=不抢):"); }else{ decision=rand()%2; } if(decision){ cout<<this->player[first]->name<<"\t抢地主\n"; return first; }else{ cout<<this->player[first]->name<<"\t没有抢\n"; return last; } } } void shuffle(){ int i,j,k; //洗牌 for(i=0;i<CARDSCOUNT;i++){ swap(this->cards[i],this->cards[rand()%CARDSCOUNT]); } //分牌 for(k=i=0;i<PLAYERCOUNT;i++){ this->player[i]->cards.clear(); for(j=0;j<17;j++){ this->player[i]->cards.push_back(this->cards[k++]); } this->player[i]->arrange();//整理 this->player[i]->print(); } } bool check(CardGroup *cardGroup){ if(cardGroup->type.typeId==ERROR){ cout<<"出牌错误,重新输入\n"; return false; }else if(cardGroup->type.typeStr=="不出"){ return true; }else if(LastCards::inst()->player!=player[CURRENTPLAYER]&&!cardGroup->isCanBeat(LastCards::inst()->cardGroup)){ cout<<"打不过,重新输入\n"; return false; }else{ return true; } } }; int main(){ Landlords *landlords=new Landlords(); do{ landlords->init();//发牌、抢地主 landlords->startGame();//游戏开始 }while(makeChoice("\n是否继续游戏?(Y=继续/N=结束): ")); delete landlords; return 0; } bool makeChoice(string tip){ cout<<tip; string input; cin>>input; return input=="Y"||input=="y"; } bool cmp(Card* a,Card* b){ //比较两张牌大小 if(a->value==b->value){ return a->color>b->color; }else{ return a->value>b->value; } }
求拍砖!
相关推荐
【C++斗地主程序】是一个使用C++编程语言实现的桌面...这个C++斗地主程序是学习面向对象设计、数据结构、算法以及游戏逻辑的好实例。通过分析和理解它的源代码,开发者可以深入掌握C++的核心概念,并提升游戏开发能力。
总结,实现一个C++版的斗地主程序涉及到多方面的知识,包括C++语法、数据结构、算法设计、用户交互和错误处理等。通过这样的项目,不仅可以巩固C++技能,还能提升解决问题和设计算法的能力。对于学习者来说,这是一...
QQ斗地主单机版源码是一个基于C++编程语言开发的桌面游戏项目,主要针对的是Windows操作系统。这个源码可以在Microsoft Visual C++ 6.0或更高版本的环境中进行编译和运行。项目的核心是实现斗地主游戏的逻辑,尽管AI...
总之,"基于C++实现斗地主小游戏"是一个综合性的编程项目,涵盖了C++语言基础、面向对象编程、算法设计、游戏规则实现、人工智能、用户界面设计等多个方面的知识。对于学习者来说,这是一个极好的实践平台,能够提升...
在本项目中,我们探讨的...总之,C++实现斗地主游戏涉及到面向对象设计、数据结构、算法、游戏逻辑等多个方面,是一个综合性的编程实践。通过这个项目,开发者不仅能提升C++编程技巧,还能深入了解游戏设计的各个方面。
1. **面向对象编程**:C++支持类和对象的概念,斗地主游戏中的每个元素,如玩家、牌、牌组,都可以抽象为类,通过对象来表示。类的定义包含了数据成员(如牌的点数、花色)和成员函数(如出牌、比较大小)。 2. **...
游戏除了提供基础的斗地主玩法,还可能包含计分系统、成就解锁等游戏机制,这些设计让单机版斗地主不仅仅是一场简单的卡牌游戏,而是一个具有长期吸引力的游戏体验。玩家可以通过不断的游戏获得成就感,同时也可以借...
"单机网络版都可行"表明这个游戏不仅包含单人模式,还可以支持网络对战,即玩家可以通过网络与其他玩家进行实时游戏,这涉及到网络编程和多用户同步技术。 C++源码作为"学习的好范例",暗示了这个项目可能包含良好...
分析选牌牌型 先分析选牌是否为王炸,如不是则进行第二步; 找出选牌中相同权值的最大数量; 根据最大数量判断是否为相应的有效牌型。 分析手牌并拆分成有效牌型 判断牌型集合是否为空,不为空就不需要分析直接返回...
在本项目中,"java单机版斗地主源码" 提供了一个用Java实现的斗地主游戏的单机版本。斗地主是一款流行的扑克牌游戏,通常需要三名玩家参与,但在单机版中,通常会通过人工智能(AI)来模拟对手。 源码中的AI设计是...
1. **C++类封装**:在源码中,开发者运用了面向对象的编程思想,将斗地主游戏的各种元素如玩家、牌组、出牌规则等抽象为类。类的封装性使得代码结构清晰,易于维护和扩展。例如,可能会有`Player`类来代表玩家,`...
在本项目中,"单机版斗地主小游戏源码 (2)" 是一个基于Web技术实现的简单网页游戏,特别适合初学者进行学习和参考。它利用HTML作为基础结构语言,jQuery作为JavaScript库来增强页面交互性,实现斗地主游戏的逻辑。接...
C++作为一门强大的面向对象编程语言,以其高效和灵活性著称,非常适合用于开发复杂的游戏程序。在斗地主游戏中,首先需要设计出游戏的基本逻辑,包括发牌、叫分、出牌规则以及胜利条件等。C++的类和对象机制能够帮助...
首先,C++是一种静态类型的、编译式的、通用的、大小写敏感的、不仅支持过程化编程,也支持面向对象编程的程序设计语言。在编写斗地主游戏的过程中,初学者将学习到C++的基础语法,如变量声明、数据类型、控制流(if...
面向对象的斗地主案例是将现实世界中的斗地主游戏逻辑抽象成计算机程序的一种方法。这个案例主要涉及面向对象编程的基本概念和技术,是初学者理解面向对象编程思想的良好实践。下面将详细介绍其中的关键知识点。 ...
C++ 写的 网络版 高清 斗地主
斗地主c++游戏代码资源!
【标题】: "C++基于Qt实现的单机版斗地主游戏源码" 是一个用C++编程语言,结合Qt框架开发的桌面版斗地主游戏项目。Qt是一个跨平台的应用程序开发框架,适用于创建GUI(图形用户界面)应用程序。这个项目提供了完整的...
总的来说,C#单机版斗地主源码是一个结合了面向对象设计、事件驱动编程、数据结构与算法、图形界面设计以及测试技术的综合实例。通过深入学习和分析,不仅可以提升C#编程能力,也能对游戏开发有更深入的理解。对于...