1use std::collections::HashMap;
2
3use super::attack_requirement::{self, AttackRequirement};
4use super::attack_restriction::{self, AttackRestrictionType};
5use super::global_attack_restrictions::GlobalAttackRestrictions;
6use super::DefenderId;
7use crate::card::Card;
8use crate::ids::{CardId, PlayerId};
9
10#[derive(Debug, Clone)]
16pub struct AttackConstraints {
17 pub possible_attackers: Vec<CardId>,
18 pub possible_defenders: Vec<DefenderId>,
19 pub global_restrictions: GlobalAttackRestrictions,
20 pub restrictions: HashMap<CardId, AttackRestriction>,
21 pub requirements: HashMap<CardId, AttackRequirement>,
22}
23
24#[derive(Debug, Clone)]
27pub struct AttackRestriction {
28 pub attacker: CardId,
29 pub types: std::collections::HashSet<AttackRestrictionType>,
30 pub cant_attack: bool,
31 pub cant_attack_defenders: Vec<DefenderId>,
32}
33
34impl AttackRestriction {
35 pub fn new(
36 attacker: CardId,
37 cards: &[Card],
38 possible_defenders: &[DefenderId],
39 game: &crate::game::GameState,
40 ) -> Self {
41 let card = &cards[attacker.index()];
42 let types = attack_restriction::get_restrictions(card);
43
44 let mut cant_attack_defenders = Vec::new();
46 for &defender in possible_defenders {
47 if !super::combat_util::can_attack_defender(game, attacker, defender) {
48 cant_attack_defenders.push(defender);
49 }
50 }
51
52 let cant_attack = types.contains(&AttackRestrictionType::Never)
54 || (types.contains(&AttackRestrictionType::OnlyAlone)
55 && (types.contains(&AttackRestrictionType::NeedGreaterPower)
56 || types.contains(&AttackRestrictionType::NeedBlackOrGreen)
57 || types.contains(&AttackRestrictionType::NotAlone)
58 || types.contains(&AttackRestrictionType::NeedTwoOthers)))
59 || cant_attack_defenders.len() == possible_defenders.len();
60
61 AttackRestriction {
62 attacker,
63 types,
64 cant_attack,
65 cant_attack_defenders,
66 }
67 }
68
69 pub fn can_attack(&self, defender: DefenderId) -> bool {
71 !self.cant_attack && !self.cant_attack_defenders.contains(&defender)
72 }
73
74 pub fn can_attack_with(
77 &self,
78 defender: DefenderId,
79 attackers: &[(CardId, DefenderId)],
80 cards: &[Card],
81 ) -> bool {
82 if !self.can_attack(defender) {
83 return false;
84 }
85 self.get_violations(attackers, cards).is_empty()
86 }
87
88 pub fn get_violations(
90 &self,
91 attackers: &[(CardId, DefenderId)],
92 cards: &[Card],
93 ) -> std::collections::HashSet<AttackRestrictionType> {
94 let mut violations = std::collections::HashSet::new();
95 let n = attackers.len();
96
97 if self.types.contains(&AttackRestrictionType::OnlyAlone) && n > 1 {
98 violations.insert(AttackRestrictionType::OnlyAlone);
99 }
100 if self.types.contains(&AttackRestrictionType::NotAlone) && n <= 1 {
101 violations.insert(AttackRestrictionType::NotAlone);
102 }
103 if self.types.contains(&AttackRestrictionType::NeedTwoOthers) && n <= 2 {
104 violations.insert(AttackRestrictionType::NeedTwoOthers);
105 }
106 if self
107 .types
108 .contains(&AttackRestrictionType::NeedGreaterPower)
109 {
110 let my_power = cards[self.attacker.index()].power();
111 let has_greater = attackers
112 .iter()
113 .any(|&(cid, _)| cid != self.attacker && cards[cid.index()].power() > my_power);
114 if !has_greater {
115 violations.insert(AttackRestrictionType::NeedGreaterPower);
116 }
117 }
118 if self
119 .types
120 .contains(&AttackRestrictionType::NeedBlackOrGreen)
121 {
122 let has_bg = attackers.iter().any(|&(cid, _)| {
123 cid != self.attacker && {
124 let c = &cards[cid.index()];
125 c.color.has_black() || c.color.has_green()
126 }
127 });
128 if !has_bg {
129 violations.insert(AttackRestrictionType::NeedBlackOrGreen);
130 }
131 }
132 violations
133 }
134
135 pub fn get_types(&self) -> &std::collections::HashSet<AttackRestrictionType> {
136 &self.types
137 }
138}
139
140#[derive(Debug, Clone)]
142struct Attack {
143 attacker: CardId,
144 defender: DefenderId,
145 requirements: i32,
146}
147
148impl AttackConstraints {
149 pub fn new(
152 game: &crate::game::GameState,
153 attacking_player: PlayerId,
154 possible_defenders: &[DefenderId],
155 ) -> Self {
156 let possible_attackers: Vec<CardId> = game
157 .creatures_on_battlefield(attacking_player)
158 .into_iter()
159 .collect();
160
161 let global_restrictions = GlobalAttackRestrictions::get_global_restrictions(
162 &game.cards,
163 attacking_player,
164 possible_defenders,
165 );
166
167 let mut restrictions = HashMap::new();
168 let mut requirements = HashMap::new();
169
170 for &attacker in &possible_attackers {
171 restrictions.insert(
172 attacker,
173 AttackRestriction::new(attacker, &game.cards, possible_defenders, game),
174 );
175 }
176
177 let reqs = attack_requirement::compute_attack_requirements_with_defenders(
178 &game.cards,
179 &possible_attackers,
180 possible_defenders,
181 );
182 for req in reqs {
183 requirements.insert(req.attacker, req);
184 }
185
186 AttackConstraints {
187 possible_attackers,
188 possible_defenders: possible_defenders.to_vec(),
189 global_restrictions,
190 restrictions,
191 requirements,
192 }
193 }
194
195 pub fn get_restrictions(&self) -> &HashMap<CardId, AttackRestriction> {
196 &self.restrictions
197 }
198
199 pub fn get_global_restrictions(&self) -> &GlobalAttackRestrictions {
200 &self.global_restrictions
201 }
202
203 pub fn get_requirements(&self) -> &HashMap<CardId, AttackRequirement> {
204 &self.requirements
205 }
206
207 pub fn get_legal_attackers(&self, cards: &[Card]) -> (Vec<(CardId, DefenderId)>, i32) {
215 let max = self
216 .global_restrictions
217 .get_max()
218 .unwrap_or(i32::MAX)
219 .min(self.possible_attackers.len() as i32);
220
221 if max == 0 {
222 return (Vec::new(), 0);
223 }
224
225 let mut reqs = self.get_sorted_filtered_requirements(cards);
227
228 reqs.retain(|a| {
230 let restriction = self.restrictions.get(&a.attacker);
231 if let Some(r) = restriction {
232 if r.cant_attack {
233 return false;
234 }
235 let types = &r.types;
236 if (types.contains(&AttackRestrictionType::NeedTwoOthers) && max <= 2)
238 || (types.contains(&AttackRestrictionType::NotAlone) && max <= 1)
239 || (types.contains(&AttackRestrictionType::NeedBlackOrGreen) && max <= 1)
240 || (types.contains(&AttackRestrictionType::NeedGreaterPower) && max <= 1)
241 {
242 return false;
243 }
244 }
245 true
246 });
247
248 let mut best_result: Option<(Vec<(CardId, DefenderId)>, i32)> = None;
250
251 for req in &reqs {
252 if let Some(r) = self.restrictions.get(&req.attacker) {
253 if r.types.contains(&AttackRestrictionType::OnlyAlone) && req.requirements > 0 {
254 let attack_map = vec![(req.attacker, req.defender)];
255 let violations = self.count_violations(&attack_map, cards);
256 if violations != -1 {
257 match &best_result {
258 None => best_result = Some((attack_map, violations)),
259 Some((_, best_v)) if violations < *best_v => {
260 best_result = Some((attack_map, violations));
261 }
262 _ => {}
263 }
264 }
265 }
266 }
267 }
268
269 reqs.retain(|a| {
271 self.restrictions
272 .get(&a.attacker)
273 .is_none_or(|r| !r.types.contains(&AttackRestrictionType::OnlyAlone))
274 });
275
276 let mut attack_map: Vec<(CardId, DefenderId)> = Vec::new();
278 let mut used: std::collections::HashSet<CardId> = std::collections::HashSet::new();
279 let mut remaining_max = max;
280
281 for req in &reqs {
282 if remaining_max <= 0 {
283 break;
284 }
285 if used.contains(&req.attacker) {
286 continue;
287 }
288 if req.requirements == 0 {
289 continue;
290 }
291
292 if let Some(&def_max) = self
294 .global_restrictions
295 .get_defender_max()
296 .get(&req.defender)
297 {
298 let count = attack_map
299 .iter()
300 .filter(|(_, d)| *d == req.defender)
301 .count() as i32;
302 if count >= def_max {
303 continue;
304 }
305 }
306
307 attack_map.push((req.attacker, req.defender));
308 used.insert(req.attacker);
309 remaining_max -= 1;
310 }
311
312 let greedy_violations = self.count_violations(&attack_map, cards);
313 if greedy_violations != -1 {
314 match &best_result {
315 None => best_result = Some((attack_map.clone(), greedy_violations)),
316 Some((_, best_v)) if greedy_violations < *best_v => {
317 best_result = Some((attack_map.clone(), greedy_violations));
318 }
319 _ => {}
320 }
321 }
322
323 let empty_violations = self.count_violations(&[], cards);
325 if empty_violations != -1 {
326 match &best_result {
327 None => best_result = Some((Vec::new(), empty_violations)),
328 Some((_, best_v)) if empty_violations < *best_v => {
329 best_result = Some((Vec::new(), empty_violations));
330 }
331 _ => {}
332 }
333 }
334
335 best_result.unwrap_or((Vec::new(), 0))
336 }
337
338 pub fn count_violations(&self, attackers: &[(CardId, DefenderId)], cards: &[Card]) -> i32 {
343 if !self.global_restrictions.is_legal(attackers) {
344 return -1;
345 }
346
347 for &(attacker_id, defender) in attackers {
349 if let Some(restriction) = self.restrictions.get(&attacker_id) {
350 if !restriction.can_attack_with(defender, attackers, cards) {
351 return -1;
352 }
353 }
354 }
355
356 let mut violations = 0;
358 for &possible_attacker in &self.possible_attackers {
359 if let Some(requirement) = self.requirements.get(&possible_attacker) {
360 let defender = attackers
361 .iter()
362 .find(|(a, _)| *a == possible_attacker)
363 .map(|(_, d)| *d);
364 violations += requirement.count_violations(defender);
365 }
366 }
367
368 violations
369 }
370
371 fn get_sorted_filtered_requirements(&self, _cards: &[Card]) -> Vec<Attack> {
374 let mut result = Vec::new();
375
376 for (&attacker_id, req) in &self.requirements {
377 let restriction = self.restrictions.get(&attacker_id);
378 let sorted_reqs = req.get_sorted_requirements();
379
380 for (defender, count) in sorted_reqs {
381 let can_attack = restriction.is_none_or(|r| r.can_attack(defender));
382 if can_attack {
383 result.push(Attack {
384 attacker: attacker_id,
385 defender,
386 requirements: count,
387 });
388 }
389 }
390 }
391
392 result.sort_by(|a, b| b.requirements.cmp(&a.requirements));
394 result
395 }
396}