三国杀游戏模拟器:用C++预测游戏结果
这是一个用C++模拟三国杀游戏的程序,可以根据玩家角色、手牌和牌堆信息预测游戏最终的结果,并输出玩家的最终手牌状态。
《三国杀》是一种多人牌类回合制游戏,一共有3种角色:主公,忠臣,反贼。每局游戏主公有且只有1人,忠臣和反贼可以有多人,每个人扮演1种角色。
游戏目的:
- 主公(ZG):自己存活的情况下消灭所有的反贼。
- 忠臣(ZC):不惜一切保护主公,胜利条件与主公相同。
- 反贼(FZ):杀死主公。
游戏过程:
- 游戏开始时,每个玩家手里都会有4张牌,且体力上限和初始体力都是4。
- 开始游戏时,从主公开始,按照逆时针方向(数据中就是按照编号从1, 2, 3 ...... n , 1 ......的顺序)依次行动。
- 每个玩家自己的回合可以分为 2 个阶段:
- 摸牌阶段:从牌堆顶部摸2张牌,依次放到手牌的最右边;
- 出牌阶段:你可以使用任意张牌,每次使用牌的时候都使用最靠左的能够使用的牌。当然,要满足如下规则:
- 如果没有诸葛连弩,每个出牌阶段只能使用1次'杀'来攻击;
- 任何牌被使用后被弃置(武器是装备上);被弃置的牌以后都不能再用,即与游戏无关。
各种牌介绍:
每张手牌用1个字母表示,字母代表牌的种类。
基本牌:
-
'桃(P)':在自己的回合内,如果自己的体力值不等于体力上限,那么使用1个桃可以为自己补充1点体力,否则不能使用桃;桃只能对自己使用;在自己的回合外,如果自己的血变为0或者更低,那么也可以使用。
-
'杀(K)':在自己的回合内,对攻击范围内除自己以外的1名角色使用。如果没有被'闪'抵消,则造成1点伤害。无论有无武器,杀的攻击范围都是1。
-
'闪(D)':当你受到杀的攻击时,可以弃置1张闪来抵消杀的效果。
锦囊牌:
-
'决斗(F)':出牌阶段,对除自己以外任意1名角色使用,由目标角色先开始,自己和目标角色轮流弃置1张杀,首先没有杀可弃的一方受到1点伤害,另一方视为此伤害的来源。
-
'南蛮入侵(N)':出牌阶段,对除你以外所有角色使用,按逆时针顺序从使用者下家开始依次结算,除非弃置1张杀,否则受到1点伤害。
-
'万箭齐发(W)':和南蛮入侵类似,不过要弃置的不是杀而是闪。
-
'无懈可击(J)':在目标锦囊生效前抵消其效果。每次有1张锦囊即将生效时,从使用这张锦囊的猪开始,按照逆时针顺序,依次得到使用无懈可击的机会;效果:用于决斗时,决斗无效并弃置;用于南蛮入侵或万箭齐发时,当结算到某个角色时才能使用,当前角色不需弃置牌并且不会受到伤害(仅对1个角色产生效果);用于无懈可击时,成为目标的无懈可击被无效。
装备牌:
- '诸葛连弩(Z)':武器,攻击范围1 ,出牌阶段你可以使用任意张杀; 同一时刻最多只能装1把武器;如果先前已经有了1把武器,那么之后再装武器的话,会弃置以前的武器来装现在的武器。
特殊事件及概念解释:
- 伤害来源:杀、南蛮入侵、万箭齐发的伤害来源均是使用该牌的人,决斗的伤害来源如上;
- 距离:两个人的距离定义为沿着逆时针方向间隔的人数+1 。即初始时1和2的距离为1,但是2和1的距离就是n-1。注意一个角色的死亡会导致一些任距离的改变;
- 玩家死亡:如果该玩家的体力降到0或者更低,并且自己手中没有足够的桃使得自己的体力值回到1,那么就死亡了,死亡后所有的牌(装备区,手牌区)被弃置;
- 奖励与惩罚:反贼死亡时,最后一个伤害来源处(即使是反贼)立即摸3张牌。忠臣死亡时,如果最后一个伤害来源是主公,那么主公所有装备牌、手牌被弃置。
注意:一旦达成胜利条件,游戏立刻结束,因此即使会摸3张牌或者还有牌可以用也不用执行了。
现在,我们已经知道每个人的角色、手牌,还有牌堆初始情况,并且假设每个角色会按照如下的行为准则进行游戏,你需要做的就是告诉 iPig 最后的结果。
几种行为:
- 献殷勤:使用无懈可击挡下南蛮入侵、万箭齐发、决斗;使用无懈可击抵消表敌意;
- 表敌意:对某个角色使用杀、决斗;使用无懈可击抵消献殷勤;
- 跳忠:即通过行动表示自己是忠臣。跳忠行动就是对主公或对某个已经跳忠的人献殷勤,或者对某个已经跳反的人表敌意;
- 跳反:即通过行动表示自己是反贼。跳反行动就是对主公或对某个已经跳忠的人表敌意,或者对某个已经跳反的人献殷勤。
注意:忠臣不会跳反,反贼也不会跳忠;不管是忠臣还是反贼,能够跳必然跳。
行动准则:
共性:
- 每个角色如果手里有桃且生命值未满,那么必然吃掉;
- 有南蛮入侵、万箭齐发、必然使用;有装备必然装上;
- 受到杀时,有闪必然弃置;
- 响应南蛮入侵或者万箭齐发时候,有杀/闪必然弃置;
- 不会对未表明身份的人献殷勤(包括自己)。
特性:
- 主公:
- 主公会认为「没有跳身份,且用南蛮入侵 / 万箭齐发对自己造成伤害的人」是“类反贼”(没伤害到不算,注意类反贼并没有表明身份),如果之后跳了,那么主猪会重新认识这个人;
- 对于每种表敌意的方式,对逆时针方向能够执行到的第一只类反贼或者已跳反贼表;如果没有,那么就不表敌意;
- 决斗时会不遗余力弃置杀;
- 如果能对已经跳忠的人或自己献殷勤,那么一定献;如果能够对已经跳反的人表敌意,那么一定表。
- 忠臣:
- 对于每种表敌意的方式,对「逆时针方向能够执行到的第一只已经跳反的人」表,如果没有,那么就不表敌意;
- 决斗时,如果对方是主公,那么不会弃置杀,否则,会不遗余力弃置杀;
- 如果有机会对主公或者已经跳忠的人献殷勤,那么一定献。
- 反猪:
- 对于每种表敌意的方式,如果有机会则对主公表,否则,对「逆时针方向能够执行到的第一个已经跳忠的人」表,如果没有,那么就不表敌意;
- 决斗时会不遗余力弃置杀;
- 如果有机会对已经跳反的人献殷勤,那么一定献。
限于 iPig 只会用 C++ 语言写 A + B,他请你用C++语言来帮他预测最后的结果。
输入格式:
输入文件第一行包含两个正整数n (2 <= n <= 10) 和 m (m <= 2000) ,分别代表玩家数和牌堆中牌的数量。数据保证牌的数量够用。 接下来 n 行,每行 5 个字符串,依次表示对第 i 个人的角色和初始 4 张手牌描述。编号为 1 的肯定是主公。 再接下来一行,一共 m 个字符串,按照从牌堆顶部到牌堆底部的顺序描述每张牌。 注意:所有的相邻的两个字符串都严格用 1 个空格隔开,行尾没有多余空格。
输出格式:
输出数据第一行包含一个字符串代表游戏结果。如果是主公胜利,那么输出 MP ,否则输出 FP 。数据保证游戏总会结束。 接下来 n 行,第 i 行是对第 i 只猪的手牌描述(注意只需要输出手牌),按照手牌从左往右的顺序输出,相邻两张牌用 1 个空格隔开,行末尾没有多余空格。如果这个人已阵亡,那么只要输出 DEAD 即可。 注意:如果要输出手牌而没有手牌的话,那么只需输出 1 个空行。 由于数据问题,若牌堆已空,按照每次抽牌抽到的都是最后一张。
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
// 定义角色类型
enum Role {
ZG, // 主公
ZC, // 忠臣
FZ // 反贼
};
// 定义牌的种类
enum Card {
P, // 桃
K, // 杀
D, // 闪
F, // 决斗
N, // 南蛮入侵
W, // 万箭齐发
J, // 无懈可击
Z // 诸葛连弩
};
// 定义玩家结构体
struct Player {
Role role; // 角色
int health; // 当前体力值
int maxHealth; // 体力上限
vector<Card> handCards; // 手牌
vector<Card> equipCards; // 装备区的牌
};
// 定义牌堆
queue<Card> cardPile;
// 判断是否是基本牌
bool isBasicCard(Card card) {
return card == P || card == K || card == D;
}
// 判断是否是锦囊牌
bool isSkillCard(Card card) {
return card == F || card == N || card == W || card == J;
}
// 判断是否是装备牌
bool isEquipCard(Card card) {
return card == Z;
}
// 判断是否是杀
bool isSha(Card card) {
return card == K || card == Z;
}
// 判断是否是闪
bool isShan(Card card) {
return card == D;
}
// 判断是否是桃
bool isTao(Card card) {
return card == P;
}
// 判断是否是决斗
bool isDuel(Card card) {
return card == F;
}
// 判断是否是南蛮入侵
bool isNanMan(Card card) {
return card == N;
}
// 判断是否是万箭齐发
bool isWanJian(Card card) {
return card == W;
}
// 判断是否是无懈可击
bool isWuXie(Card card) {
return card == J;
}
// 判断是否是诸葛连弩
bool isZhuGe(Card card) {
return card == Z;
}
// 判断是否是主公
bool isZhuGong(Role role) {
return role == ZG;
}
// 判断是否是忠臣
bool isZhongChen(Role role) {
return role == ZC;
}
// 判断是否是反贼
bool isFanZei(Role role) {
return role == FZ;
}
// 初始化牌堆
void initCardPile(int m, const vector<Card>& cards) {
for (int i = m - 1; i >= 0; i--) {
cardPile.push(cards[i]);
}
}
// 初始化玩家
void initPlayers(int n, const vector<vector<Card>>& playerCards, vector<Player>& players) {
for (int i = 0; i < n; i++) {
Player player;
player.role = static_cast<Role>(i);
player.health = 4;
player.maxHealth = 4;
player.handCards = playerCards[i];
players.push_back(player);
}
}
// 获取下一个玩家的编号
int getNextPlayer(int curPlayer, int n) {
return (curPlayer + 1) % n;
}
// 获取上一个玩家的编号
int getPrevPlayer(int curPlayer, int n) {
return (curPlayer - 1 + n) % n;
}
// 判断是否存在闪
bool hasShan(const Player& player) {
for (Card card : player.handCards) {
if (isShan(card)) {
return true;
}
}
return false;
}
// 判断是否存在杀
bool hasSha(const Player& player) {
for (Card card : player.handCards) {
if (isSha(card)) {
return true;
}
}
return false;
}
// 判断是否存在桃
bool hasTao(const Player& player) {
for (Card card : player.handCards) {
if (isTao(card)) {
return true;
}
}
return false;
}
// 判断是否可以对目标玩家使用杀
bool canUseSha(const Player& player, const Player& target, const vector<Player>& players) {
// 没有诸葛连弩,每个出牌阶段只能使用一次杀
if (!isZhuGe(player.equipCards[0])) {
for (Card card : player.handCards) {
if (isSha(card)) {
return false;
}
}
}
// 杀的攻击范围是 1
int distance = getNextPlayer(player, players.size()) - target;
if (distance == 0 || distance == players.size() - 1) {
return true;
}
return false;
}
// 移除玩家手牌中的指定牌
void removeCard(vector<Card>& handCards, Card card) {
for (int i = 0; i < handCards.size(); i++) {
if (handCards[i] == card) {
handCards.erase(handCards.begin() + i);
return;
}
}
}
// 玩家摸牌
void drawCard(Player& player) {
if (!cardPile.empty()) {
player.handCards.push_back(cardPile.front());
cardPile.pop();
} else {
// 牌堆为空,模拟抽取最后一张牌
player.handCards.push_back(cardPile.back());
}
}
// 玩家使用桃
bool useTao(Player& player) {
if (hasTao(player) && player.health < player.maxHealth) {
removeCard(player.handCards, P);
player.health++;
return true;
}
return false;
}
// 玩家使用杀
bool useSha(Player& player, Player& target, vector<Player>& players) {
if (canUseSha(player, target, players)) {
// 使用杀
for (int i = 0; i < player.handCards.size(); i++) {
if (isSha(player.handCards[i])) {
removeCard(player.handCards, player.handCards[i]);
// 判断目标玩家是否有闪
if (hasShan(target)) {
// 弃置闪
removeCard(target.handCards, D);
} else {
// 造成伤害
target.health--;
}
break;
}
}
return true;
}
return false;
}
// 玩家使用决斗
bool useDuel(Player& player, Player& target, vector<Player>& players) {
if (hasSha(player) && hasSha(target)) {
// 轮流弃置杀
while (hasSha(player) && hasSha(target)) {
removeCard(player.handCards, K);
removeCard(target.handCards, K);
}
// 优先弃置杀的人获胜
if (hasSha(player)) {
target.health--;
} else {
player.health--;
}
return true;
}
return false;
}
// 玩家使用南蛮入侵
bool useNanMan(Player& player, vector<Player>& players) {
if (hasSha(player)) {
// 按逆时针顺序结算
int curPlayer = getNextPlayer(player, players.size());
while (curPlayer != player) {
// 判断当前玩家是否有杀
if (hasSha(players[curPlayer])) {
// 弃置杀
removeCard(players[curPlayer].handCards, K);
} else {
// 造成伤害
players[curPlayer].health--;
}
curPlayer = getNextPlayer(curPlayer, players.size());
}
return true;
}
return false;
}
// 玩家使用万箭齐发
bool useWanJian(Player& player, vector<Player>& players) {
if (hasShan(player)) {
// 按逆时针顺序结算
int curPlayer = getNextPlayer(player, players.size());
while (curPlayer != player) {
// 判断当前玩家是否有闪
if (hasShan(players[curPlayer])) {
// 弃置闪
removeCard(players[curPlayer].handCards, D);
} else {
// 造成伤害
players[curPlayer].health--;
}
curPlayer = getNextPlayer(curPlayer, players.size());
}
return true;
}
return false;
}
// 玩家使用无懈可击
bool useWuXie(Player& player, Player& target, vector<Player>& players, Card skill) {
if (hasWuXie(player)) {
removeCard(player.handCards, J);
// 抵消效果
if (skill == F) {
// 决斗无效并弃置
for (int i = 0; i < target.handCards.size(); i++) {
if (target.handCards[i] == F) {
removeCard(target.handCards, F);
break;
}
}
} else if (skill == N || skill == W) {
// 当前角色不需弃置牌并且不会受到伤害
int curPlayer = getNextPlayer(player, players.size());
while (curPlayer != target) {
curPlayer = getNextPlayer(curPlayer, players.size());
}
// 不需要弃置牌
// 不会受到伤害
} else if (skill == J) {
// 成为目标的无懈可击被无效
// 不做任何处理
}
return true;
}
return false;
}
// 玩家装备诸葛连弩
bool equipZhuGe(Player& player) {
if (hasZhuGe(player)) {
for (int i = 0; i < player.handCards.size(); i++) {
if (isZhuGe(player.handCards[i])) {
// 装备诸葛连弩
player.equipCards[0] = player.handCards[i];
removeCard(player.handCards, player.handCards[i]);
return true;
}
}
}
return false;
}
// 玩家出牌阶段
void playCards(Player& player, vector<Player>& players) {
// 使用桃
if (useTao(player)) {
return;
}
// 装备诸葛连弩
if (equipZhuGe(player)) {
return;
}
// 使用南蛮入侵
if (isNanMan(player.handCards[0]) && useNanMan(player, players)) {
return;
}
// 使用万箭齐发
if (isWanJian(player.handCards[0]) && useWanJian(player, players)) {
return;
}
// 使用决斗
if (isDuel(player.handCards[0]) && useDuel(player, players[getNextPlayer(player, players.size())], players)) {
return;
}
// 使用杀
if (useSha(player, players[getNextPlayer(player, players.size())], players)) {
return;
}
}
// 玩家回合
void playerTurn(Player& player, vector<Player>& players) {
// 摸牌阶段
drawCard(player);
drawCard(player);
// 出牌阶段
playCards(player, players);
}
// 判断游戏是否结束
bool isGameOver(const vector<Player>& players) {
// 主公存活且反贼全部阵亡
if (isZhuGong(players[0].role) && players[0].health > 0) {
for (int i = 1; i < players.size(); i++) {
if (isFanZei(players[i].role) && players[i].health > 0) {
return false;
}
}
return true;
}
// 反贼阵亡
if (players[0].health <= 0) {
return true;
}
return false;
}
// 游戏主逻辑
int main() {
int n, m;
cin >> n >> m;
vector<vector<Card>> playerCards(n);
vector<Player> players;
vector<Card> cards(m);
// 读取玩家信息
for (int i = 0; i < n; i++) {
string role, card1, card2, card3, card4;
cin >> role >> card1 >> card2 >> card3 >> card4;
if (role == "ZG") {
playerCards[i].push_back(Z);
} else if (role == "ZC") {
playerCards[i].push_back(P);
playerCards[i].push_back(K);
playerCards[i].push_back(D);
playerCards[i].push_back(F);
} else if (role == "FZ") {
playerCards[i].push_back(K);
playerCards[i].push_back(D);
playerCards[i].push_back(N);
playerCards[i].push_back(W);
}
}
// 读取牌堆信息
for (int i = 0; i < m; i++) {
string cardStr;
cin >> cardStr;
if (cardStr == "P") {
cards[i] = P;
} else if (cardStr == "K") {
cards[i] = K;
} else if (cardStr == "D") {
cards[i] = D;
} else if (cardStr == "F") {
cards[i] = F;
} else if (cardStr == "N") {
cards[i] = N;
} else if (cardStr == "W") {
cards[i] = W;
} else if (cardStr == "J") {
cards[i] = J;
} else if (cardStr == "Z") {
cards[i] = Z;
}
}
// 初始化牌堆和玩家
initCardPile(m, cards);
initPlayers(n, playerCards, players);
// 游戏循环
int curPlayer = 0;
while (!isGameOver(players)) {
// 玩家回合
playerTurn(players[curPlayer], players);
// 下一个玩家
curPlayer = getNextPlayer(curPlayer, players.size());
}
// 输出游戏结果
if (players[0].health > 0) {
cout << "MP" << endl;
} else {
cout << "FP" << endl;
}
// 输出玩家手牌
for (int i = 0; i < players.size(); i++) {
if (players[i].health > 0) {
for (int j = 0; j < players[i].handCards.size(); j++) {
if (j > 0) {
cout << " ";
}
switch (players[i].handCards[j]) {
case P:
cout << "P";
break;
case K:
cout << "K";
break;
case D:
cout << "D";
break;
case F:
cout << "F";
break;
case N:
cout << "N";
break;
case W:
cout << "W";
break;
case J:
cout << "J";
break;
case Z:
cout << "Z";
break;
}
}
cout << endl;
} else {
cout << "DEAD" << endl;
}
}
return 0;
}
代码说明:
-
数据结构:
Role枚举类型:定义了主公、忠臣、反贼三种角色。Card枚举类型:定义了所有牌的类型,包括基本牌、锦囊牌和装备牌。Player结构体:存储玩家信息,包括角色、体力值、手牌、装备牌等。cardPile队列:存储牌堆。
-
函数功能:
isBasicCard、isSkillCard、isEquipCard:判断牌的种类。isSha、isShan、isTao、isDuel、isNanMan、isWanJian、isWuXie、isZhuGe:判断牌的具体类型。isZhuGong、isZhongChen、isFanZei:判断角色类型。initCardPile:初始化牌堆。initPlayers:初始化玩家信息。getNextPlayer、getPrevPlayer:获取下一个或上一个玩家的编号。hasShan、hasSha、hasTao:判断玩家手牌中是否包含指定牌。canUseSha:判断玩家是否可以对目标玩家使用杀。removeCard:移除玩家手牌中的指定牌。drawCard:玩家摸牌。useTao、useSha、useDuel、useNanMan、useWanJian、useWuXie、equipZhuGe:玩家使用各种牌的逻辑。playCards:玩家出牌阶段的逻辑。playerTurn:玩家回合的逻辑。isGameOver:判断游戏是否结束。
-
游戏逻辑:
- 主程序读取玩家角色、手牌和牌堆信息,初始化游戏环境。
- 使用循环进行游戏,每个玩家轮流执行回合操作。
- 每个玩家回合分为摸牌阶段和出牌阶段。
- 出牌阶段根据玩家角色、手牌和游戏规则决定使用哪张牌。
- 玩家使用牌后,根据牌的效果判断是否造成伤害、弃置牌或其他效果。
- 判定游戏是否结束,结束游戏后输出游戏结果和玩家手牌信息。
使用说明:
- 将代码保存为
.cpp文件,例如三国杀.cpp。 - 使用编译器编译代码,例如使用
g++ 三国杀.cpp -o 三国杀生成可执行文件三国杀。 - 运行可执行文件,例如
./三国杀,输入游戏数据,即可模拟游戏并输出结果。
注意:
- 代码只模拟了游戏基本规则和玩家行动逻辑,并没有实现所有三国杀游戏的复杂规则和卡牌效果。
- 代码中没有加入随机性,每个玩家的行动都是根据游戏规则和设定好的行为准则进行的。
- 代码中使用了简单的字符来代表牌的种类,可以根据需要进行修改。
改进建议:
- 可以加入随机性,让游戏更加真实。
- 可以实现更多三国杀游戏的复杂规则和卡牌效果。
- 可以增加游戏界面和图形化输出,提升游戏体验。
- 可以使用更复杂的算法和数据结构来优化代码性能。
希望这个程序能够帮助您更好地理解三国杀游戏规则和逻辑,并进行游戏模拟和预测。
原文地址: https://www.cveoy.top/t/topic/pWB3 著作权归作者所有。请勿转载和采集!