1use std::collections::BTreeSet;
2
3use crate::agent::game_log::GameLogEvent;
4use crate::agent::types::{
5 BinaryChoiceKind, CombatCostAction, GameEntity, ManaAbilityOption, ManaCostAction, PlayOption,
6 RollSwapChoice, TargetChoice,
7};
8use crate::agent::PlayerAgent;
9use crate::combat::DefenderId;
10use crate::cost::{payment_decision::PaymentDecision, CostPart};
11use crate::game::GameState;
12use crate::ids::{CardId, PlayerId};
13use crate::mana::ManaPool;
14use crate::player::actions::PlayerAction;
15use crate::player::player_factory_util::build_priority_actions;
16use crate::player::DelayedReveal;
17use crate::spellability::SpellAbility;
18use forge_foundation::{ManaCost, ZoneType};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
21pub enum FullControlFlag {
22 ChooseCostOrder,
23 ChooseCostReductionOrderAndVariableAmount,
24 NoPaymentFromManaAbility,
25 NoFreeCombatCostHandling,
26 AllowPaymentStartWithMissingResources,
27 LayerTimestampOrder,
28}
29
30pub struct PlayerController<'a, A: PlayerAgent + ?Sized> {
31 pub game: &'a GameState,
32 pub player: PlayerId,
33 pub agent: &'a mut A,
34 pub full_controls: BTreeSet<FullControlFlag>,
35}
36
37impl<'a, A: PlayerAgent + ?Sized> PlayerController<'a, A> {
38 pub fn new(game: &'a GameState, player: PlayerId, agent: &'a mut A) -> Self {
39 Self {
40 game,
41 player,
42 agent,
43 full_controls: BTreeSet::new(),
44 }
45 }
46
47 pub fn snapshot_state(&mut self, mana_pools: &[ManaPool]) {
48 self.agent.snapshot_state(self.game, mana_pools);
49 }
50
51 pub fn available_priority_actions(
52 &self,
53 playable: &[PlayOption],
54 tappable_lands: &[CardId],
55 untappable_lands: &[CardId],
56 activatable: &[(CardId, usize)],
57 ) -> Vec<PlayerAction> {
58 build_priority_actions(playable, tappable_lands, untappable_lands, activatable)
59 }
60
61 pub fn add_full_control(&mut self, flag: FullControlFlag) {
62 self.full_controls.insert(flag);
63 }
64
65 pub fn remove_full_control(&mut self, flag: FullControlFlag) {
66 self.full_controls.remove(&flag);
67 }
68
69 pub fn has_full_control(&self, flag: FullControlFlag) -> bool {
70 self.full_controls.contains(&flag)
71 }
72
73 pub fn notify(&mut self, event: crate::agent::notification::GameNotification) {
74 self.agent.notify(event);
75 }
76
77 pub fn reveal_cards(
78 &mut self,
79 cards: &[CardId],
80 zone: ZoneType,
81 owner: PlayerId,
82 message_prefix: Option<&str>,
83 ) {
84 self.agent
85 .reveal_cards(self.game, self.player, cards, zone, owner, message_prefix);
86 let mut message = String::new();
87 if let Some(prefix) = message_prefix {
88 message.push_str(prefix);
89 message.push(' ');
90 }
91 message.push_str("Reveal ");
92 message.push_str(&format!("{zone:?} cards"));
93 self.agent
94 .notify(crate::agent::notification::GameNotification::Event(
95 GameLogEvent::rule(message)
96 .with_player(owner)
97 .with_card(*cards.first().unwrap_or(&CardId(0))),
98 ));
99 }
100
101 pub fn temp_show_cards(&mut self, cards: &[CardId]) {
102 self.reveal_cards(cards, ZoneType::Hand, self.player, Some("Show"));
103 }
104
105 pub fn end_temp_show_cards(&mut self) {}
106
107 pub fn reveal_delayed(&mut self, delayed: &DelayedReveal) {
108 let owner = delayed.owner.unwrap_or(self.player);
109 for &zone in &delayed.zone {
110 self.reveal_cards(
111 &delayed.cards,
112 zone,
113 owner,
114 delayed.message_prefix.as_deref(),
115 );
116 }
117 }
118
119 pub fn choose_attackers(
120 &mut self,
121 available: &[CardId],
122 possible_defenders: &[DefenderId],
123 ) -> Vec<(CardId, DefenderId)> {
124 self.agent
125 .choose_attackers(self.player, available, possible_defenders)
126 }
127
128 pub fn choose_blockers(
129 &mut self,
130 attackers: &[CardId],
131 available_blockers: &[CardId],
132 max_blockers: Option<usize>,
133 ) -> Vec<(CardId, CardId)> {
134 self.agent
135 .choose_blockers(self.player, attackers, available_blockers, max_blockers)
136 }
137
138 pub fn choose_blocker_for(&mut self, attackers: &[CardId], blocker: CardId) -> Option<CardId> {
139 self.agent
140 .choose_blocker_for(self.player, attackers, blocker)
141 }
142
143 pub fn exert_attackers(&mut self, attackers: &[CardId]) -> Vec<CardId> {
144 self.agent.exert_attackers(self.player, attackers)
145 }
146
147 pub fn enlist_attackers(&mut self, attackers: &[CardId]) -> Vec<CardId> {
148 self.agent.enlist_attackers(self.player, attackers)
149 }
150
151 pub fn choose_damage_assignment_order(
152 &mut self,
153 attacker: CardId,
154 blockers: &[CardId],
155 ) -> Vec<CardId> {
156 self.agent
157 .choose_damage_assignment_order(self.player, attacker, blockers)
158 }
159
160 pub fn assign_combat_damage(
161 &mut self,
162 attacker: CardId,
163 blockers_in_order: &[CardId],
164 defender: Option<DefenderId>,
165 damage_to_assign: i32,
166 ) -> Vec<(Option<CardId>, i32)> {
167 self.agent.assign_combat_damage(
168 self.game,
169 self.player,
170 attacker,
171 blockers_in_order,
172 defender,
173 damage_to_assign,
174 )
175 }
176
177 pub fn choose_target_player(
178 &mut self,
179 valid: &[PlayerId],
180 sa: Option<&SpellAbility>,
181 ) -> Option<PlayerId> {
182 self.agent.choose_target_player(self.player, valid, sa)
183 }
184
185 pub fn choose_target_card(
186 &mut self,
187 valid: &[CardId],
188 sa: Option<&SpellAbility>,
189 ) -> Option<CardId> {
190 self.agent.choose_target_card(self.player, valid, sa)
191 }
192
193 pub fn choose_target_any(
194 &mut self,
195 valid_players: &[PlayerId],
196 valid_cards: &[CardId],
197 sa: Option<&SpellAbility>,
198 ) -> TargetChoice {
199 self.agent
200 .choose_target_any(self.player, valid_players, valid_cards, sa)
201 }
202
203 pub fn choose_entities_for_effect(
204 &mut self,
205 candidates: &[GameEntity],
206 min: usize,
207 max: usize,
208 ) -> Vec<GameEntity> {
209 self.agent
210 .choose_entities_for_effect(self.player, candidates, min, max)
211 }
212
213 pub fn choose_single_entity_for_effect(
214 &mut self,
215 candidates: &[GameEntity],
216 ) -> Option<GameEntity> {
217 self.agent
225 .choose_single_entity_for_effect(self.player, candidates, false)
226 }
227
228 pub fn choose_cards_for_effect(
229 &mut self,
230 valid: &[CardId],
231 min: usize,
232 max: usize,
233 ) -> Vec<CardId> {
234 self.agent
235 .choose_cards_for_effect(self.player, valid, min, max)
236 }
237
238 pub fn choose_cards_for_zone_change(
239 &mut self,
240 valid: &[CardId],
241 min: usize,
242 max: usize,
243 select_prompt: &str,
244 ) -> Vec<CardId> {
245 self.agent.choose_cards_for_zone_change(
246 self.game,
247 self.player,
248 valid,
249 min,
250 max,
251 select_prompt,
252 )
253 }
254
255 pub fn choose_single_card_for_zone_change(
256 &mut self,
257 valid: &[CardId],
258 select_prompt: &str,
259 is_optional: bool,
260 ) -> Option<CardId> {
261 self.agent.choose_single_card_for_zone_change(
262 self.game,
263 self.player,
264 valid,
265 select_prompt,
266 is_optional,
267 )
268 }
269
270 pub fn choose_type(&mut self, type_category: &str, valid_types: &[String]) -> Option<String> {
271 self.agent
272 .choose_type(self.player, type_category, valid_types)
273 }
274
275 pub fn choose_some_type(
276 &mut self,
277 type_category: &str,
278 valid_types: &[String],
279 ) -> Option<String> {
280 self.choose_type(type_category, valid_types)
281 }
282
283 pub fn choose_number_from_list(
284 &mut self,
285 choices: &[i32],
286 message: &str,
287 source_card_id: Option<CardId>,
288 ) -> Option<i32> {
289 self.agent
290 .choose_number_from_list(self.player, choices, message, source_card_id)
291 }
292
293 pub fn choose_binary(
294 &mut self,
295 question: &str,
296 kind: BinaryChoiceKind,
297 default_choice: Option<bool>,
298 source: Option<CardId>,
299 api: Option<crate::ability::api_type::ApiType>,
300 ) -> bool {
301 self.agent
302 .choose_binary(self.player, question, kind, default_choice, source, api)
303 }
304
305 pub fn confirm_action(
306 &mut self,
307 mode: Option<&str>,
308 message: &str,
309 options: &[String],
310 source: Option<CardId>,
311 api: Option<crate::ability::api_type::ApiType>,
312 ) -> bool {
313 self.agent
314 .confirm_action(self.player, mode, message, options, source, api)
315 }
316
317 pub fn confirm_payment(
318 &mut self,
319 cost_kind: &str,
320 message: &str,
321 source: Option<CardId>,
322 api: Option<crate::ability::api_type::ApiType>,
323 ) -> bool {
324 self.agent
325 .confirm_payment(self.player, cost_kind, message, source, api)
326 }
327
328 pub fn pay_cost_to_prevent_effect(
329 &mut self,
330 cost_kind: &str,
331 message: &str,
332 source: Option<CardId>,
333 api: Option<crate::ability::api_type::ApiType>,
334 can_pay: bool,
335 targets: &[crate::agent::GameEntity],
336 effect_text: &str,
337 ) -> bool {
338 self.agent.pay_cost_to_prevent_effect(
339 self.player,
340 cost_kind,
341 message,
342 source,
343 api,
344 can_pay,
345 targets,
346 effect_text,
347 )
348 }
349
350 pub fn confirm_bid_action(
351 &mut self,
352 mode: Option<&str>,
353 message: &str,
354 bid: i32,
355 winner: Option<PlayerId>,
356 ) -> bool {
357 let mut options = vec![format!("Bid {bid}")];
358 if let Some(winner) = winner {
359 options.push(format!("Winner {}", winner.0));
360 }
361 self.confirm_action(mode, message, &options, None, None)
362 }
363
364 pub fn confirm_replacement_effect(
365 &mut self,
366 description: &str,
367 source: Option<CardId>,
368 ) -> bool {
369 self.confirm_action(Some("ReplacementEffect"), description, &[], source, None)
370 }
371
372 pub fn confirm_static_application(
373 &mut self,
374 message: &str,
375 logic: Option<&str>,
376 source: Option<CardId>,
377 ) -> bool {
378 let options = logic.into_iter().map(str::to_string).collect::<Vec<_>>();
379 self.confirm_action(Some("StaticAbility"), message, &options, source, None)
380 }
381
382 pub fn choose_optional_trigger(
383 &mut self,
384 description: &str,
385 source: Option<CardId>,
386 api: Option<crate::ability::api_type::ApiType>,
387 ) -> bool {
388 self.agent
389 .choose_optional_trigger(self.player, description, source, api)
390 }
391
392 pub fn choose_target_spell(
393 &mut self,
394 valid_entries: &[u32],
395 source: Option<CardId>,
396 ) -> Option<u32> {
397 self.agent
398 .choose_target_spell(self.player, valid_entries, source)
399 }
400
401 pub fn choose_mode(
402 &mut self,
403 descriptions: &[String],
404 min: usize,
405 max: usize,
406 source_card_id: Option<CardId>,
407 ) -> Vec<usize> {
408 self.agent
409 .choose_mode(self.player, descriptions, min, max, source_card_id)
410 }
411
412 pub fn pay_mana_cost(
413 &mut self,
414 card_id: CardId,
415 card_name: &str,
416 mana_cost: &str,
417 mana_cost_display: &str,
418 mana_cost_checkpoint: &str,
419 can_confirm_from_pool: bool,
420 allow_reserved_source_reuse: bool,
421 reserved_sacrifices: &[CardId],
422 mana_ability_options: &[ManaAbilityOption],
423 tappable_lands: &[CardId],
424 untappable_lands: &[CardId],
425 mana_pool: &ManaPool,
426 ) -> ManaCostAction {
427 self.agent.pay_mana_cost(
428 self.player,
429 card_id,
430 card_name,
431 mana_cost,
432 mana_cost_display,
433 mana_cost_checkpoint,
434 can_confirm_from_pool,
435 allow_reserved_source_reuse,
436 reserved_sacrifices,
437 mana_ability_options,
438 tappable_lands,
439 untappable_lands,
440 mana_pool,
441 )
442 }
443
444 pub fn pay_combat_cost(
445 &mut self,
446 attacker: CardId,
447 cost: i32,
448 description: &str,
449 mana_ability_options: &[ManaAbilityOption],
450 tappable_lands: &[CardId],
451 untappable_lands: &[CardId],
452 mana_pool_total: i32,
453 ) -> CombatCostAction {
454 self.agent.pay_combat_cost(
455 self.player,
456 attacker,
457 cost,
458 description,
459 mana_ability_options,
460 tappable_lands,
461 untappable_lands,
462 mana_pool_total,
463 )
464 }
465
466 pub fn decide_cost_part(
467 &mut self,
468 source: CardId,
469 cost_part: &CostPart,
470 ) -> Option<PaymentDecision> {
471 self.agent
472 .decide_cost_part(self.player, source, cost_part, self.game)
473 }
474
475 pub fn order_cost_parts(&mut self, parts: Vec<CostPart>) -> Vec<CostPart> {
476 self.agent.order_cost_parts(parts)
477 }
478
479 pub fn choose_color(&mut self, valid_colors: &[String]) -> Option<String> {
480 self.agent.choose_color(self.player, valid_colors)
481 }
482
483 pub fn choose_card_name(&mut self, valid_names: &[String]) -> Option<String> {
484 self.agent.choose_card_name(self.player, valid_names)
485 }
486
487 pub fn choose_scry(&mut self, source: Option<CardId>, cards: &[CardId]) -> Vec<Vec<CardId>> {
488 self.agent
489 .choose_scry(self.game, self.player, source, cards)
490 }
491
492 pub fn choose_surveil(&mut self, source: Option<CardId>, cards: &[CardId]) -> Vec<Vec<CardId>> {
493 self.agent
494 .choose_surveil(self.game, self.player, source, cards)
495 }
496
497 pub fn choose_reorder_library(&mut self, cards: &[CardId]) -> Vec<CardId> {
498 self.agent
499 .choose_reorder_library(self.game, self.player, cards)
500 }
501
502 pub fn choose_discard(&mut self, hand: &[CardId], num: usize) -> Vec<CardId> {
503 self.agent.choose_discard(self.player, hand, num)
504 }
505
506 pub fn choose_random_discard(&mut self, hand: &[CardId], num: usize) -> Vec<CardId> {
507 self.agent.choose_random_discard(self.player, hand, num)
508 }
509
510 pub fn choose_delve(
511 &mut self,
512 valid: &[CardId],
513 max: usize,
514 source: Option<CardId>,
515 ) -> Vec<CardId> {
516 self.agent.choose_delve(self.player, valid, max, source)
517 }
518
519 pub fn choose_improvise(
520 &mut self,
521 untapped_artifacts: &[CardId],
522 remaining_cost: &ManaCost,
523 source: Option<CardId>,
524 ) -> Vec<CardId> {
525 self.agent
526 .choose_improvise(self.player, untapped_artifacts, remaining_cost, source)
527 }
528
529 pub fn choose_convoke(
530 &mut self,
531 untapped_creatures: &[CardId],
532 remaining_cost: &ManaCost,
533 source: Option<CardId>,
534 ) -> Vec<CardId> {
535 self.agent
536 .choose_convoke(self.player, untapped_creatures, remaining_cost, source)
537 }
538
539 pub fn specify_mana_combo(
540 &mut self,
541 available_colors: &[String],
542 amount: usize,
543 source: Option<CardId>,
544 express_choice: Option<u16>,
545 ) -> Vec<String> {
546 self.agent.specify_mana_combo(
547 self.player,
548 available_colors,
549 amount,
550 source,
551 express_choice,
552 )
553 }
554
555 pub fn choose_roll_swap_value(
556 &mut self,
557 current_result: i32,
558 power: i32,
559 toughness: i32,
560 source: Option<CardId>,
561 ) -> Option<RollSwapChoice> {
562 self.agent
563 .choose_roll_swap_value(self.player, current_result, power, toughness, source)
564 }
565
566 pub fn reveal(
567 &mut self,
568 cards: &[CardId],
569 zone: ZoneType,
570 owner: PlayerId,
571 message_prefix: Option<&str>,
572 ) {
573 self.reveal_cards(cards, zone, owner, message_prefix);
574 }
575
576 pub fn notify_of_value(&mut self, label: &str, value: &str) {
577 self.notify(crate::agent::notification::GameNotification::Event(
578 GameLogEvent::info(format!("{label}: {value}")).with_player(self.player),
579 ));
580 }
581
582 pub fn choose_single_replacement_effect(&mut self, descriptions: &[String]) -> usize {
583 self.agent
584 .choose_single_replacement_effect(self.player, descriptions)
585 }
586
587 pub fn choose_land_or_spell(&mut self) -> Option<bool> {
588 self.agent.choose_land_or_spell(self.player)
589 }
590
591 pub fn choose_sector(&mut self, sectors: &[String]) -> Option<String> {
592 self.choose_some_type("Sector", sectors)
593 }
594
595 pub fn add_keyword_cost(&mut self, prompt: &str) -> bool {
596 self.choose_binary(
597 prompt,
598 BinaryChoiceKind::AddOrRemove,
599 Some(true),
600 None,
601 None,
602 )
603 }
604
605 pub fn cheat_shuffle(&mut self) {
606 self.notify(crate::agent::notification::GameNotification::Event(
607 GameLogEvent::info("Shuffle requested"),
608 ));
609 }
610
611 pub fn reset_inputs(&mut self) {}
612
613 pub fn can_play_unlimited_lands(&self) -> bool {
614 self.game.player(self.player).unlimited_land_plays
615 }
616}