Skip to main content

manabrew_engine/player/actions/
player_action.rs

1use serde::{Deserialize, Serialize};
2
3use crate::agent::types::{MainPhaseAction, PlayOption, TargetChoice};
4use crate::agent::PlayerAgent;
5use crate::ids::{CardId, PlayerId};
6use crate::player::PlayerController;
7
8pub const STATIC_ALTERNATIVE_ABILITY_INDEX: usize = usize::MAX;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum PlayerAction {
12    PassPriority,
13    Concede,
14    FinishTargeting,
15    CastSpell(PlayOption),
16    ActivateMana(CardId, Option<usize>, Option<u16>),
17    UndoMana(CardId),
18    ActivateAbility(AbilityRef),
19    PayCost(CardId),
20    PayManaFromPool(ManaChoice),
21    SelectCard(CardId),
22    SelectPlayer(PlayerId),
23    TargetEntity(TargetEntity),
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27pub struct AbilityRef {
28    pub card_id: CardId,
29    pub ability_index: usize,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
33pub struct ManaChoice {
34    pub color_code: u8,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
38pub enum TargetEntity {
39    Card(CardId),
40    Player(PlayerId),
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum PlayerActionOutcome {
45    Priority(MainPhaseAction),
46    Target(TargetChoice),
47    Pending,
48}
49
50impl PlayerAction {
51    pub fn to_priority_action(
52        self,
53        playable: &[PlayOption],
54        tappable_lands: &[CardId],
55        untappable_lands: &[CardId],
56        activatable: &[(CardId, usize)],
57    ) -> Option<MainPhaseAction> {
58        match self {
59            PlayerAction::PassPriority => Some(MainPhaseAction::Pass),
60            PlayerAction::CastSpell(play) => playable
61                .contains(&play)
62                .then_some(MainPhaseAction::Play(play)),
63            PlayerAction::ActivateMana(card_id, ability_index, express_choice) => tappable_lands
64                .contains(&card_id)
65                .then_some(MainPhaseAction::ActivateMana(
66                    card_id,
67                    ability_index,
68                    express_choice,
69                )),
70            PlayerAction::UndoMana(card_id) => untappable_lands
71                .contains(&card_id)
72                .then_some(MainPhaseAction::UntapMana(card_id)),
73            PlayerAction::ActivateAbility(ability) => activatable
74                .iter()
75                .copied()
76                .find(|(card_id, index)| {
77                    *card_id == ability.card_id && *index == ability.ability_index
78                })
79                .map(|(card_id, index)| MainPhaseAction::ActivateAbility(card_id, index)),
80            _ => None,
81        }
82    }
83
84    pub fn to_target_choice(self) -> Option<TargetChoice> {
85        match self {
86            PlayerAction::SelectCard(card_id) => Some(TargetChoice::Card(card_id)),
87            PlayerAction::SelectPlayer(player_id) => Some(TargetChoice::Player(player_id)),
88            PlayerAction::TargetEntity(TargetEntity::Card(card_id)) => {
89                Some(TargetChoice::Card(card_id))
90            }
91            PlayerAction::TargetEntity(TargetEntity::Player(player_id)) => {
92                Some(TargetChoice::Player(player_id))
93            }
94            PlayerAction::FinishTargeting => Some(TargetChoice::None),
95            _ => None,
96        }
97    }
98
99    pub fn run<A: PlayerAgent + ?Sized>(
100        self,
101        _controller: &mut PlayerController<'_, A>,
102        playable: &[PlayOption],
103        tappable_lands: &[CardId],
104        untappable_lands: &[CardId],
105        activatable: &[(CardId, usize)],
106    ) -> PlayerActionOutcome {
107        if let Some(priority) =
108            self.to_priority_action(playable, tappable_lands, untappable_lands, activatable)
109        {
110            return PlayerActionOutcome::Priority(priority);
111        }
112        if let Some(target) = self.to_target_choice() {
113            return PlayerActionOutcome::Target(target);
114        }
115        PlayerActionOutcome::Pending
116    }
117}