总结整理
阅读指南 建议先拥有了200题以上的刷题经验再看 (没有也碾压我)
题目描述
《猪国杀》是一种多猪牌类回合制游戏,一共有三种角色:主猪,忠猪,反猪。每局游戏主猪有且只有一只,忠猪和反猪可以有多只,每只猪扮演一种角色。
游戏目的:
主猪(MP):自己存活的情况下消灭所有的反猪。
忠猪(ZP):不惜一切保护主猪,胜利条件与主猪相同。
反猪(FP):杀死主猪。
游戏过程:
游戏开始时候,每个玩家手里都会有 4 张牌,且体力上限和初始体力都是 4。
开始游戏时,从主猪开始,按照逆时针方向(数据中就是按照编号从 1,2,3..n,1.. 的顺序)依次行动。
每个玩家自己的回合可以分为 2 个阶段:
摸牌阶段:从牌堆顶部摸两张牌,依次放到手牌的最右边;
出牌阶段:你可以使用0张到任意张牌,每次使用牌的时候都使用最靠左的能够使用的牌。当然,要满足如下规则:
如果没有猪哥连弩,每个出牌阶段只能使用一次“杀”来攻击;
任何牌被使用后被弃置(武器是装备上);
被弃置的牌以后都不能再用,即与游戏无关;
各种牌介绍:
每张手牌用一个字母表示,字母代表牌的种类。
基本牌:
『桃(P)』:在自己的回合内,如果自己的体力值不等于体力上限,那么使用一个桃可以为自己补充一点体力,否则不能使用桃;桃只能对自己使用;在自己的回合外,如果自己的血变为 0 或者更低,那么也可以使用;
『杀(K)』:在自己的回合内,对攻击范围内除自己以外的一名角色使用。如果没有被『闪』抵消,则造成 1 点伤害。无论有无武器,杀的攻击范围都是 1;
『闪(D)』:当你受到杀的攻击时,可以弃置一张闪来抵消杀的效果;
锦囊牌:
『决斗(F)』:出牌阶段,对除自己以外任意一名角色使用,由目标角色先开始,自己和目标角色轮流弃置一张杀,首先没有杀可弃的一方受到 1 点伤害,另一方视为此伤害的来源;
『南猪入侵(N)』:出牌阶段,对除你以外所有角色使用,按逆时针顺序从使用者下家开始依次结算,除非弃置一张杀,否则受到 1 点伤害;
『万箭齐发(W)』:和南猪入侵类似,不过要弃置的不是杀而是闪;
『无懈可击(J)』:在目标锦囊生效前抵消其效果。每次有一张锦囊即将生效时,从使用这张锦囊的猪开始,按照逆时针顺序,依次得到使用无懈可击的机会;效果:用于决斗时,决斗无效并弃置;用于南猪入侵或万箭齐发时,当结算到某个角色时才能使用,当前角色不需弃置牌并且不会受到伤害(仅对一个角色产生效果);用于无懈可击时,成为目标的无懈可击被无效。
装备牌:
『猪哥连弩 (Z) 』:武器,攻击范围 1,出牌阶段你可以使用任意张杀;
同一时刻最多只能装一个武器;如果先前已经有了一把武器,那么之后再装武器的话,会弃置以前的武器来装现在的武器;
特殊事件及概念解释:
伤害来源:杀、南猪入侵、万箭齐发的伤害来源均是使用该牌的猪,决斗的伤害来源如上;
距离:两只猪的距离定义为沿着逆时针方向间隔的猪数 +1。即初始时 1 和 2 的距离为 1,但是 2 和 1 的距离就是 n−1。注意一个角色的死亡会导致一些猪距离的改变;
玩家死亡:如果该玩家的体力降到 0 或者更低,并且自己手中没有足够的桃使得自己的体力值回到 1,那么就死亡了,死亡后所有的牌(装备区,手牌区)被弃置;
奖励与惩罚:反猪死亡时,最后一个伤害来源处(即使是反猪)立即摸三张牌。忠猪死亡时,如果最后一个伤害来源是主猪,那么主猪所有装备牌、手牌被弃置;
注意,一旦达成胜利条件,游戏立刻结束,因此即使会摸 3 张牌或者还有牌可以用也不用执行了。
现在,我们已经知道每只猪的角色、手牌,还有牌堆初始情况,并且假设每个角色会按照如下的行为准则进行游戏,你需要做的就是告诉小猪 iPig 最后的结果。
几种行为:
献殷勤:使用无懈可击挡下南猪入侵、万箭齐发、决斗;使用无懈可击抵消表敌意;
表敌意:对某个角色使用杀、决斗;使用无懈可击抵消献殷勤;
跳忠:即通过行动表示自己是忠猪。跳忠行动就是对主猪或对某只已经跳忠的猪献殷勤,或者对某只已经跳反的猪表敌意;
跳反:即通过行动表示自己是反猪。跳反行动就是对主猪或对某只已经跳忠的猪表敌意,或者对某只已经跳反的猪献殷勤;
忠猪不会跳反,反猪也不会跳忠;不管是忠猪还是反猪,能够跳必然跳;
行动准则:
共性:每个角色如果手里有桃且生命值未满,那么必然吃掉;有南猪入侵、万箭齐发、必然使用;有装备必然装上;受到杀时,有闪必然弃置;响应南猪入侵或者万箭齐发时候,有杀/闪必然弃置;不会对未表明身份的猪献殷勤(包括自己);
特性:
主猪:主猪会认为没有跳身份,且用南猪入侵/万箭齐发对自己造成伤害的猪是“类反猪”(没伤害到不算,注意“类反猪”并没有表明身份),如果之后跳了,那么主猪会重新认识这只猪;对于每种表敌意的方式,对逆时针方向能够执行到的第一只“类反猪”或者已跳反猪表;如果没有,那么就不表敌意;决斗时会不遗余力弃置杀;如果能对已经跳忠的猪或自己献殷勤,那么一定献;如果能够对已经跳反的猪表敌意,那么一定表;
忠猪:对于每种表敌意的方式,对逆时针方向能够执行到的第一只已经跳反的猪表,如果没有,那么就不表敌意;决斗时,如果对方是主猪,那么不会弃置杀,否则,会不遗余力弃置杀;如果有机会对主猪或者已经跳忠的猪献殷勤,那么一定献;
反猪:对于每种表敌意的方式,如果有机会则对主猪表,否则,对逆时针方向能够执行到的第一只已经跳忠的猪表,如果没有,那么就不表敌意;决斗时会不遗余力弃置杀;如果有机会对已经跳反的猪献殷勤,那么一定献;
限于 iPig 只会用 P++ 语言写 A + B,他请你来帮他预测最后的结果。
注意
如果牌库被抽至只剩一张牌,则接下来的所有抽牌都会不断的抽到最后一张。
输入格式
第一行包含两个正整数 n 和 m,分别代表玩家数和牌堆中牌的数量。
接下来 n 行,每行 5 个字符串,依次表示对第 i 只猪的角色和初始 4 张手牌描述。编号为 1 的肯定是主猪。
再接下来一行,一共 m 个字符串,按照从牌堆顶部到牌堆底部的顺序描述每张牌。
所有的相邻的两个字符串都严格用 1 个空格隔开,行尾没有多余空格。
输出格式
第一行包含一个字符串代表游戏结果。
如果是主猪胜利,那么输出 MP,否则输出 FP。数据保证游戏总会结束。
接下来 n 行,第 i 行是对第 i 只猪的手牌描述(注意只需要输出手牌),按照手牌从左往右的顺序输出,相邻两张牌用一个空格隔开,行末尾没有多余空格。
如果这只猪已阵亡,那么只要输出 DEAD 即可。
注意如果要输出手牌而没有手牌的话,那么只需输出一个空行。
样例
input:
3 10
MP D D F F
ZP N N N D
FP J J J J
F F D D J J F F K D
print:
FP
DEAD
DEAD
J J J J J D
样例解释
第一回合主猪没有目标可以表敌意;接下来忠猪使用了 3 张南猪入侵,主猪掉了 3 点体力,并认为该角色为类反猪,3 号角色尽管手里有无懈可击,但是因为自己未表明身份,所以同样不能对自己用,乖乖掉 3 点体力;下一回合反猪无牌可出;接下来主猪对着类反猪爆发,使用 4 张决斗,忠猪死亡,结果主猪弃掉所有牌;下来反猪摸到一张杀直接杀死主猪获胜。
算法1
(模拟) $O(n^2)$
section 1:
主猪(MP):自己存活的情况下消灭所有的反猪。
忠猪(ZP):不惜一切保护主猪,胜利条件与主猪相同。
反猪(AP):杀死主猪。
section 2:
游戏开始时候,每个玩家手里都会有4张牌,且体力上限和初始体力都是4。
开始游戏时,从主猪开始,按照逆时针方向(数据中就是按照编号从1,2,3..n,1..的顺序)依次行动。
摸牌阶段:从牌堆顶部摸两张牌,依次放到手牌的最右边;
出牌阶段:你可以使用0张到任意张牌,每次使用牌的时候都使用最靠左的能够使用的牌。
当然,要满足如下规则:
1.如果没有猪哥连弩,每个出牌阶段只能使用一次“杀”来攻击;
2.任何牌被使用后被弃置(武器是装备上);
被弃置的牌以后都不能再用,即与游戏无关;
section 3:
『桃(P)』:在自己的回合内,如果自己的体力值不等于体力上限,那么使用一个桃可以为自己补充一点体力,否则不能使用桃;桃只能对自己使用;在自己的回合外,如果自己的血变为0或者更低,那么也可以使用;
『杀(K)』:在自己的回合内,对攻击范围内除自己以外的一名角色使用。如果没有被『闪』抵消,则造成1点伤害。无论有无武器,杀的攻击范围都是1;
『闪(D)』:当你受到杀的攻击时,可以弃置一张闪来抵消杀的效果;
『决斗(F)』:出牌阶段,对除自己以外任意一名角色使用,由目标角色先开始,自己和目标角色轮流弃置一张杀,首先没有杀可弃的一方受到1点伤害,另一方视为此伤害的来源;
『南猪入侵(N)』:出牌阶段,对除你以外所有角色使用,按逆时针顺序从使用者下家开始依次结算,除非弃置一张杀,否则受到1点伤害;
『万箭齐发(W)』:和南猪入侵类似,不过要弃置的不是杀而是闪;
『无懈可击(J)』:在目标锦囊生效前抵消其效果。每次有一张锦囊即将生效时,从使用这张锦囊的猪开始,按照逆时针顺序,依次得到使用无懈可击的机会;
效果:用于决斗时,决斗无效并弃置;用于南猪入侵或万箭齐发时,当结算到某个角色时才能使用,当前角色不需弃置牌并且不会受到伤害(仅对一个角色产生效果);用于无懈可击时,成为目标的无懈可击被无效。
『猪哥连弩(Z)』:武器,攻击范围1,出牌阶段你可以使用任意张杀;
同一时刻最多只能装一个武器;如果先前已经有了一把武器,那么之后再装武器的话,会弃置以前的武器来装现在的武器;
section 4:
伤害来源:杀、南猪入侵、万箭齐发的伤害来源均是使用该牌的猪,决斗的伤害来源如上;
距离:两只猪的距离定义为沿着逆时针方向间隔的猪数+1。即初始时1和2的距离为1,但是2和1的距离就是n-1。注意一个角色的死亡会导致一些猪距离的改变;
玩家死亡:如果该玩家的体力降到0或者更低,并且自己手中没有足够的桃使得自己的体力值回到1,那么就死亡了,死亡后所有的牌(装备区,手牌区)被弃置;
奖励与惩罚:反猪死亡时,最后一个伤害来源处(即使是反猪)立即摸三张牌。忠猪死亡时,如果最后一个伤害来源是主猪,那么主猪所有装备牌、手牌被弃置;
注意,一旦达成胜利条件,游戏立刻结束,因此即使会摸3张牌或者还有牌可以用也不用执行了。
section 5:
已经知道每只猪的角色、手牌,还有牌堆初始情况,并且假设每个角色会按照如下的行为准则进行游戏。
section 6:
献殷勤:使用无懈可击挡下南猪入侵、万箭齐发、决斗;使用无懈可击抵消表敌意;
表敌意:对某个角色使用杀、决斗;使用无懈可击抵消献殷勤;
跳忠:即通过行动表示自己是忠猪。跳忠行动就是对主猪或对某只已经跳忠的猪献殷勤,或者对某只已经跳反的猪表敌意;
跳反:即通过行动表示自己是反猪。跳反行动就是对主猪或对某只已经跳忠的猪表敌意,或者对某只已经跳反的猪献殷勤;
忠猪不会跳反,反猪也不会跳忠;不管是忠猪还是反猪,能够跳必然跳;
section 7:
行动准则(共性):每个角色如果手里有桃且生命值未满,那么必然吃掉;有南猪入侵、万箭齐发、必然使用;有装备必然装上;受到杀时,有闪必然弃置;响应南猪入侵或者万箭齐发时候,有杀/闪必然弃置;不会对未表明身份的猪献殷勤(包括自己);
section 8:
主猪:主猪会认为,没有跳身份且用南猪入侵/万箭齐发对自己造成伤害的猪是“类反猪”(没伤害到不算,注意“类反猪”并没有表明身份),如果之后跳了,那么主猪会重新认识这只猪;对于每种表敌意的方式,对逆时针方向能够执行到的第一只“类反猪”或者已跳反猪表;如果没有,那么就不表敌意;决斗时会不遗余力弃置杀;如果能对已经跳忠的猪或自己献殷勤,那么一定献;如果能够对已经跳反的猪表敌意,那么一定表;
忠猪:对于每种表敌意的方式,对逆时针方向能够执行到的第一只已经跳反的猪表,如果没有,那么就不表敌意;决斗时,如果对方是主猪,那么不会弃置杀,否则,会不遗余力弃置杀;如果有机会对主猪或者已经跳忠的猪献殷勤,那么一定献;
反猪:对于每种表敌意的方式,如果有机会则对主猪表,否则,对逆时针方向能够执行到的第一只已经跳忠的猪表,如果没有,那么就不表敌意;决斗时会不遗余力弃置杀;如果有机会对已经跳反的猪献殷勤,那么一定献;
好吧,其实都是挺有用的,即使已经玩过(甚至熟知)三国杀,也需要非常仔细的看懂,看清题目。
我还是一步一步来考虑的。
先看没有锦囊牌的情况。
在自己回合内,有武器当然是装上。闪一定是在非自己回合出的。桃的话血没有满就吃,杀的话能杀就杀,但是要找清目标(必须距离为1)。
所谓找清目标,就是——反贼找主公或表明身份的忠臣,忠臣找已经表明身份的反贼,主公找已经表明身份的反贼或类反贼。
这应该是最简单的部分了。但是我WA了一两次才拿到10分。
错因有:
时间复杂度
参考文献
题目描述 by pinkex 大神。
其余参考文献如下
如有对题意模糊之处,我已特意在题目描述上加粗
C++ 代码
由作者整理而成,每一处细节和实现都详细介绍,自然也借鉴过部分前人经验,所幸未踩坑过多
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
struct pig
{
int shenfen; // 1是主猪 2是忠猪 3是反猪
int life; // 剩余体力,初始为4
int first_kill; // 杀 首要攻击对象,0表示不杀或杀不到
int first_fight; // 决斗 首要攻击对象
int num_kill; // 杀 的数量
int num_shan; // 闪 的数量
int num_peach; // 桃 的数量
int num_cards; // 手牌数量
int num_wuxiekeji; // 无懈可击 数量
int pig_before; // 前一头活着的猪
int pig_next; // 下一头活着的猪
int situation; // 对于全局的状态 0未知 1是跳忠 2是跳反 3是类反
bool is_it_alive; // 死了吗
bool is_it_zhugeliannu; // 有没有猪哥连弩
char had_cards[2010]; // 手牌 从 1 开始
}pigs[11];
int tot_cards, tot_pigs, tot_fanpigs;
// 卡牌总数, 猪总数, 存活的反猪数量
int now_top, u = 1;
char card[2010];
int winner;
// 1是主猪胜利 2是反猪胜利
void faqi_kill(int, int); // 发起杀
char getcard(); // 从stdin读入一张牌
void first_in(int); // 处理初始状态
bool gameover(); // 游戏是否结束
void round(int); // rank 的回合
void faqi_kill(int, int); // 发起 杀
void die(int); // 有猪彻底死了
char pd_getcard(); // 牌堆拿牌
void printans(); // 输出答案
void faqi_fight(int, int); // 发起决斗
void faqi_nanzhuruqin(int); // 发起南猪入侵
void faqi_wanjianqifa(int); // 发起万箭齐发
bool ask_for_wuxiekeji(int, int);//等待响应无懈可击
void find_goal_to_fight(int); // 查找决斗对象(反猪不需要)
void find_goal_to_kill(int); // 查找杀对象
void near_die(int, int); // 濒死状态处理
void near_die(int killer, int sufferer)
{ // killer 杀手, sufferer 受伤者
bool have_peach = false; // 是否有 桃
if (pigs[sufferer].num_peach > 0) // 桃的数量大于 0
{
register int i;
have_peach = true; // 有桃子
for (i = 1; pigs[sufferer].had_cards[i] != 'P'; i ++ );
// 找到手牌 桃
for (i = i + 1; i <= pigs[sufferer].num_cards; i ++ )
{
pigs[sufferer].had_cards[i - 1] = pigs[sufferer].had_cards[i];
// 将 桃 用掉,通过后面的数覆盖前面的数实现
}
pigs[sufferer].num_peach --; // 桃子 的数量减一
pigs[sufferer].num_cards --; // 牌 的数量减一
pigs[sufferer].life ++; // 生命值 加一
}
if (have_peach == false) // 如果没有桃子
{
die(sufferer); // 死亡
if (killer == 1 && pigs[sufferer].shenfen == 2) // 忠猪死了
{
pigs[1].is_it_zhugeliannu = 0; // 将装备技能清空
pigs[1].num_wuxiekeji = pigs[1].num_shan = 0;
pigs[1].num_kill = pigs[1].num_peach = pigs[1].num_cards = 0;
// 技能清空
memset(pigs[1].had_cards, 0, sizeof pigs[1].had_cards);
// 手牌清空
}
if(pigs[sufferer].shenfen == 3) // 死的是反猪
{
for (register int j = 1; j <= 3; j ++ )
{
pigs[killer].had_cards[ ++ pigs[killer].num_cards] = pd_getcard();
// 再拿三张牌
if (pigs[killer].had_cards[pigs[killer].num_cards] == 'K')
pigs[killer].num_kill ++;
// 拿到的是 杀
else if (pigs[killer].had_cards[pigs[killer].num_cards] == 'D')
pigs[killer].num_shan ++;
// 拿到的是 闪
else if (pigs[killer].had_cards[pigs[killer].num_cards] == 'P')
pigs[killer].num_peach ++;
// 拿到的是 桃子
else if (pigs[killer].had_cards[pigs[killer].num_cards] == 'J')
pigs[killer].num_wuxiekeji ++;
// 拿到的是 无懈可击
}
}
}
}
void printans()
{
cout << (winner == 1?"MP":"FP") <<endl;
// 1是主猪胜利 2是反猪胜利
for (register int i = 1; i <= tot_pigs; i ++ ) // 找那些猪死了
{
if (pigs[i].is_it_alive == false) // 死了的猪
{
puts("DEAD");
continue;
}
else if (pigs[i].num_cards > 0) // 猪的手牌数大于 0
{
for (register int j = 1; j < pigs[i].num_cards; j ++ )
{
cout << pigs[i].had_cards[j] <<" "; // 输出猪的手牌
}
cout << pigs[i].had_cards[pigs[i].num_cards] <<endl;
// 输出最后一张手牌,回车
}
else puts(""); // 没有手牌
}
}
char pd_getcard() // 牌堆拿牌
{
char c = card[now_top]; // 当前拿到了第几张牌
if (now_top != 1) // 不是最后一张,拿完就少一张
{
now_top --;
}
// 最后一张牌是无限的,一直可以拿
return c;
}
char getcard() // 从stdin读入一张牌
{
char c = getchar(); // 初始的手牌
for (; c < 'A' || c > 'Z'; c = getchar());
return c;
}
// 处理初始状态
void first_in(int rank) // 当前是第 rank 头猪
{
char temp[2];
scanf("%s", temp); // 读入角色身份
if (temp[0] == 'M') // 角色为主猪
{
pigs[rank].shenfen = 1; // 1是主猪 2是忠猪 3是反猪
pigs[rank].situation = 1;
// 对于全局的已知身份 0未知 1是跳忠 2是跳反 3是类反
}
else if (temp[0] == 'Z') // 角色为忠猪
{
pigs[rank].shenfen = 2; // 身份为忠猪
// 不过其它猪不知道它的身份
}
else if (temp[0] == 'F') // 角色为反猪
{
pigs[rank].shenfen = 3;
pigs[rank].first_fight = 1;
// 决斗 首要攻击对象为主猪
tot_fanpigs ++;
// 存活的反猪数量加一
}
for (register int i = 1; i <= 4; i ++ )
{
pigs[rank].had_cards[i] = getcard();
// 初始手牌
if (pigs[rank].had_cards[i] == 'K')
{
pigs[rank].num_kill ++;
// 手牌为 杀
}
else if (pigs[rank].had_cards[i] == 'D')
{
pigs[rank].num_shan ++;
// 手牌为 闪
}
else if (pigs[rank].had_cards[i] == 'P')
{
pigs[rank].num_peach ++;
// 手牌为 桃
}
else if (pigs[rank].had_cards[i] == 'J')
{
pigs[rank].num_wuxiekeji ++;
// 手牌为 无懈可击
}
}
pigs[rank].num_cards = 4; // 共有四张手牌
pigs[rank].life = 4; // 共有四滴血
pigs[rank].pig_before = (rank == 1?tot_pigs:rank - 1); // 前一头活着的猪
pigs[rank].pig_next = (rank == tot_pigs?1:rank + 1); // 下一头活着的猪
// 因为初始攻击距离为一,只能攻击到前后两头猪
pigs[rank].is_it_alive = true; // 没有死
}
bool gameover() // 游戏是否结束
{
if (tot_fanpigs == 0) // 存活的反猪数量为 0,结束
{
winner = 1;
return true;
}
if (pigs[1].is_it_alive == false) // 或者主猪死了,结束
{
winner = 2;
return true;
}
return false; // 否则,没结束
}
void die(int rank) // 当前是第 rank 头猪
{
pigs[rank].is_it_alive = false; // 已死亡
if (rank == 1) // 主猪死了
{
winner = 2; // 胜者为反猪
printans(); // 输出答案
exit(0); // 结束
}
if (pigs[rank].shenfen == 3) // 反猪死了
{
if (-- tot_fanpigs == 0) // 如果当前反猪数量为 0
{
winner = 1; // 胜者为正方
printans(); // 输出答案
exit(0); // 结束
}
}
pigs[pigs[rank].pig_before].pig_next = pigs[rank].pig_next;
pigs[pigs[rank].pig_next].pig_before = pigs[rank].pig_before;
// 运用链表的技巧将 前面一头猪的下家更新成死猪之后的那头猪
// 运用链表的技巧将 后一头猪的上家更新成死猪之前的那头猪
}
void find_goal_to_kill(int rank) //理清打的目标
{
if (pigs[rank].shenfen == 2) // 身份为忠猪
{
pigs[rank].first_kill = ( pigs[pigs[rank].pig_next].situation == 2?pigs[rank].pig_next:0 );
// 攻打跳反的猪
}
else if (pigs[rank].shenfen == 1) // 身份为主猪
{
pigs[rank].first_kill = ( pigs[pigs[rank].pig_next].situation == 2 || pigs[pigs[rank].pig_next].situation == 3?pigs[rank].pig_next:0 );
// 除攻打跳反的猪之外
// 主猪还会攻打没有跳身份且用南猪入侵/万箭齐发对自己造成伤害的猪
// 即类反猪
}
else if (pigs[rank].shenfen == 3) // 身份为反猪
{
pigs[rank].first_kill = (pigs[pigs[rank].pig_next].situation == 1?pigs[rank].pig_next:0);
// 攻打跳忠的猪
}
}
void find_goal_to_fight(int rank) // 查找决斗对象(反猪不需要)
{
// 因为猪之间连成了一个环,所以可以通过查找当前的猪的下家是不是开始位置的猪来结束
for (register int i = pigs[rank].pig_next; i != rank; i = pigs[i].pig_next)
{
// 主猪会攻打 跳反 和 类反 的猪
if (rank == 1 && (pigs[i].situation == 2 || pigs[i].situation == 3))
{
pigs[rank].first_fight = i;
// 设为 首要决斗对象
return;
// 返回
}
// 忠猪只会攻打 反猪
if (pigs[i].situation == 2 && pigs[rank].shenfen == 2)
{
pigs[rank].first_fight = i;
// 设为 首要决斗对象
return;
// 返回
}
}
pigs[rank].first_fight = 0;
}
void round(int rank) // 当前猪的回合
{
for (register int i = 1; i <= 2; i ++ ) // 抽两张牌
{
pigs[rank].had_cards[ ++ (pigs[rank].num_cards)] = pd_getcard();
if (pigs[rank].had_cards[pigs[rank].num_cards] == 'K')
{
pigs[rank].num_kill ++; // 抽到了杀
}
else if (pigs[rank].had_cards[pigs[rank].num_cards] == 'D')
{
pigs[rank].num_shan ++; // 抽到了闪
}
else if (pigs[rank].had_cards[pigs[rank].num_cards] == 'P')
{
pigs[rank].num_peach ++; // 抽到了桃
}
else if (pigs[rank].had_cards[pigs[rank].num_cards] == 'J')
{
pigs[rank].num_wuxiekeji ++; // 抽到了无懈可击
}
}
int sum_kill = 0; // 使用的杀为 0
// 如果没有将手牌全部找完并且手牌数不为 0,就继续找
for (register int i = 1; i <= pigs[rank].num_cards && pigs[rank].num_cards > 0; i ++ )
{
if (pigs[rank].had_cards[i] == 'Z') // 如果有猪哥连弩
{
register int j;
// 装上猪哥连弩,将此手牌去掉
for (j = i + 1; j <= pigs[rank].num_cards; j ++ )
{
pigs[rank].had_cards[j - 1] = pigs[rank].had_cards[j];
}
pigs[rank].num_cards --; // 手牌数减一
pigs[rank].is_it_zhugeliannu = 1; // 已经装上了猪哥连弩
i = 0; // 重新开始找手牌,因为杀可以无限用
}
// 如果当前找到了一张决斗
if (pigs[rank].had_cards[i] == 'F')
{
if (pigs[rank].shenfen != 3) // 如果身份不为反猪
{
find_goal_to_fight(rank); // 查找决斗对象(反猪不需要)
}
if (pigs[rank].first_fight != 0) // 如果当前猪有决斗对象
{
register int j;
// 从自己的手牌中找牌
for (j = i + 1; j <= pigs[rank].num_cards; j ++ )
{
pigs[rank].had_cards[j - 1] = pigs[rank].had_cards[j];
}
pigs[rank].num_cards --;
// 手牌数减一
faqi_fight(rank, pigs[rank].first_fight);
// 发起决斗,对自己的决斗对象
i = 0;
if (pigs[rank].is_it_alive == false) return;
//重要,如果决斗死了就立即结束回合
}
}
// 如果找到了 杀
if (pigs[rank].had_cards[i] == 'K' && sum_kill == 0)
{
find_goal_to_kill(rank);
// 就查找自己的击杀目标
if (pigs[rank].first_kill != 0) // 有目标且可以杀的到
{
register int j;
// 使用这张牌
for (j = i + 1; j <= pigs[rank].num_cards; j ++ )
{
pigs[rank].had_cards[j - 1] = pigs[rank].had_cards[j];
}
pigs[rank].num_cards --; // 牌数减一
pigs[rank].num_kill --; // 杀减一
faqi_kill(rank, pigs[rank].first_kill);
// 攻击自己的击杀目标
sum_kill ++; // 使用一张杀
i = 0;
}
}
else if (pigs[rank].had_cards[i] == 'K' && pigs[rank].is_it_zhugeliannu == 1)
{ // 虽然朴素情况下只能用一张杀,但是有猪哥连弩就能无限用
find_goal_to_kill(rank);
// 查找自己的击杀目标
if (pigs[rank].first_kill != 0) // 有目标且可以杀的到
{
register int j;
// 从手牌中取出杀,用当前后面的牌覆盖前面的牌
for (j = i + 1; j <= pigs[rank].num_cards; j ++ )
{
pigs[rank].had_cards[j - 1] = pigs[rank].had_cards[j];
}
pigs[rank].num_cards --; // 牌数减一
pigs[rank].num_kill --; // 杀减一
faqi_kill(rank, pigs[rank].first_kill);
// 攻击自己的击杀目标
sum_kill ++;
i = 0;
}
}
// 如果找到了 桃 ,且当前生命值不满就使用,桃没有次数限制
if (pigs[rank].had_cards[i] == 'P' && pigs[rank].life < 4)
{
register int j;
// 从手牌中取出桃,用当前后面的牌覆盖前面的牌
for (j = i + 1; j <= pigs[rank].num_cards; j ++ )
{
pigs[rank].had_cards[j - 1] = pigs[rank].had_cards[j];
}
pigs[rank].num_cards --; // 牌数减一
pigs[rank].num_peach --; // 桃减一
pigs[rank].life ++; // 生命值加一
i --; // 因为牌数已经覆盖,所以要从这张牌开始找起
}
// 找到了一张南猪入侵
if (pigs[rank].had_cards[i] == 'N')
{
register int j;
// 从手牌中取出南猪入侵,用当前后面的牌覆盖前面的牌
for (j = i + 1; j <= pigs[rank].num_cards; j ++ )
{
pigs[rank].had_cards[j - 1] = pigs[rank].had_cards[j];
}
pigs[rank].num_cards --; // 牌数减一
faqi_nanzhuruqin(rank); // 使用南猪入侵
// 从头开始找牌,注释:本蒟蒻错过一次
i = 0;
}
// 找到了一张万箭齐发
if (pigs[rank].had_cards[i] == 'W')
{
register int j;
// 从手牌中取出万箭齐发,用当前后面的牌覆盖前面的牌
for (j = i + 1; j <= pigs[rank].num_cards; j ++ )
{
pigs[rank].had_cards[j - 1] = pigs[rank].had_cards[j];
}
pigs[rank].num_cards --; // 牌数减一
faqi_wanjianqifa(rank); // 使用万箭齐发
// 从头开始找牌,注释:本蒟蒻错过一次
i = 0;
}
}
}
void faqi_fight(int from, int goal)
{
register int i, fi, gi;
// 发起决斗
if (pigs[from].situation == 0 || pigs[from].situation == 3)
{
// 如果当前攻击者全局状态下认为是类反或未知
pigs[from].situation = (pigs[goal].situation == 1?2:1);
// 反猪就跳反, 忠猪就跳忠
}
bool can_miss = ask_for_wuxiekeji(from, pigs[goal].situation);
// 判断是否有无懈可击
if (can_miss == true) return; // 有,就使用躲过一劫
// 如果是主猪对忠猪决斗,为了保主猪,忠猪自愿掉一滴血
if (pigs[goal].shenfen == 2 && from == 1)
{
if ( -- pigs[goal].life <= 0) // 如果忠猪的血量小于等于 0
{
near_die(from, goal); // 忠猪进入濒死状态
}
return; // 返回
}
i = goal; // i 为 被决斗的猪
while (pigs[i].num_kill > 0) // 如果被决斗的猪所拥有的杀大于 0
{
for (fi = 1; fi <= pigs[i].num_cards && pigs[i].had_cards[fi] != 'K'; fi ++ );
// 找到并使用杀,使用后 用当前后面的牌覆盖前面的牌
for (register int k = fi + 1; k <= pigs[i].num_cards; k ++ )
{
pigs[i].had_cards[k - 1] = pigs[i].had_cards[k];
}
pigs[i].num_cards --; // 牌数减一
pigs[i].num_kill --; // 杀减一
// 小技巧,如果 此时反方出杀,那么下一轮正方出杀,依次轮着进行
i = (i == goal ? from : goal);
// 直到有一方没有杀
}
int failer = i; // failer 为决斗失败的一方
int winner_in = (from == failer?goal:from); // 找到胜者
// 小技巧,如果 此时反方失败,那么正方为胜者,反之亦然
if ( -- pigs[failer].life <= 0) // 如果此时败方血量小于等于 0
{
near_die(winner_in, failer); // 进入濒死状态
}
}
// 等待响应无懈可击
bool ask_for_wuxiekeji(int rank, int situation) // situation 是编号为 rank 的猪的全局身份
{
if(situation == 0 || situation == 3) return false;
// 未表明身份的猪与类反猪不会有猪响应无懈可击
for (register int i = rank; ; i = pigs[i].pig_next) // 依次查找每个猪
{
if (pigs[i].num_wuxiekeji > 0) // 如果当前猪无懈可击的数量大于 0
{
// 如果第 rank 号猪跳忠,且当前轮到的猪的身份为主猪或忠猪
if (situation == 1 && (pigs[i].shenfen == 1 || pigs[i].shenfen == 2))
{
register int j;
for (j = 1; j <= pigs[i].num_cards && pigs[i].had_cards[j] != 'J'; j ++ );
// 找到无懈可击并使用,使用后 用当前后面的牌覆盖前面的牌
for (j = j + 1; j <= pigs[i].num_cards; j ++ )
{
pigs[i].had_cards[j - 1] = pigs[i].had_cards[j];
}
pigs[i].num_cards --; // 牌数减一
pigs[i].num_wuxiekeji--; // 无懈可击的数量减一
// 如果 使用无懈可击救助已跳忠的猪,它的全局身份为未知或类反猪
if (pigs[i].situation == 0 || pigs[i].situation == 3)
{
pigs[i].situation = 1; // 一律改为跳忠
}
// 返回
return !ask_for_wuxiekeji(i, 2);
}
// 如果第 rank 号猪跳反,且当前轮到的猪的身份为反猪
else if (situation == 2 && pigs[i].shenfen == 3)
{
register int j;
for (j = 1; j <= pigs[i].num_cards && pigs[i].had_cards[j] != 'J'; j ++ );
// 找到无懈可击并使用,使用后 用当前后面的牌覆盖前面的牌
for (j = j + 1; j <= pigs[i].num_cards; j ++ )
{
pigs[i].had_cards[j - 1] = pigs[i].had_cards[j];
}
pigs[i].num_cards --; // 牌数减一
pigs[i].num_wuxiekeji --; // 无懈可击的数量减一
// 如果 使用无懈可击救助已跳忠的猪,它的全局身份为未知或类反猪
if (pigs[i].situation == 0 || pigs[i].situation == 3)
{
pigs[i].situation = 2; // 一律改为跳反
}
// 总结一下,向跳忠或跳反的猪献殷勤会被认为是跳忠或跳反
return !ask_for_wuxiekeji(i, 1); // 有人使用无懈可击
}
}
if (i == pigs[rank].pig_before) //递归边界
return false; // 如果已经找到了这头猪前面的猪,递归结束
}
}
// 使用南猪入侵
void faqi_nanzhuruqin(int from)
{
// 按顺序从使用者下家开始依次结算
for (register int i = pigs[from].pig_next; i != from; i = pigs[i].pig_next)
{
bool can_miss = ask_for_wuxiekeji(from, pigs[i].situation);
// 求无懈可击
if (can_miss == true) continue;
// 有则免除
if (pigs[i].num_kill > 0) // 如果当前角色的 杀 大于 0
{
register int j;
for (j = 1; pigs[i].had_cards[j] != 'K'; j ++ );
// 找到杀并使用,使用后 用当前后面的牌覆盖前面的牌
for (j = j + 1; j <= pigs[i].num_cards; j ++ )
{
pigs[i].had_cards[j - 1] = pigs[i].had_cards[j];
}
pigs[i].num_cards --; // 牌数减一
pigs[i].num_kill --; // 杀 减一
can_miss = true; // 躲避
}
if (can_miss == true) continue; // 免除
if (i == 1 && pigs[from].situation == 0)
{
pigs[from].situation = 3;
// 如果受伤的猪为主猪且使用者的全局身份未知,使用者的全局身份会变成类反猪
}
if ( -- pigs[i].life <= 0) // 如果受伤的猪血量小于等于 0
{
near_die(from, i); // 进入濒死状态
}
}
}
// 使用万箭齐发
void faqi_wanjianqifa(int from)
{
// 按顺序从使用者下家开始依次结算
for (register int i = pigs[from].pig_next; i != from; i = pigs[i].pig_next)
{
bool can_miss = ask_for_wuxiekeji(from, pigs[i].situation);
// 求无懈可击
if (can_miss == true) continue;
// 有则免除
if (pigs[i].num_shan > 0) // 如果当前角色的 闪 大于 0
{
register int j;
for (j = 1; pigs[i].had_cards[j] != 'D'; j ++ );
// 找到闪并使用,使用后 用当前后面的牌覆盖前面的牌
for (j = j + 1; j <= pigs[i].num_cards; j ++ )
{
pigs[i].had_cards[j - 1] = pigs[i].had_cards[j];
}
pigs[i].num_cards --; // 牌数减一
pigs[i].num_shan --; // 闪 减一
can_miss = true; // 躲避
}
if(can_miss == true) continue; // 免除
if(i == 1 && pigs[from].situation == 0)
{
pigs[from].situation = 3;
// 如果受伤的猪为主猪且使用者的全局身份未知,使用者的全局身份会变成类反猪
}
if( -- pigs[i].life <= 0) // 如果受伤的猪血量小于等于 0
{
near_die(from,i); // 进入濒死状态
}
}
}
void faqi_kill(int from, int goal)//发起 杀
{
register int i;
if (pigs[from].situation == 0) // 如果使用者的全局身份未知
{
pigs[from].situation = (pigs[goal].situation == 1 ?2 :1);
// 猪的身份会根据它进攻的对象变成攻击对象的敌对势力
}
if (pigs[from].situation == 3) // 如果使用者的全局身份为类反猪
{
pigs[from].situation = (pigs[goal].situation == 1 ?2 :1);
// 猪的身份会根据它进攻的对象变成攻击对象的敌对势力
}
if(pigs[goal].num_shan > 0) // 如果被进攻对象的闪大于 0
{
for (i = 1; pigs[goal].had_cards[i] != 'D'; i ++ );
// 找到闪并使用,使用后 用当前后面的牌覆盖前面的牌
for (i = i + 1; i <= pigs[goal].num_cards; i ++ )
{
pigs[goal].had_cards[i - 1] = pigs[goal].had_cards[i];
}
pigs[goal].num_shan --; // 闪 减一
pigs[goal].num_cards --; // 牌数减一
}
else if( -- pigs[goal].life <= 0) // 否则,如果受伤后血量小于等于 0
{
near_die(from,goal); // 进入濒死状态
}
}
int main()
{
scanf("%d %d", &tot_pigs, &tot_cards);
now_top = tot_cards; // 当前可以摸最顶部的牌
for (register int i = 1; i <= tot_pigs; i ++ ) // 将猪的所有物品清零
{
pigs[i].num_kill = pigs[i].num_peach = 0;
pigs[i].num_shan = pigs[i].num_wuxiekeji = 0;
}
for (register int i = 1; i <= tot_pigs; i ++ )
{
first_in(i); //初始化每头猪
}
for (register int i = tot_cards; i >= 1; i -- )
{
card[i] = getcard(); //初始化牌堆
}
while (gameover() == false) // 如果游戏没有结束
{
round(u); // 依次进行一轮游戏
u = pigs[u].pig_next; // 然后轮到它的下家进行
}
printans(); // 最后输出答案
return 0;
}
C语言大作业级别的题目
nb
%%%