当前位置: 首页 > news >正文

C语言之斗地主游戏


🌟 嗨,我是LucianaiB!

🌍 总有人间一两风,填我十万八千梦。

🚀 路漫漫其修远兮,吾将上下而求索。


C语言之斗地主游戏

目录

  1. 程序概述
  2. 程序设计
    1. Card类
    2. CardGroup类
    3. Player类
    4. LastCards类
    5. Landlords类
  3. 游戏流程
  4. 特点与功能
    1. 牌型判断
    2. AI提示
    3. 交互性
    4. 随机性
  5. 测试与运行
  6. 总体评价
  7. 附录代码

本文介绍了一个基于C++实现的简单斗地主游戏程序。该程序模拟了斗地主的基本规则和流程,包括发牌、抢地主、出牌以及胜负判定等功能。程序的核心由多个类组成,包括Card(表示单张牌)、CardGroup(表示一组牌)、Player(表示玩家)、LastCards(用于记录上家出的牌)和Landlords(用于管理整个游戏流程)。

程序设计

程序通过Card类定义了牌的基本属性,如牌面值、花色和显示方法。CardGroup类用于管理一组牌,并提供牌型判断和大小比较的功能。Player类则管理玩家手中的牌,并提供出牌和提示功能。LastCards类用于记录上家出的牌,并提供查找可打得过的牌的功能。Landlords类作为游戏的核心,负责初始化游戏、发牌、抢地主、轮流出牌以及判断游戏结束等逻辑。

游戏流程

游戏开始时,程序通过洗牌和分牌将54张牌随机分配给三个玩家,并通过抢地主环节确定地主玩家。地主玩家获得额外的三张底牌后,游戏正式开始。玩家按顺序出牌,每次出牌需要比上家的牌大,或者选择不出。当某个玩家手中的牌全部出完时,游戏结束,程序会判断地主是否获胜。

特点与功能
  1. 牌型判断:程序能够准确判断牌型,如单牌、对子、三带一、炸弹等,并根据牌型和牌的大小进行比较。
  2. AI提示:非地主玩家在出牌时,程序会自动提示可打得过的牌,提高了游戏的流畅性。
  3. 交互性:程序通过控制台输入输出与用户交互,用户可以选择是否抢地主、出牌以及是否继续游戏。
  4. 随机性:通过随机洗牌和抢地主环节,增加了游戏的趣味性和不确定性。
测试与运行

程序在测试中表现出了良好的稳定性和正确性。玩家可以自由选择出牌策略,程序会根据规则判断出牌是否合法,并在游戏结束后输出胜负结果。用户可以通过简单的命令行交互体验完整的斗地主游戏流程。
在这里插入图片描述

总体评价

本文介绍了一个基于C++实现的斗地主游戏程序,模拟了斗地主的基本规则和流程,包括发牌、抢地主、出牌和胜负判定等功能。程序的核心由多个类组成,如Card、CardGroup、Player、LastCards和Landlords,通过面向对象编程的方式实现了游戏的完整逻辑。

在程序设计中,Card类用于定义单张牌的基本属性,如牌面值、花色和显示方法。CardGroup类管理一组牌,并提供牌型判断和大小比较的功能。Player类管理玩家手中的牌,并提供出牌和提示功能。LastCards类用于记录上家出的牌,并提供查找可打得过的牌的功能。Landlords类作为游戏的核心,负责初始化游戏、发牌、抢地主、轮流出牌以及判断游戏结束等逻辑。

游戏流程从洗牌和分牌开始,通过抢地主环节确定地主玩家。地主获得额外三张底牌后,游戏正式开始。玩家按顺序出牌,每次出牌需要比上家的牌大,或者选择不出。当某个玩家手中的牌全部出完时,游戏结束,程序会判断地主是否获胜。
该程序具有以下特点:

牌型判断:能够准确判断牌型,如单牌、对子、三带一、炸弹等,并根据牌型和牌的大小进行比较。

AI提示:非地主玩家在出牌时,程序会自动提示可打得过的牌,提高了游戏的流畅性。

交互性:通过控制台输入输出与用户交互,用户可以选择是否抢地主、出牌以及是否继续游戏。

