1use crate::agent::notification::GameNotification;
2use crate::card::CounterType;
3use crate::combat::DefenderId;
4use crate::cost::payment_decision::PaymentDecision;
5use crate::cost::CostPart;
6use crate::game::GameState;
7use crate::ids::{CardId, PlayerId};
8use crate::mana::ManaPool;
9use crate::player::actions::PlayerAction;
10use crate::spellability::SpellAbility;
11
12pub mod attach_ai;
13pub mod creature_evaluator;
14pub mod game_log;
15pub mod notification;
16pub mod types;
17
18pub use game_log::*;
19pub use types::*;
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub struct PassUntilTarget {
25 pub player: PlayerId,
26 pub phase: forge_foundation::PhaseType,
27}
28
29pub trait PlayerAgent {
32 fn snapshot_state(&mut self, _game: &GameState, _mana_pools: &[ManaPool]) {}
35
36 fn take_restore_request(&mut self) -> Option<u64> {
38 None
39 }
40
41 fn reveal_cards(
42 &mut self,
43 _game: &GameState,
44 _player: PlayerId,
45 _cards: &[CardId],
46 _zone: forge_foundation::ZoneType,
47 _owner: PlayerId,
48 _message_prefix: Option<&str>,
49 ) {
50 }
51
52 fn get_pass_until(&self) -> Option<PassUntilTarget> {
59 None
60 }
61
62 fn clear_pass_until(&mut self) {}
65
66 fn mulligan_decision(&mut self, player: PlayerId, hand: &[CardId], mulligan_count: u32)
70 -> bool;
71
72 fn mulligan_decision_send(
76 &mut self,
77 _player: PlayerId,
78 _hand: &[CardId],
79 _mulligan_count: u32,
80 ) {
81 }
82
83 fn mulligan_decision_recv(
87 &mut self,
88 player: PlayerId,
89 hand: &[CardId],
90 mulligan_count: u32,
91 ) -> bool {
92 self.mulligan_decision(player, hand, mulligan_count)
93 }
94
95 fn choose_cards_to_bottom(
99 &mut self,
100 _player: PlayerId,
101 hand: &[CardId],
102 count: usize,
103 ) -> Vec<CardId> {
104 hand.iter().copied().take(count).collect()
105 }
106
107 fn choose_cards_to_bottom_send(&mut self, _player: PlayerId, _hand: &[CardId], _count: usize) {}
109
110 fn choose_cards_to_bottom_recv(
113 &mut self,
114 player: PlayerId,
115 hand: &[CardId],
116 count: usize,
117 ) -> Vec<CardId> {
118 self.choose_cards_to_bottom(player, hand, count)
119 }
120
121 fn choose_action(
127 &mut self,
128 player: PlayerId,
129 action_space: Option<&PriorityActionSpace>,
130 request_action_space: &mut dyn FnMut() -> PriorityActionSpace,
131 ) -> PlayerAction;
132
133 fn choose_attackers(
137 &mut self,
138 player: PlayerId,
139 available: &[CardId],
140 possible_defenders: &[DefenderId],
141 ) -> Vec<(CardId, DefenderId)>;
142
143 fn exert_attackers(&mut self, _player: PlayerId, _attackers: &[CardId]) -> Vec<CardId> {
148 vec![]
149 }
150
151 fn enlist_attackers(&mut self, _player: PlayerId, _attackers: &[CardId]) -> Vec<CardId> {
156 vec![]
157 }
158
159 fn choose_blockers(
162 &mut self,
163 player: PlayerId,
164 attackers: &[CardId],
165 available_blockers: &[CardId],
166 max_blockers: Option<usize>,
167 ) -> Vec<(CardId, CardId)>;
168
169 fn choose_blocker_for(
175 &mut self,
176 player: PlayerId,
177 attackers: &[CardId],
178 blocker: CardId,
179 ) -> Option<CardId> {
180 let pairs = self.choose_blockers(player, attackers, &[blocker], None);
181 pairs
182 .into_iter()
183 .find_map(|(b, a)| if b == blocker { Some(a) } else { None })
184 }
185
186 fn choose_damage_assignment_order(
192 &mut self,
193 _player: PlayerId,
194 _attacker: CardId,
195 blockers: &[CardId],
196 ) -> Vec<CardId> {
197 blockers.to_vec()
198 }
199
200 fn assign_combat_damage(
210 &mut self,
211 game: &GameState,
212 _player: PlayerId,
213 attacker: CardId,
214 blockers_in_order: &[CardId],
215 defender_id: Option<DefenderId>,
216 damage_to_assign: i32,
217 ) -> Vec<(Option<CardId>, i32)> {
218 let mut out: Vec<(Option<CardId>, i32)> = Vec::new();
219 if damage_to_assign <= 0 {
220 return out;
221 }
222
223 let mut dmg_left = damage_to_assign;
224 let has_deathtouch = game.card(attacker).has_deathtouch();
225 let can_assign_to_defender = defender_id.is_some() && game.card(attacker).has_trample();
226 let mut last_blocker: Option<CardId> = None;
227
228 for &blocker_id in blockers_in_order {
229 if dmg_left <= 0 {
230 break;
231 }
232 if game.card(blocker_id).zone != forge_foundation::ZoneType::Battlefield {
233 continue;
234 }
235 if crate::staticability::static_ability_colorless_damage_source::target_is_protected_from_source(
236 &game.cards,
237 game.card(blocker_id),
238 game.card(attacker),
239 ) {
240 continue;
241 }
242 last_blocker = Some(blocker_id);
243
244 let blocker_card = game.card(blocker_id);
245 let is_indestructible = blocker_card.has_keyword("Indestructible");
246 let attacker_has_wither =
247 game.card(attacker).has_wither() || game.card(attacker).has_infect();
248 let lethal = if is_indestructible && !attacker_has_wither {
249 dmg_left + 1
251 } else if has_deathtouch {
252 1
253 } else if blocker_card.type_line.is_planeswalker() {
254 blocker_card
255 .counter_count(&crate::card::CounterType::Loyalty)
256 .max(0)
257 } else {
258 (blocker_card.toughness() - blocker_card.damage).max(0)
259 };
260 let assign = lethal.min(dmg_left);
261 if assign > 0 {
262 out.push((Some(blocker_id), assign));
263 dmg_left -= assign;
264 }
265 }
266
267 if dmg_left > 0 {
268 if can_assign_to_defender {
269 out.push((None, dmg_left));
270 } else if let Some(last) = last_blocker {
271 if let Some((_, amount)) = out
272 .iter_mut()
273 .find(|(assignee, _)| assignee.map(|id| id == last).unwrap_or(false))
274 {
275 *amount += dmg_left;
276 } else {
277 out.push((Some(last), dmg_left));
278 }
279 }
280 }
281 out
282 }
283
284 fn choose_targets_for(
285 &mut self,
286 sa: &mut SpellAbility,
287 game: &GameState,
288 mana_pools: &[ManaPool],
289 ) -> bool;
290
291 fn choose_target_player(
294 &mut self,
295 player: PlayerId,
296 valid: &[PlayerId],
297 sa: Option<&SpellAbility>,
298 ) -> Option<PlayerId>;
299
300 fn choose_target_card(
302 &mut self,
303 player: PlayerId,
304 valid: &[CardId],
305 sa: Option<&SpellAbility>,
306 ) -> Option<CardId>;
307
308 fn choose_target_card_from_zone(
310 &mut self,
311 player: PlayerId,
312 _zone: forge_foundation::ZoneType,
313 valid: &[CardId],
314 sa: Option<&SpellAbility>,
315 ) -> Option<CardId> {
316 self.choose_target_card(player, valid, sa)
317 }
318
319 fn choose_target_any(
321 &mut self,
322 player: PlayerId,
323 valid_players: &[PlayerId],
324 valid_cards: &[CardId],
325 sa: Option<&SpellAbility>,
326 ) -> TargetChoice;
327
328 fn choose_sacrifice(
332 &mut self,
333 _player: PlayerId,
334 valid: &[CardId],
335 _source: Option<CardId>,
336 ) -> Option<CardId> {
337 valid.first().copied()
338 }
339
340 fn choose_scry(
344 &mut self,
345 _game: &GameState,
346 _player: PlayerId,
347 _source: Option<CardId>,
348 cards: &[CardId],
349 ) -> Vec<Vec<CardId>> {
350 vec![cards.to_vec(), vec![]]
351 }
352
353 fn choose_surveil(
356 &mut self,
357 _game: &GameState,
358 _player: PlayerId,
359 _source: Option<CardId>,
360 cards: &[CardId],
361 ) -> Vec<Vec<CardId>> {
362 vec![cards.to_vec(), vec![]]
363 }
364
365 fn choose_dig(
369 &mut self,
370 _game: &GameState,
371 _player: PlayerId,
372 valid: &[CardId],
373 max: usize,
374 _optional: bool,
375 ) -> Vec<CardId> {
376 valid.iter().copied().take(max).collect()
377 }
378
379 fn choose_reorder_library(
383 &mut self,
384 _game: &GameState,
385 _player: PlayerId,
386 cards: &[CardId],
387 ) -> Vec<CardId> {
388 cards.to_vec()
389 }
390
391 fn choose_discard(&mut self, _player: PlayerId, hand: &[CardId], num: usize) -> Vec<CardId> {
395 hand.iter().copied().take(num).collect()
396 }
397
398 fn choose_discard_any_number(
402 &mut self,
403 _player: PlayerId,
404 hand: &[CardId],
405 min: usize,
406 max: usize,
407 ) -> Vec<CardId> {
408 let _ = max;
409 hand.iter().copied().take(min).collect()
410 }
411
412 fn choose_random_discard(
417 &mut self,
418 _player: PlayerId,
419 hand: &[CardId],
420 num: usize,
421 ) -> Vec<CardId> {
422 hand.iter().copied().take(num).collect()
423 }
424
425 fn choose_target_spell(
429 &mut self,
430 _player: PlayerId,
431 valid: &[u32],
432 _source: Option<CardId>,
433 ) -> Option<u32> {
434 valid.first().copied()
435 }
436
437 fn choose_mode(
446 &mut self,
447 _player: PlayerId,
448 descriptions: &[String],
449 min: usize,
450 _max: usize,
451 _source_card_id: Option<CardId>,
452 ) -> Vec<usize> {
453 (0..min.min(descriptions.len())).collect()
454 }
455
456 fn choose_spell_abilities_for_effect(
457 &mut self,
458 _player: PlayerId,
459 abilities: &[SpellAbility],
460 num: usize,
461 ) -> Vec<usize> {
462 (0..num.min(abilities.len())).collect()
463 }
464
465 fn choose_single_entity_for_effect(
467 &mut self,
468 _player: PlayerId,
469 valid: &[GameEntity],
470 _is_optional: bool,
471 ) -> Option<GameEntity> {
472 valid.first().copied()
473 }
474
475 fn get_ability_to_play(
476 &mut self,
477 _player: PlayerId,
478 abilities: &[SpellAbility],
479 ) -> Option<usize> {
480 if abilities.is_empty() {
481 None
482 } else {
483 Some(0)
484 }
485 }
486
487 fn choose_legend_keep(&mut self, _player: PlayerId, duplicates: &[CardId]) -> CardId {
491 duplicates[0]
492 }
493
494 fn choose_optional_trigger(
501 &mut self,
502 _player: PlayerId,
503 _description: &str,
504 _source: Option<CardId>,
505 _api: Option<crate::ability::api_type::ApiType>,
506 ) -> bool {
507 true
508 }
509
510 fn confirm_replacement_effect(
511 &mut self,
512 _player: PlayerId,
513 _question: &str,
514 _effect_description: &str,
515 _source: Option<CardId>,
516 ) -> bool {
517 true
518 }
519
520 fn confirm_action(
525 &mut self,
526 _player: PlayerId,
527 _mode: Option<&str>,
528 _message: &str,
529 _options: &[String],
530 _source: Option<CardId>,
531 _api: Option<crate::ability::api_type::ApiType>,
532 ) -> bool {
533 false
534 }
535
536 fn confirm_payment(
537 &mut self,
538 player: PlayerId,
539 cost_kind: &str,
540 message: &str,
541 source: Option<CardId>,
542 api: Option<crate::ability::api_type::ApiType>,
543 ) -> bool {
544 let _ = (player, cost_kind, message, source, api);
545 true
546 }
547
548 fn pay_cost_to_prevent_effect(
549 &mut self,
550 player: PlayerId,
551 cost_kind: &str,
552 message: &str,
553 source: Option<CardId>,
554 api: Option<crate::ability::api_type::ApiType>,
555 can_pay: bool,
556 targets: &[GameEntity],
557 effect_text: &str,
558 ) -> bool {
559 let _ = (targets, effect_text);
560 if !can_pay {
561 return false;
562 }
563 self.confirm_payment(player, cost_kind, message, source, api)
564 }
565
566 fn choose_binary(
567 &mut self,
568 player: PlayerId,
569 question: &str,
570 kind: BinaryChoiceKind,
571 _default_choice: Option<bool>,
572 source: Option<CardId>,
573 api: Option<crate::ability::api_type::ApiType>,
574 ) -> bool {
575 let (left, right) = kind.labels();
576 self.confirm_action(
577 player,
578 Some(kind.as_str()),
579 question,
580 &[right.to_string(), left.to_string()],
581 source,
582 api,
583 )
584 }
585
586 fn choose_kicker(
592 &mut self,
593 _player: PlayerId,
594 _kicker_cost: &str,
595 _source: Option<CardId>,
596 ) -> bool {
597 false
598 }
599
600 fn help_pay_assist(&mut self, _player: PlayerId, _card_name: &str, _max_generic: u32) -> u32 {
603 0
604 }
605
606 fn choose_buyback(
610 &mut self,
611 _player: PlayerId,
612 _buyback_cost: &str,
613 _source: Option<CardId>,
614 ) -> bool {
615 false
616 }
617
618 fn choose_multikicker(
623 &mut self,
624 _player: PlayerId,
625 _cost: &str,
626 _max_kicks: u32,
627 _source: Option<CardId>,
628 ) -> u32 {
629 0
630 }
631
632 fn choose_replicate(
637 &mut self,
638 _player: PlayerId,
639 _cost: &str,
640 _max_replicates: u32,
641 _source: Option<CardId>,
642 ) -> u32 {
643 0
644 }
645
646 fn choose_color(&mut self, _player: PlayerId, valid_colors: &[String]) -> Option<String> {
650 valid_colors.first().cloned()
651 }
652
653 fn choose_colors(
655 &mut self,
656 _player: PlayerId,
657 valid_colors: &[String],
658 min: usize,
659 max: usize,
660 ) -> Vec<String> {
661 let hi = max.min(valid_colors.len());
662 let lo = min.min(hi);
663 valid_colors.iter().take(lo).cloned().collect()
664 }
665
666 fn choose_cards_for_effect(
670 &mut self,
671 _player: PlayerId,
672 valid: &[CardId],
673 _min: usize,
674 max: usize,
675 ) -> Vec<CardId> {
676 valid.iter().copied().take(max).collect()
677 }
678
679 fn choose_tap_type_for_cost(
685 &mut self,
686 player: PlayerId,
687 valid: &[CardId],
688 _min_total_power: i32,
689 _card_powers: &[(CardId, i32)],
690 _card_sort_powers: &[(CardId, i32)],
691 _sa: Option<&SpellAbility>,
692 ) -> Vec<CardId> {
693 self.choose_cards_for_effect(player, valid, 1, valid.len())
694 }
695
696 fn choose_entities_for_effect(
698 &mut self,
699 _player: PlayerId,
700 candidates: &[GameEntity],
701 _min: usize,
702 max: usize,
703 ) -> Vec<GameEntity> {
704 candidates.iter().copied().take(max).collect()
705 }
706
707 fn choose_single_card_for_zone_change(
709 &mut self,
710 _game: &GameState,
711 player: PlayerId,
712 valid: &[CardId],
713 _select_prompt: &str,
714 _is_optional: bool,
715 ) -> Option<CardId> {
716 self.choose_cards_for_effect(player, valid, 1, 1)
717 .into_iter()
718 .next()
719 }
720
721 fn choose_cards_for_zone_change(
723 &mut self,
724 _game: &GameState,
725 player: PlayerId,
726 valid: &[CardId],
727 min: usize,
728 max: usize,
729 _select_prompt: &str,
730 ) -> Vec<CardId> {
731 self.choose_cards_for_effect(player, valid, min, max)
732 }
733
734 fn choose_type(
739 &mut self,
740 _player: PlayerId,
741 _type_category: &str,
742 valid_types: &[String],
743 ) -> Option<String> {
744 valid_types.first().cloned()
745 }
746
747 fn choose_counter_type(
749 &mut self,
750 _player: PlayerId,
751 options: &[CounterType],
752 _prompt: &str,
753 ) -> Option<CounterType> {
754 options.first().cloned()
755 }
756
757 fn choose_card_name(&mut self, _player: PlayerId, valid_names: &[String]) -> Option<String> {
761 valid_names.first().cloned()
762 }
763
764 fn choose_number(
768 &mut self,
769 _player: PlayerId,
770 _source: Option<CardId>,
771 _title: &str,
772 _description: Option<&str>,
773 min: i32,
774 _max: i32,
775 ) -> Option<i32> {
776 Some(min)
777 }
778
779 fn choose_number_for_keyword_cost(
782 &mut self,
783 _player: PlayerId,
784 _max: i32,
785 _prompt: &str,
786 _source: Option<CardId>,
787 ) -> i32 {
788 0
789 }
790
791 fn choose_number_from_list(
793 &mut self,
794 _player: PlayerId,
795 choices: &[i32],
796 _message: &str,
797 _source_card_id: Option<CardId>,
798 ) -> Option<i32> {
799 choices.first().copied()
800 }
801
802 fn choose_roll_to_ignore(
804 &mut self,
805 _player: PlayerId,
806 rolls: &[i32],
807 _source: Option<CardId>,
808 ) -> Option<i32> {
809 rolls.first().copied()
810 }
811
812 fn choose_roll_to_swap(
814 &mut self,
815 _player: PlayerId,
816 rolls: &[i32],
817 _source: Option<CardId>,
818 ) -> Option<i32> {
819 rolls.first().copied()
820 }
821
822 fn choose_dice_to_reroll(
824 &mut self,
825 _player: PlayerId,
826 _rolls: &[i32],
827 _source: Option<CardId>,
828 ) -> Vec<i32> {
829 vec![]
830 }
831
832 fn choose_roll_to_modify(
834 &mut self,
835 _player: PlayerId,
836 rolls: &[i32],
837 _source: Option<CardId>,
838 ) -> Option<i32> {
839 rolls.first().copied()
840 }
841
842 fn choose_roll_swap_value(
844 &mut self,
845 _player: PlayerId,
846 _current_result: i32,
847 _power: i32,
848 _toughness: i32,
849 _source: Option<CardId>,
850 ) -> Option<RollSwapChoice> {
851 Some(RollSwapChoice::Power)
852 }
853
854 fn flip_coin_call(&mut self, _player: PlayerId) -> bool {
858 true
859 }
860
861 fn choose_phyrexian_pay_life(
865 &mut self,
866 _player: PlayerId,
867 _color: &str,
868 _source: Option<CardId>,
869 ) -> bool {
870 false
871 }
872
873 fn pay_combat_cost(
876 &mut self,
877 _player: PlayerId,
878 _attacker: CardId,
879 _cost: i32,
880 _description: &str,
881 _mana_ability_options: &[ManaAbilityOption],
882 _tappable_lands: &[CardId],
883 _untappable_lands: &[CardId],
884 _mana_pool_total: i32,
885 ) -> CombatCostAction {
886 CombatCostAction::Decline
887 }
888
889 fn choose_delve(
894 &mut self,
895 _player: PlayerId,
896 valid: &[CardId],
897 max: usize,
898 _source: Option<CardId>,
899 ) -> Vec<CardId> {
900 valid.iter().copied().take(max).collect()
901 }
902
903 fn choose_improvise(
907 &mut self,
908 _player: PlayerId,
909 _untapped_artifacts: &[CardId],
910 _remaining_cost: &forge_foundation::ManaCost,
911 _source: Option<CardId>,
912 ) -> Vec<CardId> {
913 vec![]
914 }
915
916 fn choose_convoke(
920 &mut self,
921 _player: PlayerId,
922 _untapped_creatures: &[CardId],
923 _remaining_cost: &forge_foundation::ManaCost,
924 _source: Option<CardId>,
925 ) -> Vec<CardId> {
926 vec![]
927 }
928
929 fn pay_mana_cost(
936 &mut self,
937 _player: PlayerId,
938 _card_id: CardId,
939 _card_name: &str,
940 _mana_cost: &str,
941 _mana_cost_display: &str,
942 _mana_cost_checkpoint: &str,
943 _can_confirm_from_pool: bool,
944 _allow_reserved_source_reuse: bool,
945 _reserved_sacrifices: &[CardId],
946 _mana_ability_options: &[ManaAbilityOption],
947 _tappable_lands: &[CardId],
948 _untappable_lands: &[CardId],
949 _mana_pool: &ManaPool,
950 ) -> ManaCostAction {
951 ManaCostAction::AttemptedAndFailed
952 }
953
954 fn await_display_ack(&mut self) {}
965
966 fn decide_cost_part(
968 &mut self,
969 _player: PlayerId,
970 _source: CardId,
971 _cost_part: &CostPart,
972 _game: &GameState,
973 ) -> Option<PaymentDecision> {
974 None
976 }
977
978 fn pays_right_after_decision(&self) -> bool {
981 false
982 }
983
984 fn order_cost_parts(&mut self, parts: Vec<CostPart>) -> Vec<CostPart> {
986 parts
987 }
988
989 fn specify_mana_combo(
995 &mut self,
996 _player: PlayerId,
997 available_colors: &[String],
998 amount: usize,
999 _source: Option<CardId>,
1000 _express_choice: Option<u16>,
1001 ) -> Vec<String> {
1002 if let Some(first) = available_colors.first() {
1004 vec![first.clone(); amount]
1005 } else {
1006 vec!["C".to_string(); amount]
1007 }
1008 }
1009
1010 fn choose_land_or_spell(&mut self, player: PlayerId) -> Option<bool>;
1013
1014 fn notify(&mut self, _event: GameNotification) {}
1017
1018 fn choose_single_replacement_effect(
1020 &mut self,
1021 _player: PlayerId,
1022 _descriptions: &[String],
1023 ) -> usize {
1024 0
1025 }
1026}
1027
1028pub struct PassAgent;
1031
1032impl PlayerAgent for PassAgent {
1033 fn choose_targets_for(
1034 &mut self,
1035 _sa: &mut SpellAbility,
1036 _game: &GameState,
1037 _mana_pools: &[ManaPool],
1038 ) -> bool {
1039 true
1040 }
1041
1042 fn mulligan_decision(
1043 &mut self,
1044 _player: PlayerId,
1045 _hand: &[CardId],
1046 _mulligan_count: u32,
1047 ) -> bool {
1048 true
1049 }
1050
1051 fn choose_action(
1052 &mut self,
1053 _player: PlayerId,
1054 _action_space: Option<&PriorityActionSpace>,
1055 _request_action_space: &mut dyn FnMut() -> PriorityActionSpace,
1056 ) -> PlayerAction {
1057 PlayerAction::PassPriority
1058 }
1059
1060 fn choose_attackers(
1061 &mut self,
1062 _player: PlayerId,
1063 _available: &[CardId],
1064 _possible_defenders: &[DefenderId],
1065 ) -> Vec<(CardId, DefenderId)> {
1066 Vec::new() }
1068
1069 fn choose_blockers(
1070 &mut self,
1071 _player: PlayerId,
1072 _attackers: &[CardId],
1073 _available_blockers: &[CardId],
1074 _max_blockers: Option<usize>,
1075 ) -> Vec<(CardId, CardId)> {
1076 Vec::new() }
1078
1079 fn choose_target_player(
1080 &mut self,
1081 _player: PlayerId,
1082 valid: &[PlayerId],
1083 _sa: Option<&SpellAbility>,
1084 ) -> Option<PlayerId> {
1085 valid.first().copied()
1086 }
1087
1088 fn choose_target_card(
1089 &mut self,
1090 _player: PlayerId,
1091 valid: &[CardId],
1092 _sa: Option<&SpellAbility>,
1093 ) -> Option<CardId> {
1094 valid.first().copied()
1095 }
1096
1097 fn choose_target_any(
1098 &mut self,
1099 _player: PlayerId,
1100 valid_players: &[PlayerId],
1101 valid_cards: &[CardId],
1102 _sa: Option<&SpellAbility>,
1103 ) -> TargetChoice {
1104 if let Some(&pid) = valid_players.first() {
1105 TargetChoice::Player(pid)
1106 } else if let Some(&cid) = valid_cards.first() {
1107 TargetChoice::Card(cid)
1108 } else {
1109 TargetChoice::None
1110 }
1111 }
1112
1113 fn choose_sacrifice(
1114 &mut self,
1115 _player: PlayerId,
1116 valid: &[CardId],
1117 _source: Option<CardId>,
1118 ) -> Option<CardId> {
1119 valid.first().copied()
1120 }
1121
1122 fn choose_land_or_spell(&mut self, _player: PlayerId) -> Option<bool> {
1123 None
1124 }
1125}