remotro 0.2.0

A crate allowing remote control of Balatro
Documentation
use super::{Error, consumables::Consumable, jokers::Joker};
use crate::balatro::play::PokerHandKind::{FiveOfAKind, FlushFive, FlushHouse};
use crate::balatro::{
    Screen,
    blinds::{BigBlindChoice, BossBlindChoice, SmallBlindChoice, Tag},
    deck::PlayingCard,
    menu::{Deck, Stake},
    play::PokerHand,
    shop::VoucherKind,
};
use serde::{Deserialize, Serialize};

#[allow(async_fn_in_trait)]
pub trait Hud<'a>: Sized + Screen<'a> {
    fn deck(&self) -> &[PlayingCard];
    fn hands(&self) -> u32;
    fn discards(&self) -> u32;
    fn round(&self) -> u32;
    fn ante(&self) -> u32;
    fn money(&self) -> u32;
    fn joker_slots(&self) -> u32;
    fn jokers(&self) -> &[Joker];
    fn tags(&self) -> &[Tag];
    fn run_info(&self) -> &RunInfo;
    async fn move_joker(self, from: u32, to: u32) -> Result<Self, Error>;
    async fn sell_joker(self, index: u32) -> Result<Self, Error>;
    fn consumable_slots(&self) -> u32;
    fn consumables(&self) -> &[Consumable];
    async fn move_consumable(self, from: u32, to: u32) -> Result<Self, Error>;
    async fn use_consumable(self, index: u32) -> Result<Self, Error>;
    async fn sell_consumable(self, index: u32) -> Result<Self, Error>;
}

