恶魔轮盘赌(AI制作)
2026-08-15 14:11:17
发布于:浙江
#include<bits/stdc++.h>
using namespace std;
enum Item { MAGNIFYING_GLASS=0, HANDCUFFS, CIGARETTE, BEER, SAW, ITEM_COUNT };
const string itemNames[]={"放大镜","手铐","烟","啤酒","锯子"};
struct Player{
string name;
int health,maxHealth;
vector<int> items;
Player(string n,int h):name(n),health(h),maxHealth(h){items.resize(ITEM_COUNT,0);}
void addItem(Item item,int count=1){items[item]+=count;}
bool hasItem(Item item)const{return items[item]>0;}
void useItem(Item item){if(hasItem(item))items[item]--;}
void heal(int amount){health=min(health+amount,maxHealth);}
};
struct GameState{
vector<bool> chamber;
int currentSlot;
bool knownNextType;
int damageMultiplier;
bool handcuffed;
int handcuffTurns;
GameState():currentSlot(0),knownNextType(false),damageMultiplier(1),handcuffed(false),handcuffTurns(0){}
};
void clearScreen(){
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
void pause(){
cout<<"\n按回车键继续...";
cin.ignore();
cin.get();
}
int randomInt(int mn,int mx){return rand()%(mx-mn+1)+mn;}
void loadChamber(GameState& st,int realCount,int fakeCount){
st.chamber.clear();
st.currentSlot=0;
st.knownNextType=false;
st.damageMultiplier=1;
for(int i=0;i<realCount;i++)st.chamber.push_back(true);
for(int i=0;i<fakeCount;i++)st.chamber.push_back(false);
random_shuffle(st.chamber.begin(),st.chamber.end());
cout<<"装弹完成: "<<realCount<<" 发实弹, "<<fakeCount<<" 发空弹."<<endl;
}
void displayStatus(const Player& p1,const Player& p2,const GameState& st,bool isP1Turn){
clearScreen();
cout<<"\n=== 恶魔轮盘赌 ===\n";
cout<<left<<setw(15)<<p1.name<<" HP: "<<p1.health<<"/"<<p1.maxHealth
<<right<<setw(20)<<p2.name<<" HP: "<<p2.health<<"/"<<p2.maxHealth<<"\n";
cout<<"当前回合: "<<(isP1Turn?p1.name:p2.name)<<"\n";
if(st.damageMultiplier>1)cout<<"*** 下一发伤害加倍! ***\n";
if(st.handcuffed)cout<<"*** "<<(isP1Turn?p1.name:p2.name)<<" 被手铐铐住了! 本回合无法行动! ***\n";
auto showItems=[&](const Player& p){
for(int i=0;i<ITEM_COUNT;i++)if(p.items[i]>0)cout<<"["<<itemNames[i]<<":"<<p.items[i]<<"] ";
cout<<"\n";
};
cout<<p1.name<<" 道具: ";showItems(p1);
cout<<p2.name<<" 道具: ";showItems(p2);
cout<<"========================\n";
}
int getIntInput(const string& prompt,int mn,int mx){
int v;
while(true){
cout<<prompt;
if(cin>>v&&v>=mn&&v<=mx)return v;
cin.clear();
cin.ignore(10000,'\n');
cout<<"输入无效,请重新输入 ("<<mn<<"-"<<mx<<")\n";
}
}
bool shoot(Player& shooter,Player& target,GameState& st,bool shootSelf){
if(st.chamber.empty())return false;
bool isReal=st.chamber[st.currentSlot];
st.currentSlot++;
cout<<"\n>>> "<<shooter.name<<" 扣下扳机... <<<\n";
this_thread::sleep_for(chrono::milliseconds(800));
if(isReal){
int dmg=1*st.damageMultiplier;
if(shootSelf){shooter.health-=dmg;cout<<shooter.name<<" 射中了自己! -"<<dmg<<" HP\n";}
else{target.health-=dmg;cout<<shooter.name<<" 射中了 "<<target.name<<"! -"<<dmg<<" HP\n";}
st.damageMultiplier=1;
return true;
}else{
cout<<"空弹!\n";
st.damageMultiplier=1;
return false;
}
}
void playerTurn(Player& me,Player& opp,GameState& st){
while(true){
if(me.health<=0||opp.health<=0)return;
displayStatus(me,opp,st,true);
if(st.handcuffed){
cout<<"你被手铐铐住了! 本回合无法行动,跳过了!\n";
st.handcuffed=false;
pause();
return;
}
cout<<"1.射对手 2.射自己\n";
vector<Item> avail;
int off=3;
for(int i=0;i<ITEM_COUNT;i++)if(me.hasItem((Item)i)){
cout<<off<<".使用 "<<itemNames[i]<<"\n";
avail.push_back((Item)i);
off++;
}
int ch=getIntInput("选项: ",1,off-1);
if(ch==1){shoot(me,opp,st,false);break;}
else if(ch==2){bool r=shoot(me,opp,st,true);if(!r){pause();continue;}break;}
else{
Item it=avail[ch-3];
me.useItem(it);
if(it==MAGNIFYING_GLASS){
if(st.currentSlot<(int)st.chamber.size()){
bool nxt=st.chamber[st.currentSlot];
cout<<"下一发: "<<(nxt?"实弹":"空弹")<<"\n";
st.knownNextType=true;
}else cout<<"弹仓已空\n";
pause();
}else if(it==HANDCUFFS){
st.handcuffed=true;
cout<<"你使用了手铐! 对方下回合无法行动!\n";
pause();
}else if(it==CIGARETTE){
me.heal(1);cout<<"回复1点HP\n";pause();
}else if(it==BEER){
if(!st.chamber.empty()){
bool ej=st.chamber[st.currentSlot];
st.currentSlot++;
cout<<"退出一颗"<<(ej?"实弹":"空弹")<<"\n";
st.damageMultiplier=1;
st.knownNextType=false;
}pause();
}else if(it==SAW){
st.damageMultiplier=2;
cout<<"下一发伤害加倍!\n";pause();
}
}
}
}
void aiTurn(Player& ai,Player& player,GameState& st){
if(ai.health<=0||player.health<=0)return;
displayStatus(player,ai,st,false);
if(st.handcuffed){
cout<<ai.name<<" 被手铐铐住了! 本回合无法行动!\n";
st.handcuffed=false;
pause();
return;
}
cout<<ai.name<<" 思考中...\n";
this_thread::sleep_for(chrono::milliseconds(1000));
int rem=st.chamber.size()-st.currentSlot;
int realLeft=0;
for(int i=st.currentSlot;i<(int)st.chamber.size();i++)if(st.chamber[i])realLeft++;
bool nxtReal=(rem>0&&realLeft>rem/2);
if(ai.hasItem(HANDCUFFS)&&rem>0){
ai.useItem(HANDCUFFS);
st.handcuffed=true;
cout<<ai.name<<" 使用了手铐! 你下回合无法行动!\n";pause();
return;
}
if(ai.hasItem(SAW)&&nxtReal){
ai.useItem(SAW);st.damageMultiplier=2;
cout<<ai.name<<" 使用锯子(伤害加倍)\n";pause();
}
if(ai.hasItem(CIGARETTE)&&ai.health<=2){
ai.useItem(CIGARETTE);ai.heal(1);
cout<<ai.name<<" 抽烟回血\n";pause();
}
if(ai.hasItem(MAGNIFYING_GLASS)&&!st.knownNextType&&rem>0){
ai.useItem(MAGNIFYING_GLASS);
nxtReal=st.chamber[st.currentSlot];
st.knownNextType=true;
cout<<ai.name<<" 使用放大镜\n";pause();
}
if(ai.hasItem(BEER)&&!nxtReal&&rem>0&&realLeft==0){
ai.useItem(BEER);
st.currentSlot++;
cout<<ai.name<<" 喝啤酒退空弹\n";pause();
}
if(st.knownNextType){
if(nxtReal){
if(st.damageMultiplier==2){shoot(ai,player,st,false);return;}
if(ai.hasItem(SAW)&&rem>0){ai.useItem(SAW);st.damageMultiplier=2;cout<<ai.name<<" 使用锯子\n";pause();}
shoot(ai,player,st,false);return;
}else{
bool r=shoot(ai,player,st,true);
if(!r){cout<<ai.name<<" 继续行动\n";pause();aiTurn(ai,player,st);}
return;
}
}
if(realLeft==0){shoot(ai,player,st,true);return;}
if(ai.health<=1&&realLeft>0){shoot(ai,player,st,false);return;}
if(player.health<=1&&realLeft>0){shoot(ai,player,st,false);return;}
bool shootSelf=false;
if(ai.health>player.health+1&&realLeft>rem/2)shootSelf=false;
else if(ai.health<=player.health&&realLeft<=rem/2)shootSelf=true;
else shootSelf=(realLeft<rem/2);
bool r=shoot(ai,player,st,shootSelf);
if(!r&&shootSelf){cout<<ai.name<<" 继续行动\n";pause();aiTurn(ai,player,st);}
}
void distributeItems(Player& p1,Player& p2){
int c1=randomInt(1,2),c2=randomInt(1,2);
for(int i=0;i<c1;i++)p1.addItem((Item)randomInt(0,ITEM_COUNT-1));
for(int i=0;i<c2;i++)p2.addItem((Item)randomInt(0,ITEM_COUNT-1));
}
int main(){
srand(time(0));
cout<<"欢迎进入恶魔轮盘赌!\n";
cout<<"1.PVP 2.PVE\n";
int mode=getIntInput("选择: ",1,2);
string n1,n2;
cout<<"你的名字: ";cin>>n1;
Player p1(n1,4);
Player ai("恶魔",4);
Player* p2Ptr;
if(mode==1){cout<<"对手名字: ";cin>>n2;p2Ptr=new Player(n2,4);}
else p2Ptr=&ai;
Player& p2=*p2Ptr;
bool isP1Turn=true;
int round=1;
while(p1.health>0&&p2.health>0){
cout<<"\n=== 第 "<<round<<" 轮 ===\n";
int real=randomInt(2,4),fake=randomInt(2,4);
GameState st;
loadChamber(st,real,fake);
distributeItems(p1,p2);
while(st.currentSlot<(int)st.chamber.size()&&p1.health>0&&p2.health>0){
if(isP1Turn){playerTurn(p1,p2,st);isP1Turn=false;}
else{
if(mode==1){playerTurn(p2,p1,st);}
else aiTurn(p2,p1,st);
isP1Turn=true;
}
}
if(p1.health>0&&p2.health>0){cout<<"双方存活,进入下一轮\n";pause();round++;}
}
cout<<"\n========================\n";
if(p1.health>0)cout<<p1.name<<" 获胜!\n";
else cout<<p2.name<<" 获胜!\n";
cout<<"========================\n";
if(mode==1)delete p2Ptr;
return 0;
}
全部评论 7
d
4天前 来自 浙江
1d
4天前 来自 浙江
1d
4天前 来自 浙江
1我去,这回看得清楚了
4天前 来自 浙江
1有 2 个较严重的运行期问题。我用 GCC 编译并做了边界验证,问题如下:
-
严重:弹仓会越界访问。
shoot()只判断st.chamber.empty(),但开枪后只是递增currentSlot,并不会从chamber删除元素。因此currentSlot == chamber.size()时,chamber仍然非空,随后执行st.chamber[st.currentSlot]就越界。
最典型情况是“最后一发为空弹且射自己”:玩家会continue,AI 会递归aiTurn(),之后可能再次开枪。
我用_GLIBCXX_DEBUG已实际复现out-of-bounds index。 啤酒退掉最后一颗子弹后也存在同类风险。 -
严重:
knownNextType会残留,AI 会把“猜测”当成“已知”。 开枪导致currentSlot++后,没有把knownNextType清零。 更关键的是GameState只保存“是否已知”,没有保存已知的是实弹还是空弹。
AI 下一次进入aiTurn()时重新用剩余实弹比例计算nxtReal,随后若旧的knownNextType==true,就把这个概率判断当成精确信息。 -
AI 使用啤酒后状态没有完整更新。 AI
currentSlot++后没有重新计算rem/realLeft/nxtReal,也没有像玩家路径那样清除knownNextType。后面的决策继续使用退弹前的数据;若退的是最后一发,还可直接触发上述越界。 -
手铐在人类与 AI 路径行为不一致。 玩家使用手铐后仍可继续使用道具/开枪;AI 使用手铐后却立即
return,等于主动结束自己的本回合。 如果设计意图是“道具不消耗行动”,这里明显不一致。 -
Linux/GCC 下存在直接编译问题。 自定义全局
void pause()会与 POSIX 的pause()冲突;我在当前 GCC/glibc 环境用 C17/C20 编译均得到ambiguating new declaration of 'void pause()'。 建议改名如waitForEnter()。 -
兼容性问题:
random_shuffle已过时。 该接口从 C14 起弃用、C17 标准中移除;当前 GCC 尚兼容但会产生警告,应改为std::shuffle+<random>。
另外有几个较轻的问题:
handcuffTurns从未使用,rand()%N存在分布偏差,system("cls"/"clear")可移植性较差。我建议の优先修复顺序:①弹仓越界 → ②已知子弹状态 → ③AI啤酒状态 → ④
pause()编译冲突。4天前 来自 浙江
0我求你了,不要用付费AI了
4天前 来自 浙江
0我给你指出了问题,建议你修正。
4天前 来自 浙江
0此外,你为什么认为我用了付费AI?
4天前 来自 浙江
0
-
d
4天前 来自 浙江
0d
4天前 来自 浙江
0





















有帮助,赞一个