随机性:通过随机洗牌和抢地主环节,增加了游戏的趣味性和不确定性。

在测试中,程序表现出良好的稳定性和正确性,用户可以通过简单的命令行交互体验完整的斗地主游戏流程。该程序不仅实现了斗地主的基本规则,还通过类的设计和封装展示了面向对象编程的思想。代码结构清晰,易于理解和扩展,适合用于学习和研究C++编程以及游戏开发。

附录代码:
#include <iostream>
#include<vector>
#include<assert.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<time.h>using namespace std;#define PLAYERCOUNT 3
#define CARDSCOUNT 54
#define CURRENTPLAYER 0
#define VALUECOUNT 17
#define ERROR -1
using namespace std;
int scnt=0;
const char toFigure[]="34567890JQKA 2YZ";enum COLOR{  //花色显示ASCII: 3~6
eHEART=3,//红桃
eDIAMOND=4,//方片
eCLUB=5,   //草花
eSPADE=6   //黑桃
};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);switch (color) {case eHEART:cout << "♥"; // 红桃break;case eDIAMOND:cout << "♦"; // 方片break;case eCLUB:cout << "♣"; // 草花break;case eSPADE:cout << "♠"; // 黑桃break;default:cout << "?"; // 未知花色break;}cout << figure << ' ';}
};
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("炸弹",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=="炸弹"){return value()>cardGroup.value();}else if(cardGroup.type.typeStr=="炸弹"){return false;}else if(this->type.typeStr=="炸弹"){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();//没牌打得过时查找有没有炸弹if(typeStr!="炸弹"){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=0,j=0,k=0;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 i1=0;i1<CARDSCOUNT;i1++){delete this->cards[i1];}
}
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 N=不要 输入可无序 例如: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;
}
}

嗨,我是[LucianaiB](https://lucianaib.blog.csdn.net/ “LucianaiB”)。如果你觉得我的分享有价值,不妨通过以下方式表达你的支持:👍 点赞来表达你的喜爱,📁 关注以获取我的最新消息,💬 评论与我交流你的见解。我会继续努力,为你带来更多精彩和实用的内容。

点击这里👉[LucianaiB](https://lucianaib.blog.csdn.net/ “LucianaiB”) ,获取最新动态,⚡️ 让信息传递更加迅速。

http://www.lryc.cn/news/524691.html

相关文章:

  • 【玩转全栈】----Django制作部门管理页面
  • Unreal Engine 5 C++ Advanced Action RPG 十章笔记
  • 学习ASP.NET Core的身份认证(基于JwtBearer的身份认证9)
  • 缓存之美:万文详解 Caffeine 实现原理(上)
  • Spark/Kafka
  • 深入浅出:Go语言中的Unicode与字符编码详解
  • 什么是SSL及SSL的工作流程
  • .Net Core微服务入门全纪录(二)——Consul-服务注册与发现(上)
  • AD7606, 逐次逼近型ADC以及一次被GPT坑了的过程.
  • 抬手、放手识别算法
  • 深度学习篇---AnacondaLabelImg
  • 探索云原生可观测性:技术与团队协作的深度结合
  • 解决 Django 5.1 中的 TemplateSyntaxError 错误
  • 基于SSM的自助购药小程序设计与实现(LW+源码+讲解)
  • 04JavaWeb——Maven-SpringBootWeb入门
  • 场馆预定平台高并发时间段预定实现V2
  • 如何利用边缘节点服务打造极致用户体验?
  • C语言之小型成绩管理系统
  • ASP.NET Core 中基于 Cookie 的身份鉴权实现
  • 为什么要学习C++?
  • freecad1.0的编译
  • 汇编与逆向(一)-汇编工具简介
  • .NET Framework
  • LabVIEW太赫兹二维扫描成像系统
  • 图片专栏——概念
  • Linux内存管理(Linux内存架构,malloc,slab的实现)
  • 【C++】模板(进阶)
  • Esxi下虚拟机磁盘类型厚置备改精简置备
  • Element使用表单重置如果不使用prop,重置无法生效
  • Windows FileZila Server共享电脑文件夹 映射21端口外网连接