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 set_targeting_cancellable(&mut self, _cancellable: bool) {}
292
293 fn choose_target_player(
296 &mut self,
297 player: PlayerId,
298 valid: &[PlayerId],
299 sa: Option<&SpellAbility>,
300 ) -> Option<PlayerId>;
301
302 fn choose_target_card(
304 &mut self,
305 player: PlayerId,
306 valid: &[CardId],
307 sa: Option<&SpellAbility>,
308 ) -> Option<CardId>;
309
310 fn choose_target_card_from_zone(
312 &mut self,
313 player: PlayerId,
314 _zone: forge_foundation::ZoneType,
315 valid: &[CardId],
316 sa: Option<&SpellAbility>,
317 ) -> Option<CardId> {
318 self.choose_target_card(player, valid, sa)
319 }
320
321 fn choose_target_any(
323 &mut self,
324 player: PlayerId,
325 valid_players: &[PlayerId],
326 valid_cards: &[CardId],
327 sa: Option<&SpellAbility>,
328 ) -> TargetChoice;
329
330 fn choose_sacrifice(
334 &mut self,
335 _player: PlayerId,
336 valid: &[CardId],
337 _source: Option<CardId>,
338 ) -> Option<CardId> {
339 valid.first().copied()
340 }
341
342 fn choose_scry(
346 &mut self,
347 _game: &GameState,
348 _player: PlayerId,
349 _source: Option<CardId>,
350 cards: &[CardId],
351 ) -> Vec<Vec<CardId>> {
352 vec![cards.to_vec(), vec![]]
353 }
354
355 fn choose_surveil(
358 &mut self,
359 _game: &GameState,
360 _player: PlayerId,
361 _source: Option<CardId>,
362 cards: &[CardId],
363 ) -> Vec<Vec<CardId>> {
364 vec![cards.to_vec(), vec![]]
365 }
366
367 fn choose_dig(
371 &mut self,
372 _game: &GameState,
373 _player: PlayerId,
374 valid: &[CardId],
375 max: usize,
376 _optional: bool,
377 ) -> Vec<CardId> {
378 valid.iter().copied().take(max).collect()
379 }
380
381 fn choose_reorder_library(
385 &mut self,
386 _game: &GameState,
387 _player: PlayerId,
388 cards: &[CardId],
389 ) -> Vec<CardId> {
390 cards.to_vec()
391 }
392
393 fn choose_discard(&mut self, _player: PlayerId, hand: &[CardId], num: usize) -> Vec<CardId> {
397 hand.iter().copied().take(num).collect()
398 }
399
400 fn choose_discard_any_number(
404 &mut self,
405 _player: PlayerId,
406 hand: &[CardId],
407 min: usize,
408 max: usize,
409 ) -> Vec<CardId> {
410 let _ = max;
411 hand.iter().copied().take(min).collect()
412 }
413
414 fn choose_random_discard(
419 &mut self,
420 _player: PlayerId,
421 hand: &[CardId],
422 num: usize,
423 ) -> Vec<CardId> {
424 hand.iter().copied().take(num).collect()
425 }
426
427 fn choose_target_spell(
431 &mut self,
432 _player: PlayerId,
433 valid: &[u32],
434 _source: Option<CardId>,
435 ) -> Option<u32> {
436 valid.first().copied()
437 }
438
439 fn choose_mode(
448 &mut self,
449 _player: PlayerId,
450 descriptions: &[String],
451 min: usize,
452 _max: usize,
453 _source_card_id: Option<CardId>,
454 ) -> Vec<usize> {
455 (0..min.min(descriptions.len())).collect()
456 }
457
458 fn choose_spell_abilities_for_effect(
459 &mut self,
460 _player: PlayerId,
461 abilities: &[SpellAbility],
462 num: usize,
463 ) -> Vec<usize> {
464 (0..num.min(abilities.len())).collect()
465 }
466
467 fn choose_single_entity_for_effect(
469 &mut self,
470 _player: PlayerId,
471 valid: &[GameEntity],
472 _is_optional: bool,
473 ) -> Option<GameEntity> {
474 valid.first().copied()
475 }
476
477 fn get_ability_to_play(
478 &mut self,
479 _player: PlayerId,
480 abilities: &[SpellAbility],
481 ) -> Option<usize> {
482 if abilities.is_empty() {
483 None
484 } else {
485 Some(0)
486 }
487 }
488
489 fn choose_legend_keep(&mut self, _player: PlayerId, duplicates: &[CardId]) -> CardId {
493 duplicates[0]
494 }
495
496 fn choose_optional_trigger(
503 &mut self,
504 _player: PlayerId,
505 _description: &str,
506 _source: Option<CardId>,
507 _api: Option<crate::ability::api_type::ApiType>,
508 ) -> bool {
509 true
510 }
511
512 fn confirm_replacement_effect(
513 &mut self,
514 _player: PlayerId,
515 _question: &str,
516 _effect_description: &str,
517 _source: Option<CardId>,
518 ) -> bool {
519 true
520 }
521
522 fn confirm_action(
527 &mut self,
528 _player: PlayerId,
529 _mode: Option<&str>,
530 _message: &str,
531 _options: &[String],
532 _source: Option<CardId>,
533 _api: Option<crate::ability::api_type::ApiType>,
534 ) -> bool {
535 false
536 }
537
538 fn confirm_payment(
539 &mut self,
540 player: PlayerId,
541 cost_kind: &str,
542 message: &str,
543 source: Option<CardId>,
544 api: Option<crate::ability::api_type::ApiType>,
545 ) -> bool {
546 let _ = (player, cost_kind, message, source, api);
547 true
548 }
549
550 fn pay_cost_to_prevent_effect(
551 &mut self,
552 player: PlayerId,
553 cost_kind: &str,
554 message: &str,
555 source: Option<CardId>,
556 api: Option<crate::ability::api_type::ApiType>,
557 can_pay: bool,
558 targets: &[GameEntity],
559 effect_text: &str,
560 ) -> bool {
561 let _ = (targets, effect_text);
562 if !can_pay {
563 return false;
564 }
565 self.confirm_payment(player, cost_kind, message, source, api)
566 }
567
568 fn choose_binary(
569 &mut self,
570 player: PlayerId,
571 question: &str,
572 kind: BinaryChoiceKind,
573 _default_choice: Option<bool>,
574 source: Option<CardId>,
575 api: Option<crate::ability::api_type::ApiType>,
576 ) -> bool {
577 let (left, right) = kind.labels();
578 self.confirm_action(
579 player,
580 Some(kind.as_str()),
581 question,
582 &[right.to_string(), left.to_string()],
583 source,
584 api,
585 )
586 }
587
588 fn choose_kicker(
594 &mut self,
595 _player: PlayerId,
596 _kicker_cost: &str,
597 _source: Option<CardId>,
598 ) -> bool {
599 false
600 }
601
602 fn help_pay_assist(&mut self, _player: PlayerId, _card_name: &str, _max_generic: u32) -> u32 {
605 0
606 }
607
608 fn choose_buyback(
612 &mut self,
613 _player: PlayerId,
614 _buyback_cost: &str,
615 _source: Option<CardId>,
616 ) -> bool {
617 false
618 }
619
620 fn choose_multikicker(
625 &mut self,
626 _player: PlayerId,
627 _cost: &str,
628 _max_kicks: u32,
629 _source: Option<CardId>,
630 ) -> u32 {
631 0
632 }
633
634 fn choose_replicate(
639 &mut self,
640 _player: PlayerId,
641 _cost: &str,
642 _max_replicates: u32,
643 _source: Option<CardId>,
644 ) -> u32 {
645 0
646 }
647
648 fn choose_color(&mut self, _player: PlayerId, valid_colors: &[String]) -> Option<String> {
652 valid_colors.first().cloned()
653 }
654
655 fn choose_colors(
657 &mut self,
658 _player: PlayerId,
659 valid_colors: &[String],
660 min: usize,
661 max: usize,
662 ) -> Vec<String> {
663 let hi = max.min(valid_colors.len());
664 let lo = min.min(hi);
665 valid_colors.iter().take(lo).cloned().collect()
666 }
667
668 fn choose_cards_for_effect(
672 &mut self,
673 _player: PlayerId,
674 valid: &[CardId],
675 _min: usize,
676 max: usize,
677 ) -> Vec<CardId> {
678 valid.iter().copied().take(max).collect()
679 }
680
681 fn choose_tap_type_for_cost(
687 &mut self,
688 player: PlayerId,
689 valid: &[CardId],
690 _min_total_power: i32,
691 _card_powers: &[(CardId, i32)],
692 _card_sort_powers: &[(CardId, i32)],
693 _sa: Option<&SpellAbility>,
694 ) -> Vec<CardId> {
695 self.choose_cards_for_effect(player, valid, 1, valid.len())
696 }
697
698 fn choose_entities_for_effect(
700 &mut self,
701 _player: PlayerId,
702 candidates: &[GameEntity],
703 _min: usize,
704 max: usize,
705 ) -> Vec<GameEntity> {
706 candidates.iter().copied().take(max).collect()
707 }
708
709 fn choose_single_card_for_zone_change(
711 &mut self,
712 _game: &GameState,
713 player: PlayerId,
714 valid: &[CardId],
715 _select_prompt: &str,
716 _is_optional: bool,
717 ) -> Option<CardId> {
718 self.choose_cards_for_effect(player, valid, 1, 1)
719 .into_iter()
720 .next()
721 }
722
723 fn choose_cards_for_zone_change(
725 &mut self,
726 _game: &GameState,
727 player: PlayerId,
728 valid: &[CardId],
729 min: usize,
730 max: usize,
731 _select_prompt: &str,
732 ) -> Vec<CardId> {
733 self.choose_cards_for_effect(player, valid, min, max)
734 }
735
736 fn choose_type(
741 &mut self,
742 _player: PlayerId,
743 _type_category: &str,
744 valid_types: &[String],
745 ) -> Option<String> {
746 valid_types.first().cloned()
747 }
748
749 fn choose_counter_type(
751 &mut self,
752 _player: PlayerId,
753 options: &[CounterType],
754 _prompt: &str,
755 ) -> Option<CounterType> {
756 options.first().cloned()
757 }
758
759 fn choose_card_name(&mut self, _player: PlayerId, valid_names: &[String]) -> Option<String> {
763 valid_names.first().cloned()
764 }
765
766 fn choose_number(
770 &mut self,
771 _player: PlayerId,
772 _source: Option<CardId>,
773 _title: &str,
774 _description: Option<&str>,
775 min: i32,
776 _max: i32,
777 ) -> Option<i32> {
778 Some(min)
779 }
780
781 fn choose_number_for_keyword_cost(
784 &mut self,
785 _player: PlayerId,
786 _max: i32,
787 _prompt: &str,
788 _source: Option<CardId>,
789 ) -> i32 {
790 0
791 }
792
793 fn choose_number_from_list(
795 &mut self,
796 _player: PlayerId,
797 choices: &[i32],
798 _message: &str,
799 _source_card_id: Option<CardId>,
800 ) -> Option<i32> {
801 choices.first().copied()
802 }
803
804 fn choose_roll_to_ignore(
806 &mut self,
807 _player: PlayerId,
808 rolls: &[i32],
809 _source: Option<CardId>,
810 ) -> Option<i32> {
811 rolls.first().copied()
812 }
813
814 fn choose_roll_to_swap(
816 &mut self,
817 _player: PlayerId,
818 rolls: &[i32],
819 _source: Option<CardId>,
820 ) -> Option<i32> {
821 rolls.first().copied()
822 }
823
824 fn choose_dice_to_reroll(
826 &mut self,
827 _player: PlayerId,
828 _rolls: &[i32],
829 _source: Option<CardId>,
830 ) -> Vec<i32> {
831 vec![]
832 }
833
834 fn choose_roll_to_modify(
836 &mut self,
837 _player: PlayerId,
838 rolls: &[i32],
839 _source: Option<CardId>,
840 ) -> Option<i32> {
841 rolls.first().copied()
842 }
843
844 fn choose_roll_swap_value(
846 &mut self,
847 _player: PlayerId,
848 _current_result: i32,
849 _power: i32,
850 _toughness: i32,
851 _source: Option<CardId>,
852 ) -> Option<RollSwapChoice> {
853 Some(RollSwapChoice::Power)
854 }
855
856 fn flip_coin_call(&mut self, _player: PlayerId) -> bool {
860 true
861 }
862
863 fn choose_phyrexian_pay_life(
867 &mut self,
868 _player: PlayerId,
869 _color: &str,
870 _source: Option<CardId>,
871 ) -> bool {
872 false
873 }
874
875 fn pay_combat_cost(
878 &mut self,
879 _player: PlayerId,
880 _attacker: CardId,
881 _cost: i32,
882 _description: &str,
883 _mana_ability_options: &[ManaAbilityOption],
884 _tappable_lands: &[CardId],
885 _untappable_lands: &[CardId],
886 _mana_pool_total: i32,
887 ) -> CombatCostAction {
888 CombatCostAction::Decline
889 }
890
891 fn choose_delve(
896 &mut self,
897 _player: PlayerId,
898 valid: &[CardId],
899 max: usize,
900 _source: Option<CardId>,
901 ) -> Vec<CardId> {
902 valid.iter().copied().take(max).collect()
903 }
904
905 fn choose_improvise(
909 &mut self,
910 _player: PlayerId,
911 _untapped_artifacts: &[CardId],
912 _remaining_cost: &forge_foundation::ManaCost,
913 _source: Option<CardId>,
914 ) -> Vec<CardId> {
915 vec![]
916 }
917
918 fn choose_convoke(
922 &mut self,
923 _player: PlayerId,
924 _untapped_creatures: &[CardId],
925 _remaining_cost: &forge_foundation::ManaCost,
926 _source: Option<CardId>,
927 ) -> Vec<CardId> {
928 vec![]
929 }
930
931 fn pay_mana_cost(
938 &mut self,
939 _player: PlayerId,
940 _card_id: CardId,
941 _card_name: &str,
942 _mana_cost: &str,
943 _mana_cost_display: &str,
944 _mana_cost_checkpoint: &str,
945 _can_confirm_from_pool: bool,
946 _allow_reserved_source_reuse: bool,
947 _reserved_sacrifices: &[CardId],
948 _mana_ability_options: &[ManaAbilityOption],
949 _tappable_lands: &[CardId],
950 _untappable_lands: &[CardId],
951 _mana_pool: &ManaPool,
952 ) -> ManaCostAction {
953 ManaCostAction::AttemptedAndFailed
954 }
955
956 fn await_display_ack(&mut self) {}
967
968 fn decide_cost_part(
970 &mut self,
971 _player: PlayerId,
972 _source: CardId,
973 _cost_part: &CostPart,
974 _game: &GameState,
975 ) -> Option<PaymentDecision> {
976 None
978 }
979
980 fn pays_right_after_decision(&self) -> bool {
983 false
984 }
985
986 fn order_cost_parts(&mut self, parts: Vec<CostPart>) -> Vec<CostPart> {
988 parts
989 }
990
991 fn specify_mana_combo(
997 &mut self,
998 _player: PlayerId,
999 available_colors: &[String],
1000 amount: usize,
1001 _source: Option<CardId>,
1002 _express_choice: Option<u16>,
1003 ) -> Vec<String> {
1004 if let Some(first) = available_colors.first() {
1006 vec![first.clone(); amount]
1007 } else {
1008 vec!["C".to_string(); amount]
1009 }
1010 }
1011
1012 fn choose_land_or_spell(&mut self, player: PlayerId) -> Option<bool>;
1015
1016 fn notify(&mut self, _event: GameNotification) {}
1019
1020 fn choose_single_replacement_effect(
1022 &mut self,
1023 _player: PlayerId,
1024 _descriptions: &[String],
1025 ) -> usize {
1026 0
1027 }
1028}
1029
1030pub struct PassAgent;
1033
1034impl PlayerAgent for PassAgent {
1035 fn choose_targets_for(
1036 &mut self,
1037 _sa: &mut SpellAbility,
1038 _game: &GameState,
1039 _mana_pools: &[ManaPool],
1040 ) -> bool {
1041 true
1042 }
1043
1044 fn mulligan_decision(
1045 &mut self,
1046 _player: PlayerId,
1047 _hand: &[CardId],
1048 _mulligan_count: u32,
1049 ) -> bool {
1050 true
1051 }
1052
1053 fn choose_action(
1054 &mut self,
1055 _player: PlayerId,
1056 _action_space: Option<&PriorityActionSpace>,
1057 _request_action_space: &mut dyn FnMut() -> PriorityActionSpace,
1058 ) -> PlayerAction {
1059 PlayerAction::PassPriority
1060 }
1061
1062 fn choose_attackers(
1063 &mut self,
1064 _player: PlayerId,
1065 _available: &[CardId],
1066 _possible_defenders: &[DefenderId],
1067 ) -> Vec<(CardId, DefenderId)> {
1068 Vec::new() }
1070
1071 fn choose_blockers(
1072 &mut self,
1073 _player: PlayerId,
1074 _attackers: &[CardId],
1075 _available_blockers: &[CardId],
1076 _max_blockers: Option<usize>,
1077 ) -> Vec<(CardId, CardId)> {
1078 Vec::new() }
1080
1081 fn choose_target_player(
1082 &mut self,
1083 _player: PlayerId,
1084 valid: &[PlayerId],
1085 _sa: Option<&SpellAbility>,
1086 ) -> Option<PlayerId> {
1087 valid.first().copied()
1088 }
1089
1090 fn choose_target_card(
1091 &mut self,
1092 _player: PlayerId,
1093 valid: &[CardId],
1094 _sa: Option<&SpellAbility>,
1095 ) -> Option<CardId> {
1096 valid.first().copied()
1097 }
1098
1099 fn choose_target_any(
1100 &mut self,
1101 _player: PlayerId,
1102 valid_players: &[PlayerId],
1103 valid_cards: &[CardId],
1104 _sa: Option<&SpellAbility>,
1105 ) -> TargetChoice {
1106 if let Some(&pid) = valid_players.first() {
1107 TargetChoice::Player(pid)
1108 } else if let Some(&cid) = valid_cards.first() {
1109 TargetChoice::Card(cid)
1110 } else {
1111 TargetChoice::None
1112 }
1113 }
1114
1115 fn choose_sacrifice(
1116 &mut self,
1117 _player: PlayerId,
1118 valid: &[CardId],
1119 _source: Option<CardId>,
1120 ) -> Option<CardId> {
1121 valid.first().copied()
1122 }
1123
1124 fn choose_land_or_spell(&mut self, _player: PlayerId) -> Option<bool> {
1125 None
1126 }
1127}