Game Overview
This problem involves a complex simulation of a card game variant. The objective is to determine the final outcome given initial roles, hands, and a deck of cards, assuming all players follow strict behavioral rules.
Victory Conditions
- Master Pig (MP): Survives while eliminating all Rebel Pigs.
- Loyal Pig (ZP): Protects the Master Pig at all costs; shares the Master's victory condition.
- Rebel Pig (FP): Eliminates the Master Pig.
Game Flow
At the start, every player holds 4 cards, with max HP and current HP set to 4. The game proceeds counter-clockwise (index 1, 2, ..., n, then back to 1). Each turn consists of:
- Draw Phase: Draw 2 cards from the deck to the rightmost side of the hand.
- Play Phase: Use cards from left to right.
- Without the "Pig Crossbow" weapon, only one "Slash" can be used per turn.
- Used cards (except equipped weapons) are discarded permanently.
Card Types
Basic Cards
- Peach (P): Restore 1 HP during your turn if HP is below max. Can be used when HP drops to 0 outside your turn.
- Slash (K): Attack a target within range (default range is 1). If not dodged by a "Dodge", deals 1 damage.
- Dodge (D): Discard to negate a "Slash" attack.
Trick Cards
- Duel (F): Target vs. User alternate discarding "Slash". The first unable to discard takes 1 damage.
- Southern Invasion (N): All others must discard a "Slash" or take 1 damage.
- Volley (W): Similar to Invasion, but requires discarding a "Dodge".
- Nullify (J): Counter a trick card.
- Against Duel: Cancels the duel.
- Against Invasion/Volley: Protects the current target being processed.
- Against Nullify: Cancels the counter-attempt.
Equipment
- Pig Crossbow (Z): Allows unlimited "Slash" uses per turn. Equipping a new weapon discards the old one.
Key Mechanics
- Damage Source: The user of Slash, Invasion, or Volley. In a Duel, the source is the player who forced the opponent to lack a Slash.
- Distance: Calculated as the number of players in the counter-clocwkise path + 1. Deaths alter distances.
- Death: If HP ≤ 0 and insufficient Peaches are held to revive to 1 HP, the player dies. All cards are discarded.
- Rewards: Killing a Rebel grants the killer 3 cards. If Master kills a Loyal, Master discards all cards.
Behavioral Logic
Identity Declaration
- Loyal: Shows loyalty by helping Master/Loyalists and attacking Rebels.
- Rebel: Shows rebellion by attacking Master/Loyalists and helping Rebels.
- Neutral: Unidentified players are not assisted by the Master until they cause damage via AoE.
General Rules (All Roles)
- Use Peach immediately if HP < 4.
- Equip weapons and use AoE cards (Invasion/Volley) immediately.
- Dodge Slashes and respond to AoE if possible.
- Do not assist unidentified players.
Role-Specific Rules
- Master:
- Labels players as "Suspicious Rebels" if they damage him via AoE.
- Attacks the first Suspicious or Declared Rebel counter-clockwise.
- Always fights aggressively in Duels unless the opponent is a Loyalist he falsely suspects.
- Loyalist:
- Attacks the first Declared Rebel counter-clockwise.
- Refuses to discard Slashes in a Duel against the Master.
- Rebel:
- Prioritizes attacking the Master. If not possible, attacks the first Declared Loyalist.
- Fights aggressively in Duels.
- Assists fellow Rebels.
Impleemntation Strategy
The simulation is broken down by features. We track player states using arrays for HP, hand cards, identity status, and equipment.
Data Structures
int health[15]; // Current HP
int cardCount[15]; // Total cards owned (hand + discarded)
char hand[15][1010]; // Card storage
bool isAlive[15]; // Death status
bool revealed[15]; // Has the player declared identity?
bool suspicious[15]; // Master's suspicion list
bool hasCrossbow[15]; // Weapon flag
int nextPlayer[15]; // Circular linked list (counter-clockwise)
int prevPlayer[15];
string role[15]; // "MP", "ZP", "FP"
char deck[1010]; // Draw pile
int drawPos; // Current position in deck
int rebelCount; // Live rebel count
bool used[15][1010]; // Track discarded cards in hand
Core Helper Functions
These functions handle card management and basic checks.
// Count specific card type in hand
int countCard(int player, char type) {
int res = 0;
for (int i = 1; i <= cardCount[player]; i++)
if (!used[player][i] && hand[player][i] == type) res++;
return res;
}
// Remove K cards of a specific type from hand
void removeCard(int player, int k, char type) {
if (k <= 0) return;
int removed = 0;
for (int i = 1; i <= cardCount[player] && removed < k; i++)
if (!used[player][i] && hand[player][i] == type) {
used[player][i] = true;
removed++;
}
}
// Restore health during own turn
void usePeach(int player) {
if (health[player] < 4) {
health[player]++;
removeCard(player, 1, 'P');
}
}
// Attempt revival when HP <= 0 (outside own turn)
bool tryRevive(int player) {
if (health[player] > 0) return false;
int peachNum = countCard(player, 'P');
if (peachNum >= (abs(health[player]) + 1)) {
removeCard(player, abs(health[player]) + 1, 'P');
health[player] = 1;
return false; // Survived
} else {
// Die
for (int i = 1; i <= cardCount[player]; i++) used[player][i] = true;
hasCrossbow[player] = false;
isAlive[player] = false;
return true; // Dead
}
}
// Discard all cards of a player (Master kills Loyalist)
void discardAll(int player) {
for (int i = 1; i <= cardCount[player]; i++) used[player][i] = true;
hasCrossbow[player] = false;
}
Death Handling
When a player dies, we update the linked list and check victory conditions.
void handleDeath(int killer, int victim) {
isAlive[victim] = false;
// Update circular list
nextPlayer[prevPlayer[victim]] = nextPlayer[victim];
prevPlayer[nextPlayer[victim]] = prevPlayer[victim];
if (role[victim] == "MP") { outputResult(); exit(0); }
if (role[victim] == "FP") {
rebelCount--;
if (rebelCount == 0) { outputResult(); exit(0); }
// Killer draws 3 cards
for (int i = 0; i < 3; i++) {
if (drawPos <= m) hand[killer][++cardCount[killer]] = deck[drawPos++];
}
} else if (role[killer] == "MP") {
discardAll(killer);
}
}
Action Simulation (Slash, Duel, AoE)
We implement the logic for specific card plays based on the player's role and targets.
// Slash logic
void performSlash(int user, int target) {
revealed[user] = true;
bool hit = true;
if (countCard(target, 'D') > 0) {
removeCard(target, 1, 'D');
hit = false;
}
removeCard(user, 1, 'K');
if (hit) {
health[target]--;
if (health[target] <= 0) {
if (tryRevive(target)) handleDeath(user, target);
}
}
}
// Duel logic
void performDuel(int user, int target) {
revealed[user] = true;
removeCard(user, 1, 'F');
// Special Master logic regarding suspicious loyalists
if (role[user] == "MP" && role[target] == "ZP" && suspicious[target] && !revealed[target]) {
health[target]--;
if (health[target] <= 0 && tryRevive(target)) handleDeath(user, target);
return;
}
int userSlash = countCard(user, 'K');
int tarSlash = countCard(target, 'K');
// Loyalist refuses to fight Master
if (role[target] == "MP" && role[user] == "ZP") tarSlash = 0;
if (tarSlash <= userSlash) {
// Target loses
removeCard(target, tarSlash, 'K');
removeCard(user, tarSlash, 'K');
health[target]--;
if (health[target] <= 0 && tryRevive(target)) handleDeath(user, target);
} else {
// User loses
removeCard(user, userSlash, 'K');
removeCard(target, userSlash + 1, 'K');
health[user]--;
if (health[user] <= 0 && tryRevive(user)) handleDeath(target, user);
}
}
Target Selection
Finding the appropriate target based on role and identity status.
int findTarget(int player, string searcherRole) {
int start = player;
// Search counter-clockwise
for (int offset = 1; offset <= n; offset++) {
int curr = (start + offset - 1) % n + 1;
if (!isAlive[curr]) continue;
if (searcherRole == "MP") {
if ((revealed[curr] && role[curr] == "FP") || suspicious[curr]) return curr;
} else if (searcherRole == "ZP") {
if (revealed[curr] && role[curr] == "FP") return curr;
} else if (searcherRole == "FP") {
if (curr == 1) return curr; // Master is priority
if (revealed[curr] && role[curr] == "ZP") return curr;
}
}
return -1; // No valid target
}
Main Turn Loop
The play phase iterates through the hand from left to right.
void executeTurn(int player) {
// 1. Draw Phase
for (int i = 0; i < 2; i++) {
if (drawPos <= m) hand[player][++cardCount[player]] = deck[drawPos++];
}
bool slashUsed = false;
// 2. Play Phase
for (int i = 1; i <= cardCount[player]; i++) {
if (used[player][i]) continue;
char card = hand[player][i];
switch (card) {
case 'P': usePeach(player); break;
case 'Z': hasCrossbow[player] = true; removeCard(player, 1, 'Z'); break;
case 'K':
if (!isAlive[player]) break;
// Check if we can/should use Slash
bool canSlash = (hasCrossbow[player] || !slashUsed);
if (canSlash) {
int target = findTarget(player, role[player]);
// Basic logic: attack next player if no specific target found (simplified for snippet)
if (target == -1) target = nextPlayer[player];
if (target != -1 && isAlive[target]) {
if (role[player] == "FP" && target != 1 && !revealed[target]) break; // Rebel waits for Master
performSlash(player, target);
slashUsed = true;
}
}
break;
case 'F':
int duelTarget = findTarget(player, role[player]);
if (duelTarget != -1 && isAlive[duelTarget]) performDuel(player, duelTarget);
break;
case 'N': // Southern Invasion
case 'W': // Volley
// Implementation involves iterating through all other players
// and checking for Nullify (J) cards in a chain reaction
break;
}
if (!isAlive[player]) break;
}
}
Full Simulation Loop
void simulateGame() {
int current = 1;
while (rebelCount > 0 && isAlive[1]) {
if (isAlive[current]) {
executeTurn(current);
}
current = nextPlayer[current];
}
outputResult();
}
Handling "Nullify" (J) and AoE
The 100-point solution requires implementing the "Nullify" chain. When a Trick card (F, N, W) is played, a sub-routine checks players in counter-clockwise order starting from the user. Each player decides to use "J" based on whether they want the Trick to succeed or fail relative to the current target.