#[doc(hidden)]
#[macro_export]
macro_rules! impl_hud {
    ($($t:ident),*) => {
        $(
            impl<'a> $crate::balatro::hud::Hud<'a> for $t<'a> {
                fn hands(&self) -> u32 {
                    self.info.hud.hands
                }

                fn deck(&self) -> &[PlayingCard] {
                    &self.info.hud.deck
                }

                fn discards(&self) -> u32 {
                    self.info.hud.discards
                }

                fn round(&self) -> u32 {
                    self.info.hud.round
                }

                fn ante(&self) -> u32 {
                    self.info.hud.ante
                }

                fn money(&self) -> u32 {
                    self.info.hud.money
                }

                fn joker_slots(&self) -> u32 {
                    self.info.hud.joker_slots
                }

                fn jokers(&self) -> &[$crate::balatro::jokers::Joker] {
                    &self.info.hud.jokers
                }

                fn tags(&self) -> &[$crate::balatro::blinds::Tag] {
                    &self.info.hud.tags
                }

                fn run_info(&self) -> &$crate::balatro::hud::RunInfo {
                    &self.info.hud.run_info
                }

                async fn move_joker(self, from: u32, to: u32) -> Result<Self, $crate::balatro::Error> {
                    let new_info = self
                        .connection
                        .request($crate::balatro::hud::protocol::MoveJoker
                            { from, to, _marker: std::marker::PhantomData::<&$t> })
                        .await??;
                    Ok(Self::new(new_info, self.connection))
                }

                async fn sell_joker(self, index: u32) -> Result<Self, $crate::balatro::Error> {
                    let new_info = self
                        .connection
                        .request($crate::balatro::hud::protocol::SellJoker
                            { index, _marker: std::marker::PhantomData::<&$t> })
                        .await??;
                    Ok(Self::new(new_info, self.connection))
                }

                fn consumable_slots(&self) -> u32 {
                    self.info.hud.consumable_slots
                }

                fn consumables(&self) -> &[$crate::balatro::consumables::Consumable] {
                    &self.info.hud.consumables
                }

                async fn move_consumable(self, from: u32, to: u32) -> Result<Self, $crate::balatro::Error> {
                    let new_info = self
                        .connection
                        .request($crate::balatro::hud::protocol::MoveConsumable { from, to, _marker: std::marker::PhantomData::<&$t> })
                        .await??;
                    Ok(Self::new(new_info, self.connection))
                }

                async fn use_consumable(self, index: u32) -> Result<Self, $crate::balatro::Error> {
                    let new_info = self
                        .connection
                        .request($crate::balatro::hud::protocol::UseConsumable { index, _marker: std::marker::PhantomData::<&$t> })
                        .await??;
                    Ok(Self::new(new_info, self.connection))
                }

                async fn sell_consumable(self, index: u32) -> Result<Self, $crate::balatro::Error> {
                    let new_info = self
                        .connection
                        .request($crate::balatro::hud::protocol::SellConsumable { index, _marker: std::marker::PhantomData::<&$t> })
                        .await??;
                    Ok(Self::new(new_info, self.connection))
                }
            }
        )*
    };
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RunInfo {
    pub poker_hands: CurrentPokerHands,
    pub blinds: CurrentBlinds,
    pub vouchers_redeemed: Vec<VoucherKind>,
    pub stake: Stake,
    pub deck: Deck,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CurrentPokerHands {
    pub high_card: CurrentPokerHand,
    pub pair: CurrentPokerHand,
    pub two_pair: CurrentPokerHand,
    pub three_of_a_kind: CurrentPokerHand,
    pub straight: CurrentPokerHand,
    pub flush: CurrentPokerHand,
    pub full_house: CurrentPokerHand,
    pub four_of_a_kind: CurrentPokerHand,
    pub straight_flush: CurrentPokerHand,
    pub five_of_a_kind: Option<CurrentPokerHand>,
    pub flush_house: Option<CurrentPokerHand>,
    pub flush_fives: Option<CurrentPokerHand>,
}
impl IntoIterator for CurrentPokerHands {
    type Item = CurrentPokerHand;
    type IntoIter = std::vec::IntoIter<Self::Item>;
    fn into_iter(self) -> Self::IntoIter {
        vec![
            self.high_card,
            self.pair,
            self.two_pair,
            self.three_of_a_kind,
            self.straight,
            self.flush,
            self.full_house,
            self.four_of_a_kind,
            self.straight_flush,
            self.five_of_a_kind.unwrap_or(CurrentPokerHand {
                hand: PokerHand {
                    kind: FiveOfAKind,
                    level: 1,
                    chips: 120,
                    mult: 12,
                },
                played: 0,
                played_round: 0,
            }),
            self.flush_house.unwrap_or(CurrentPokerHand {
                hand: PokerHand {
                    kind: FlushHouse,
                    level: 1,
                    chips: 140,
                    mult: 14,
                },
                played: 0,
                played_round: 0,
            }),
            self.flush_fives.unwrap_or(CurrentPokerHand {
                hand: PokerHand {
                    kind: FlushFive,
                    level: 1,
                    chips: 160,
                    mult: 16,
                },
                played: 0,
                played_round: 0,
            }),
        ]
        .into_iter()
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CurrentPokerHand {
    pub hand: PokerHand,
    pub played: u64,
    pub played_round: u32,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CurrentBlinds {
    pub small: SmallBlindChoice,
    pub big: BigBlindChoice,
    pub boss: BossBlindChoice,
}

pub(crate) mod protocol {
    use super::Hud;
    use crate::{
        balatro::{
            blinds::Tag, consumables::Consumable, deck::PlayingCard, hud::RunInfo, jokers::Joker,
        },
        net::protocol::{Packet, Request, Response},
    };
    use serde::{Deserialize, Serialize};

    #[derive(Serialize, Deserialize, Clone, Debug)]
    pub struct HudInfo {
        pub hands: u32,
        pub discards: u32,
        pub round: u32,
        pub ante: u32,
        pub money: u32,
        pub joker_slots: u32,
        pub jokers: Vec<Joker>,
        pub tags: Vec<Tag>,
        pub consumable_slots: u32,
        pub consumables: Vec<Consumable>,
        pub run_info: RunInfo,
        pub deck: Vec<PlayingCard>,
    }

    impl Response for HudInfo {}

    impl Packet for HudInfo {
        fn kind() -> String {
            "hud/info".to_string()
        }
    }

    #[derive(Serialize, Clone, Debug)]
    pub struct MoveJoker<'a, S: Hud<'a>> {
        pub from: u32,
        pub to: u32,
        pub _marker: std::marker::PhantomData<&'a S>,
    }

    impl<'a, S: Hud<'a>> Request for MoveJoker<'a, S> {
        type Expect = Result<S::Info, String>;
    }

    impl<'a, S: Hud<'a>> Packet for MoveJoker<'a, S> {
        fn kind() -> String {
            format!("{}/hud/jokers/move", S::name())
        }
    }

    #[derive(Serialize, Deserialize, Clone, Debug)]
    pub struct SellJoker<'a, S: Hud<'a>> {
        pub index: u32,
        pub _marker: std::marker::PhantomData<&'a S>,
    }

    impl<'a, S: Hud<'a>> Request for SellJoker<'a, S> {
        type Expect = Result<S::Info, String>;
    }

    impl<'a, S: Hud<'a>> Packet for SellJoker<'a, S> {
        fn kind() -> String {
            format!("{}/hud/jokers/sell", S::name())
        }
    }

    #[derive(Serialize, Deserialize, Clone, Debug)]
    pub struct MoveConsumable<'a, S: Hud<'a>> {
        pub from: u32,
        pub to: u32,
        pub _marker: std::marker::PhantomData<&'a S>,
    }

    impl<'a, S: Hud<'a>> Request for MoveConsumable<'a, S> {
        type Expect = Result<S::Info, String>;
    }

    impl<'a, S: Hud<'a>> Packet for MoveConsumable<'a, S> {
        fn kind() -> String {
            format!("{}/hud/consumables/move", S::name())
        }
    }

    #[derive(Serialize, Deserialize, Clone, Debug)]
    pub struct UseConsumable<'a, S: Hud<'a>> {
        pub index: u32,
        pub _marker: std::marker::PhantomData<&'a S>,
    }

    impl<'a, S: Hud<'a>> Request for UseConsumable<'a, S> {
        type Expect = Result<S::Info, String>;
    }

    impl<'a, S: Hud<'a>> Packet for UseConsumable<'a, S> {
        fn kind() -> String {
            format!("{}/hud/consumables/use", S::name())
        }
    }

    #[derive(Serialize, Deserialize, Clone, Debug)]
    pub struct SellConsumable<'a, S: Hud<'a>> {
        pub index: u32,
        pub _marker: std::marker::PhantomData<&'a S>,
    }

    impl<'a, S: Hud<'a>> Request for SellConsumable<'a, S> {
        type Expect = Result<S::Info, String>;
    }

    impl<'a, S: Hud<'a>> Packet for SellConsumable<'a, S> {
        fn kind() -> String {
            format!("{}/hud/consumables/sell", S::name())
        }
    }
}