花了6天时间切完了猪国杀,第2天基本写完了代码,跑了30分,后面4天一直在找bug。客观来讲,这道题的难度不是很大,但是在大模拟里面应该是很经典了:代码量巨多、内容巨复杂、坑巨多。题目传送门
首先花点时间把题目读懂,理一理思路,先定义数据结构,然后按照题目的意思一个一个写函数,最后就是输入数据,模拟游戏过程。
解题思路不难想,读懂题目就可以,具体方法都在代码里注释了。提醒一下这道题的几个坑点:
1.题目那句“数据保证牌的数量够用”真是坑死人了,前两个测试数据牌堆都是2000张,但是结束不了游戏,牌堆的牌摸完了以后,要反复摸最后一张牌。
2.无懈可击对锦囊的响应,可以根据锦囊的使用者和锦囊的目标设计不同的方法,但是最好是锦囊的使用者(群技能时应该是当前目标),因为锦囊的使用者(决斗或者无懈可击,是一定已经跳明身份的)。
3.楼主在写代码的时候有一个bug就是玩家死了以后继续使用手牌,所以玩家死了以后要把手牌数清零。
4.经过测试,释放群锦囊时,不论技能的目标轮到了谁,询问无懈可击时,都是从锦囊的使用者开始的。
5.我是在达成游戏结束条件时,立即保存游戏数据,所以如果在同一个玩家的回合中,主公和反贼同时胜利了,正确的做法应该是保存先胜利的数据,但是代码中后胜利的会覆盖前面的数据,所以在保存游戏数据时,必须判断一下游戏是否结束。
6.锦囊牌无论是否被无懈可击,都要丢弃,也就是要把丢弃锦囊牌的代码放前面,避免影响后续。
7.要考虑到装备武器后可以使用前面的杀,使用锦囊之后如果有人亮明身份,可能可以使用前面的决斗,即在装备武器和使用锦囊之后手牌牌序要重置。
下面是完整代码:
#include <bits/stdc++.h>
#define NMAX 11
#define MMAX 2010
using namespace std;
class Pig;
char card[MMAX];//牌堆数组
int card_index=0;//牌序
class End//游戏结束类
{
private:
string result;//游戏结果
int player_index;//玩家数
char hand[NMAX][9999];//游戏结束时保存玩家手牌
int hand_index[NMAX];//游戏结束时玩家手牌数
bool suvive[NMAX];//游戏结束时玩家是否存活
int Fnums;//反贼存活数量,用来判断主公胜利的条件
Pig* player[NMAX];//玩家
bool en;//游戏结束标记
void reserve();//保存游戏结果
public:
End(int Fnums,int playernums,Pig** a);
void GetDead(int role);//获取死亡玩家的信息,判断游戏是否结束
void Output();//结果输出
bool IsEnd(){return en;}
};
class Pig//玩家类
{
private:
char hand[9999];//手牌
bool equipment;
int blood;
int role;//0:主公;1:忠诚;2:反贼
bool exposed;//是否跳出
bool suvived;//是否存活
int hand_index;//手牌数
int enemy_index;//敌人数
int friends_index;//朋友数
int enemy[NMAX];//敌人
int friends[NMAX];//朋友
Pig* player[NMAX];//玩家
int player_index;//玩家数
int id;//玩家序号
bool IsEnemy(int n);//判断是否敌人
bool IsFriends(int n);//判断是否朋友
void SubBlood(int source);//掉血
public:
End* en;//结束类对象
Pig(string name,char card1,char card2,char card3,char card4,int num);
void GetPlayer(int,Pig**);//初始化其他玩家
void AddHand(int);//添加手牌
void GetExpose(int num,int);//获取别人的跳出信息
bool IsSuvived(){return suvived;}
void GetDead(int num);//获取别的人死亡信息
void Play();//出牌阶段
void GetFucked(int source);//被杀
int GetHand_index(){return hand_index;}
char* GetHand(){return hand;}
void AddEnd(End* aaa){en=aaa;}
void Punish()//主公杀死忠臣掉牌
{
hand_index=0;
equipment=false;
}
int GetRole(){return role;}
bool Offer(int id,int type);//type=0:消极事件;type=1:积极事件
void GetDuel(int source);//被决斗
void GetAOE1(int source);//南蛮入侵
void GetAOE2(int source);//万箭齐发
};
void Pig::GetAOE1(int source)//南蛮入侵
{
bool flag=false;
for(int i=0;i<hand_index;i++)//是否有杀
{
if(hand[i]=='K')
{
flag=true;
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
break;
}
}
if(!flag)
{
//如果是主公,在杀害来源没有跳的情况下,主猪会把伤害来源当作类反猪,加入敌人数组
if(role==0&&(!IsFriends(source)&&!IsEnemy(source)))
{
enemy[enemy_index]=source;
enemy_index++;
}
SubBlood(source);
}
}
void Pig::GetAOE2(int source)//万箭齐发
{
bool flag=false;
for(int i=0;i<hand_index;i++)//是否有闪
{
if(hand[i]=='D')
{
flag=true;
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
break;
}
}
if(!flag)
{
//如果是主公,在杀害来源没有跳的情况下,主猪会把伤害来源当作类反猪,加入敌人数组
if(role==0&&!IsFriends(source)&&!IsEnemy(source))
{
enemy[enemy_index]=source;
enemy_index++;
}
SubBlood(source);
}
}
void Pig::GetDuel(int source)//被决斗
{
if(role==1&&source==0) SubBlood(source);//如果是忠臣并且决斗对象是主公,不出杀
else
{
bool flag=false;
for(int i=0;i<hand_index;i++)//响应杀
{
if(hand[i]=='K')
{
flag=true;
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
break;
}
}
if(flag)//如果有杀的话,决斗对象需要继续出杀,相当于使用一张决斗(不需要询问无懈可击的决斗)
{
player[source]->GetDuel(id);
}
else SubBlood(source);
}
}
void End::reserve()//保存游戏结束时的状态
{
for(int i=0;i<player_index;i++)
{
suvive[i]=player[i]->IsSuvived();
hand_index[i]=player[i]->GetHand_index();
char* h=player[i]->GetHand();
for(int j=0;j<hand_index[i];j++)
hand[i][j]=h[j];
}
}
bool Pig::Offer(int id,int type)//响应无懈可击,id:响应的对象,type:事件类型,1代表积极,0代表消极
{
bool flag=false;
if(IsFriends(id)&&type==0)//如果是朋友并且这是一件消极事件
{
for(int i=0;i<hand_index;i++)//寻找无懈可击
{
if(hand[i]=='J')
{
flag=true;
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
if(!exposed)//判断是否跳明身份,因为使用无懈可击将导致身份跳明
{
exposed=true;
for(int j=0;j<player_index;j++)
{
if(player[j]->IsSuvived())
{
player[j]->GetExpose(role,this->id);
}
}
}
for(int j=this->id+1;j<this->id+player_index;j++)//询问是否响应无懈可击
{
if(player[j%player_index]->IsSuvived()&&player[j%player_index]->Offer(id,1))
{
flag=false;
break;
}
}
break;
}
}
}
else if(IsEnemy(id)&&type==1)//如果是敌人并且这是一件积极事件
{
for(int i=0;i<hand_index;i++)
{
if(hand[i]=='J')
{
flag=true;
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
if(!exposed)
{
exposed=true;
for(int j=0;j<player_index;j++)
{
if(player[j]->IsSuvived())
{
player[j]->GetExpose(role,this->id);
}
}
}
for(int j=this->id+1;j<this->id+player_index;j++)
{
if(player[j%player_index]->IsSuvived()&&player[j%player_index]->Offer(id,0))
{
flag=false;
break;
}
}
break;
}
}
}
return flag;
}
End::End(int Fnums,int playernums,Pig** a)
{
this->Fnums=Fnums;
player_index=playernums;
for(int i=0;i<playernums;i++)
player[i]=a[i];
en=false;
}
void End::GetDead(int role)
{
if(IsEnd()) return;
if(role==0)
{
en=true;
result="FP";
reserve();
}
else if(role==2)
{
Fnums--;
if(Fnums==0)
{
en=true;
result="MP";
reserve();
}
}
}
void End::Output()
{
cout<<result<<endl;
for(int i=0;i<player_index;i++)
{
if(suvive[i])
{
for(int j=0;j<hand_index[i]-1;j++)
{
cout<<hand[i][j]<<" ";
}
if(hand_index[i]!=0) cout<<hand[i][hand_index[i]-1];
cout<<endl;
}
else cout<<"DEAD"<<endl;
}
}
Pig::Pig(string name,char card1,char card2,char card3,char card4,int num)
{
hand[0]=card1;
hand[1]=card2;
hand[2]=card3;
hand[3]=card4;
id=num;
player_index=0;
equipment=false;
blood=4;
suvived=true;
hand_index=4;
friends_index=0;
enemy_index=0;
if(name=="MP")
{
role=0;
exposed=true;
}
else if(name=="ZP")
{
role=1;
exposed=false;
}
else
{
role=2;
exposed=false;
}
}
void Pig::GetPlayer(int num,Pig** a)
{
player_index=num;
for(int i=0;i<num;i++)
{
player[i]=a[i];
}
}
void Pig::AddHand(int num)//添加手牌
{
if(card_index>=1999)
{
if(num==2)
{
hand[hand_index]=card[1999];
hand[hand_index+1]=card[1999];
hand_index+=2;
}
else if(num==3)
{
hand[hand_index]=card[1999];
hand[hand_index+1]=card[1999];
hand[hand_index+2]=card[1999];
hand_index+=3;
}
return;
}
else if(card_index==1998)
{
if(num==3)
{
hand[hand_index]=card[1998];
hand[hand_index+1]=card[1999];
hand[hand_index+2]=card[1999];
hand_index+=3;
card_index+=3;
return;
}
}
for(int i=hand_index,j=card_index;i<hand_index+num;i++,j++)
hand[i]=card[j];
hand_index+=num;
card_index+=num;
}
void Pig::GetExpose(int num,int id)//收到其他玩家的身份信息,num:玩家身份,id:玩家序号
{
if(role==0&&IsEnemy(id))//如果该玩家是忠臣但是被主公当作了类反猪,那么需要从敌人数组删除而加入朋友数组
{
if(num==1)
{
for(int i=0;i<enemy_index;i++)
{
if(enemy[i]==id)
{
for(int j=i;j<enemy_index-1;j++)
enemy[j]=enemy[j+1];
enemy_index--;
break;
}
}
friends[friends_index]=id;
friends_index++;
}
return;
}
if(num==0||num==1)
{
if(role==1||role==0)
{
friends[friends_index]=id;
friends_index++;
}
else
{
enemy[enemy_index]=id;
enemy_index++;
}
}
else
{
if(role==0||role==1)
{
enemy[enemy_index]=id;
enemy_index++;
}
else
{
friends[friends_index]=id;
friends_index++;
}
}
}
bool Pig::IsEnemy(int n)
{
for(int i=0;i<enemy_index;i++)
{
if(enemy[i]==n) return true;
}
return false;
}
bool Pig::IsFriends(int n)
{
for(int i=0;i<friends_index;i++)
{
if(friends[i]==n) return true;
}
return false;
}
void Pig::GetDead(int num)//有玩家死亡时,需要将其从敌人/朋友队列中删除
{
if(IsFriends(num))
{
for(int i=0;i<friends_index;i++)
{
if(friends[i]==num)
{
for(int j=i;j<friends_index-1;j++)
friends[j]=friends[j+1];
friends_index--;
break;
}
}
}
else if(IsEnemy(num))
{
for(int i=0;i<enemy_index;i++)
{
if(enemy[i]==num)
{
for(int j=i;j<enemy_index-1;j++)
enemy[j]=enemy[j+1];
enemy_index--;
break;
}
}
}
}
void Pig::Play()//出牌阶段
{
int fuck_num=0;//记录出杀的次数
for(int i=0;i<hand_index;)
{
if(hand[i]=='P')
{
if(blood<4)
{
blood++;
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
}
else i++;
continue;
}
else if(hand[i]=='K')
{
if(fuck_num==1&&!equipment)
{
i++;
continue;
}
else
{
int target;
for(int j=id+1;j<id+player_index;j++)
{
if(player[j%player_index]->IsSuvived())
{
target=j%player_index;
break;
}
}
if(IsEnemy(target))
{
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
fuck_num++;
player[target]->GetFucked(this->id);
if(!exposed)//杀会导致亮明身份
{
exposed=true;
for(int j=0;j<player_index;j++)
{
if(player[j]->IsSuvived()) player[j]->GetExpose(this->role,this->id);
}
}
}
else i++;
continue;
}
}
else if(hand[i]=='F')
{
int target=-1;
if(role==2) target=0;
else
{
for(int j=this->id+1;j<this->id+player_index;j++)
{
if(player[j%player_index]->IsSuvived()&&IsEnemy(j%player_index))
{
target=j%player_index;
break;
}
}
}
if(target==-1)
{
i++;
continue;
}
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
if(!exposed)//决斗会导致亮明身份
{
exposed=true;
for(int j=0;j<player_index;j++)
{
if(player[j]->IsSuvived()) player[j]->GetExpose(this->role,this->id);
}
}
bool flag=false;
for(int j=this->id+1;j<this->id+player_index;j++)//询问无懈可击
{
if(player[j%player_index]->IsSuvived()&&player[j%player_index]->Offer(target,0))
{
flag=true;
break;
}
}
if(!flag)
{
player[target]->GetDuel(this->id);
}
i=0;
continue;
}
else if(hand[i]=='N')
{
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
for(int j=this->id+1;j<player_index+this->id;j++)
{
if(player[j%player_index]->IsSuvived())
{
bool flag=false;
for(int k=this->id;k<this->id+player_index;k++)//询问无懈可击
{
if(player[k%player_index]->IsSuvived()&&player[k%player_index]->Offer(j%player_index,0))
{
flag=true;
break;
}
}
if(!flag)
{
player[j%player_index]->GetAOE1(this->id);
}
}
}
i=0;
continue;
}
else if(hand[i]=='W')
{
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
for(int j=this->id+1;j<this->id+player_index;j++)
{
if(player[j%player_index]->IsSuvived())
{
bool flag=false;
for(int k=this->id;k<this->id+player_index;k++)
{
if(player[k%player_index]->IsSuvived()&&player[k%player_index]->Offer(j%player_index,0))
{
flag=true;
break;
}
}
if(!flag)
{
player[j%player_index]->GetAOE2(this->id);
}
}
}
i=0;
continue;
}
else if(hand[i]=='Z')
{
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
equipment=true;
i=0;
continue;
}
else
{
i++;
continue;
}
}
}
void Pig::GetFucked(int source)
{
bool flag=false;
for(int i=0;i<hand_index;i++)
{
if(hand[i]=='D')
{
flag=true;
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
break;
}
}
if(!flag)
{
SubBlood(source);
}
}
void Pig::SubBlood(int source)
{
blood--;
if(blood==0)
{
bool flag=false;
for(int i=0;i<hand_index;i++)
{
if(hand[i]=='P')
{
flag=true;
for(int j=i;j<hand_index-1;j++)
hand[j]=hand[j+1];
hand_index--;
blood++;
break;
}
}
if(!flag)
{
suvived=false;
hand_index=0;//手牌数清零
for(int i=0;i<player_index;i++)//向其他玩家宣告死亡
{
if(player[i]->IsSuvived()) player[i]->GetDead(this->id);
}
en->GetDead(this->role);//向游戏结束类传递死亡信息
if(this->role==2)
{
player[source]->AddHand(3);
}
if(this->role==1&&player[source]->GetRole()==0)
{
player[source]->Punish();
}
}
}
}
int main()
{
int N,M;
cin>>N>>M;
Pig* pig[NMAX];
int Fnums=0;
for(int i=0;i<N;i++)
{
string role;
cin>>role;
if(role=="FP") Fnums++;
char card1,card2,card3,card4;
cin>>card1>>card2>>card3>>card4;
pig[i]=new Pig(role,card1,card2,card3,card4,i);
}
for(int i=0;i<M;i++)
{
cin>>card[i];
}
for(int i=0;i<N;i++)
{
pig[i]->GetPlayer(N,pig);
pig[i]->GetExpose(0,0);
}
End en(Fnums,N,pig);
for(int i=0;i<N;i++)
{
pig[i]->AddEnd(&en);
}
while(!en.IsEnd())
{
for(int i=0;i<N;i++)
{
if(pig[i]->IsSuvived())
{
pig[i]->AddHand(2);
pig[i]->Play();
}
}
}
en.Output();
return 0;
}