2天时间敲了一个斗地主代码,觉得自己代码极不规范,决定重构,目标是用严格的面向对象思维、规范的代码风格、规范的命名方式重写一份。原以为这只是1小时的工作,终却断断续续花了好几天查重写后的bug。虽然代码还是很挫,但相比之前已经有了一点点可读性。大学时学了c++就以为懂了面向对象,现在回想学到的都是些渣渣,面向对象重要的是思维而不是语言规则。为提高自己代码质量不断努力。

#include <iostream>
#include <vector>
#define PLAYERCOUNT 3
#define CARDSCOUNT 54
#define CURRENTPLAYER 0
#define VALUECOUNT 17
#define ERROR -1using namespace std;
const char toFigure[]="34567890JQKA 2YZ";
enum COLOR{  //花色显示ASCII: 3~6eHEART=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、cnt2int 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++面向对象相关推荐

  1. python斗地主游戏源码_我用tkinter写的一个斗地主练习复盘python程序

    python写的斗地主模拟器使用说明,以及python的标准控件库tkinter的使用示例. http://vdisk.weibo.com/s/C5R1f8s9EVq2y 我用python写的一个斗地 ...

  2. 写Java程序要体现面向对象

          对于之前写的一篇文章现在想想存在不足之处,之前写的测试ArrayList和LinkedList的各项操作性能比较的程序没有体现面向对象的封装特性,所以,今天把代码重新写了一遍,其实改动的地 ...

  3. python socket能做什么_用python写一个聊天小程序!和女朋友的专属聊天工具!

    原标题:用python写一个聊天小程序!和女朋友的专属聊天工具! 1.UDP简介 Internet协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP).UDP为应用程序提供了无需建立就可 ...

  4. python用程序说爱你_用python写一个聊天小程序!和女朋友的专属聊天工具!

    1.UDP简介 Internet协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP).UDP为应用程序提供了无需建立就可以发送封装的IP数据包的方法. Internet的传输层有两个协议 ...

  5. 用JAVA写一个画图小程序(JAVA 大作业)

    第一次写博客 且是稍微大点的程序 看看就行 重新写的在这,更加清晰明了:点击进入:用JAVA写一个画图小程序(JAVA 大作业)重排版本 设计思路 首先我直接去了Windows自带画图程序去实践模拟, ...

  6. java斗地主程序制作过程

    java斗地主程序制作过程 效果 项目概况 代码分布 效果 项目概况 代码分布 首先对卡牌进行编写 package com;import java.awt.Point; import java.awt ...

  7. 如何看待简书大V饱醉豚 写的《为什么程序员是出轨率最高的群体》?

    今天惊闻一篇文章<为什么程序员是出轨率最高的群体(支持双十一程序员脱单专场)>.本身言论自由,总是有傻逼会发出傻逼的言论,不过文章满篇胡言,像我这种还没女朋友的就被出轨了感觉实在生气.来一 ...

  8. 【收藏】C#面试题整理笔试篇(最全1000+道带答案)300道填空 + 300道选择 + 300道判断 + 70道读程序写结果和看程序填空 + 100道简答题

    <程序员>曾陪伴了无数开发者成长.<新程序员>全新归来,推荐给大家! <新程序员> 一.填空: 1.操作符( && )被用来说明两个条件同为真的情况 ...

  9. 用python写一个聊天小程序!和女朋友的专属聊天工具!

    1.UDP简介 Internet协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP).UDP为应用程序提供了无需建立就可以发送封装的IP数据包的方法. PS:如有需要Python学习资料 ...

  10. 写给想当程序员的朋友!

    原文地址:写给想当程序员的朋友! 作者:岩之GIS (一) 文章由来及个人经历 我是一名计算机专业的本科毕业生,毕业已经1年多了.毕业后从事的是软件编程工作,经常有其他专业的朋友想从事软件编程工作,向 ...

最新文章

  1. 《女性的选择》--[日]今野由梨
  2. This Jenkins instance appears to be offline
  3. POJ 1273 Drainage Ditches 最大流
  4. igxe查询交易机器人_区块链数字货币交易所开发功能技术解决方案 | 拾里郎
  5. scrapy.request
  6. 表达、思考和解决问题的逻辑(金字塔原理-高质量读书笔记)
  7. [C/C++] gdb 调试
  8. macos+未能和恢复服务器,macOS Big Sur 完美降级教程
  9. 用c语言写易语言Linux库,c语言实现简单的易语言
  10. 修复ie浏览器主页被360篡改
  11. 小数分频器vhdl实现_verilog 实现小数分频(小数分频器)代码
  12. 搜索引擎和网站中的高级搜索技巧
  13. [ACMMM2018]BeautyGAN: Instance-level Facial Makeup Transfer with DeepGenerative Adversarial Network
  14. LaTeX中的拼写及语法检查
  15. SolidWorks学习笔记5创建基准面,基准线,基准点
  16. 管理and电子商务相关资源
  17. 计算机取消uefi启动项,如何使用老毛桃winpe删除或添加UEFI BIOS启动项?
  18. android原生打印PDF,HTML;HTML转换为PDF
  19. Python 遍历List三种方式
  20. Excel如何间隔插入空白列

热门文章

  1. 车牌识别ocr为何物—科技普及大讲堂1
  2. 注册查看隐私协议 --- 滚动条是否滚动到底
  3. 详解RS485电路,就是那么简单!
  4. python 实现省全称和省的简称互相转换
  5. 互联网运营常用8大数据分析模型
  6. 重装系统后开机启动项菜单如何删除
  7. Win10提示文件或目录损坏怎么解决?
  8. 走向.NET架构设计---第二章:设计 测试 代码
  9. word文档如何设置/取消首行输入空格当作缩进
  10. python传输视频文件_以字节形式将视频文件流式传输到stdin