use mental_poker::{ game::{ message::{ HiddenMessageToken, InputMessage, InputMessageToken, OutputMessage, OutputMessageToken, PrivateMessageToken, PublicMessageToken, RevealedMessageToken, }, PublicEvent, }, DeckPosition, OtherPlayerId, PlayerId, }; use thiserror::Error; use crate::cards::*; #[derive(Clone, Debug)] pub(crate) enum TransferAction { ToSelf(Vec), ToOther(OtherPlayerId, usize), } #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum GoFishAction { Draw, Reveal(Vec), Request { to: ReceiverId, rank: Rank }, Transfer(T), } #[derive(Clone, Debug)] pub(crate) enum GameAction { Game { from: PlayerId, action: GoFishAction, }, RevealDrawn { from: PlayerId, to: PlayerId, pos: DeckPosition, }, } #[derive(Debug, Clone, Error)] pub(crate) enum GoFishParseError { #[error("Found unexpected token {0:?}")] Unexpected(T, Vec), #[error("Expecting more input tokens")] EndOfInput, #[error("Found extra input tokens: {0:?}, {1:?}")] ExtraInput(T, Vec), } impl From), OtherPlayerId>> for OutputMessage { fn from(action: GoFishAction<(OtherPlayerId, Vec), OtherPlayerId>) -> Self { use OutputMessageToken::*; type Pub = PublicMessageToken; type Priv = PrivateMessageToken; match action { GoFishAction::Draw => [Public(Pub::Value(0))].into_iter().collect(), GoFishAction::Reveal(cards) => [Pub::Value(1)] .into_iter() .chain(cards.into_iter().map(|c| Pub::AttestedCard(c.into()))) .map(Public) .collect(), GoFishAction::Request { to, rank } => [ Pub::Value(2), Pub::Player(to.into()), Pub::Value(Into::::into(rank).into()), ] .into_iter() .map(Public) .collect(), GoFishAction::Transfer((recipient, cards)) => { [Public(Pub::Value(3)), Public(Pub::Player(recipient.into()))] .into_iter() .chain( cards .into_iter() .map(|d| Private(recipient.into(), Priv::AttestedCard(d.into()))), ) .collect() } } } } impl TryFrom> for GoFishAction { type Error = GoFishParseError>; fn try_from(message: InputMessage) -> Result { use GoFishAction::*; use GoFishParseError::*; use InputMessageToken::*; type Pub = PublicMessageToken; type Priv = PrivateMessageToken; type Hid = HiddenMessageToken; let mut tokens = message.into_iter(); match tokens.next() { Some(Public(Pub::Value(0))) => match tokens.next() { None => Ok(Draw), Some(s) => Err(ExtraInput(s, tokens.collect())), }, Some(Public(Pub::Value(1))) => tokens .try_fold(vec![], |mut v, item| match item { Public(Pub::AttestedCard(card)) => { v.push(card.into()); Ok(v) } x => Err(x), }) .map(Reveal) .map_err(|e| Unexpected(e, tokens.collect())), Some(Public(Pub::Value(2))) => match tokens.next() { Some(Public(Pub::Player(to))) => match tokens.next() { Some(r) => match r { Public(Pub::Value(rank)) => match tokens.next() { Some(s) => Err(ExtraInput(s, tokens.collect())), None => Ok(Request { to, rank: rank .try_into() .map_err(|_| ()) .and_then(|rank: u8| Rank::try_from(rank).map_err(|_| ())) .map_err(|_| Unexpected(r, tokens.collect()))?, }), }, s => Err(Unexpected(s, tokens.collect())), }, None => Err(EndOfInput), }, Some(s) => Err(Unexpected(s, tokens.collect())), None => Err(EndOfInput), }, Some(Public(Pub::Value(3))) => match tokens.next() { Some(Public(Pub::Player(PlayerId::This))) => tokens .try_fold(vec![], |mut v, item| match item { Private(Priv::AttestedCard(card)) => { v.push(card.into()); Ok(v) } x => Err(x), }) .map(|cards| Transfer(TransferAction::ToSelf(cards))) .map_err(|e| Unexpected(e, tokens.collect())), Some(Public(Pub::Player(PlayerId::Other(p)))) => tokens .try_fold(0, |count, item| match item { Hidden(o, Hid::AttestedCard) if o == p => Ok(count + 1), x => Err(x), }) .map(|count| Transfer(TransferAction::ToOther(p, count))) .map_err(|e| Unexpected(e, tokens.collect())), Some(s) => Err(Unexpected(s, tokens.collect())), None => Err(EndOfInput), }, Some(x) => Err(Unexpected(x, tokens.collect())), None => Err(EndOfInput), } } } impl TryFrom<(PlayerId, PublicEvent)> for GameAction<(PlayerId, Vec), PlayerId> { type Error = GoFishParseError>; fn try_from((from, message): (PlayerId, PublicEvent)) -> Result { use GoFishAction::*; use GoFishParseError::*; use RevealedMessageToken::*; type Pub = PublicMessageToken; type Priv = PrivateMessageToken; match message { PublicEvent::Message(message) => { let mut tokens = message.into_iter(); match tokens.next() { Some(Public(Pub::Value(0))) => match tokens.next() { None => Ok(Draw), Some(s) => Err(ExtraInput(s, tokens.collect())), }, Some(Public(Pub::Value(1))) => tokens .try_fold(vec![], |mut v, item| match item { Public(Pub::AttestedCard(card)) => { v.push(card.into()); Ok(v) } x => Err(x), }) .map(Reveal) .map_err(|e| Unexpected(e, tokens.collect())), Some(Public(Pub::Value(2))) => match tokens.next() { Some(Public(Pub::Player(to))) => match tokens.next() { Some(r) => match r { Public(Pub::Value(rank)) => match tokens.next() { Some(s) => Err(ExtraInput(s, tokens.collect())), None => Ok(Request { to, rank: rank .try_into() .map_err(|_| ()) .and_then(|rank: u8| { Rank::try_from(rank).map_err(|_| ()) }) .map_err(|_| Unexpected(r, tokens.collect()))?, }), }, s => Err(Unexpected(s, tokens.collect())), }, None => Err(EndOfInput), }, Some(s) => Err(Unexpected(s, tokens.collect())), None => Err(EndOfInput), }, Some(Public(Pub::Value(3))) => match tokens.next() { Some(Public(Pub::Player(p))) => tokens .try_fold(vec![], |mut v, item| match item { Private(pp, Priv::AttestedCard(card)) if p == pp => { v.push(card.into()); Ok(v) } x => Err(x), }) .map(|cards| Transfer((p, cards))) .map_err(|e| Unexpected(e, tokens.collect())), Some(s) => Err(Unexpected(s, tokens.collect())), None => Err(EndOfInput), }, Some(x) => Err(Unexpected(x, tokens.collect())), None => Err(EndOfInput), } } .map(|action| Self::Game { from, action }), PublicEvent::Reveal(pos, to) => Ok(Self::RevealDrawn { from, to, pos }), } } }