use std::collections::HashMap;
use super::DefenderId;
use crate::card::Card;
use crate::ids::{CardId, PlayerId};
use crate::staticability::static_ability_must_attack;
#[derive(Debug, Clone)]
pub struct AttackRequirement {
pub attacker: CardId,
pub must_attack_any: bool,
pub must_attack_defender: Option<PlayerId>,
pub goaded_by: Option<PlayerId>,
pub defender_specific: HashMap<DefenderId, i32>,
}
impl AttackRequirement {
pub fn has_requirement(&self) -> bool {
self.defender_specific.values().any(|&v| v > 0) || self.must_attack_any
}
pub fn count_violations(&self, defender: Option<DefenderId>) -> i32 {
if !self.has_requirement() {
return 0;
}
let total: i32 = self.defender_specific.values().sum();
let is_attacking = defender.is_some();
let credit = if is_attacking {
defender
.and_then(|d| self.defender_specific.get(&d).copied())
.unwrap_or(0)
} else {
0
};
total - credit
}
pub fn get_sorted_requirements(&self) -> Vec<(DefenderId, i32)> {
let mut entries: Vec<(DefenderId, i32)> = self
.defender_specific
.iter()
.map(|(&d, &c)| (d, c))
.collect();
entries.sort_by_key(|&(_, c)| c);
entries
}
}
pub fn compute_attack_requirements(
cards: &[Card],
available: &[CardId],
defending: PlayerId,
) -> Vec<AttackRequirement> {
compute_attack_requirements_with_defenders(cards, available, &[DefenderId::Player(defending)])
}
pub fn compute_attack_requirements_with_defenders(
cards: &[Card],
available: &[CardId],
possible_defenders: &[DefenderId],
) -> Vec<AttackRequirement> {
let mut requirements = Vec::new();
for &attacker_id in available {
let card = &cards[attacker_id.index()];
let must_from_static = static_ability_must_attack::must_attack(cards, card);
let goaded = card.goaded_by;
let must_attack_any = must_from_static || goaded.is_some();
let mut n_attack_anything: i32 = 0;
if goaded.is_some() {
n_attack_anything += 1;
}
if must_from_static {
n_attack_anything += 1;
}
let mut defender_specific = HashMap::new();
for &defender in possible_defenders {
defender_specific.insert(defender, n_attack_anything);
}
let goaded_by_player = goaded;
let defending = possible_defenders
.iter()
.find_map(|d| d.as_player())
.unwrap_or(PlayerId(0));
let must_attack_defender = if goaded.is_some() && goaded != Some(defending) {
Some(defending)
} else {
None
};
if must_attack_any || !defender_specific.is_empty() {
requirements.push(AttackRequirement {
attacker: attacker_id,
must_attack_any,
must_attack_defender,
goaded_by: goaded_by_player,
defender_specific,
});
}
}
requirements
}
pub fn must_attack_ids(requirements: &[AttackRequirement]) -> Vec<CardId> {
requirements
.iter()
.filter(|r| r.must_attack_any)
.map(|r| r.attacker)
.collect()
}