use rbp_cards::*;
use rbp_core::*;
const MASK: u32 = 0xFF;
const BITS: u32 = MASK.count_ones();
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub enum Action {
Draw(Hand),
Fold,
Call(Chips),
Check,
Raise(Chips),
Shove(Chips),
Blind(Chips),
}
impl Action {
pub fn is_choice(&self) -> bool {
!self.is_chance()
}
pub fn is_chance(&self) -> bool {
matches!(self, Action::Draw(_))
}
pub fn is_aggro(&self) -> bool {
matches!(self, Action::Raise(_) | Action::Shove(_))
}
pub fn is_shove(&self) -> bool {
matches!(self, Action::Shove(_))
}
pub fn is_raise(&self) -> bool {
matches!(self, Action::Raise(_))
}
pub fn is_blind(&self) -> bool {
matches!(self, Action::Blind(_))
}
pub fn is_passive(&self) -> bool {
matches!(self, Action::Fold | Action::Check)
}
}
impl Action {
pub fn hand(&self) -> Option<Hand> {
if let Action::Draw(hand) = self {
Some(hand.clone())
} else {
None
}
}
pub fn amount(&self) -> Option<Chips> {
match *self {
Action::Call(amount)
| Action::Raise(amount)
| Action::Shove(amount)
| Action::Blind(amount) => Some(amount),
_ => None,
}
}
pub fn symbol(&self) -> String {
match self {
Action::Fold => format!("F"),
Action::Check => format!("X"),
Action::Draw(h) => format!("{}", h),
Action::Call(n) => format!("C{}", n),
Action::Blind(n) => format!("B{}", n),
Action::Raise(n) => format!("R{}", n),
Action::Shove(n) => format!("S{}", n),
}
}
pub fn label(&self) -> &'static str {
match self {
Action::Fold => "Fold",
Action::Check => "Check",
Action::Call(_) => "Call",
Action::Raise(_) => "Raise",
Action::Shove(_) => "Shove",
Action::Draw(_) => "Draw",
Action::Blind(_) => "Blind",
}
}
pub fn abbrev(&self) -> &'static str {
match self {
Action::Fold => "-",
Action::Check => "•",
Action::Call(_) => "=",
Action::Raise(_) => "+",
Action::Shove(_) => "!",
Action::Draw(_) => "?",
Action::Blind(_) => "$",
}
}
}
impl From<Action> for String {
fn from(action: Action) -> Self {
action.to_string()
}
}
impl From<u32> for Action {
fn from(value: u32) -> Self {
let kind = value & MASK; let data = value >> BITS; let bets = data as Chips;
match kind {
0 => Action::Fold,
1 => Action::Check,
2 => Action::Call(bets),
3 => Action::Raise(bets),
4 => Action::Shove(bets),
5 => Action::Blind(bets),
6 => Action::Draw(
[0, 1, 2]
.iter()
.map(|i| BITS * i)
.map(|r| data >> r)
.map(|x| x & MASK)
.filter(|&x| x > 0)
.map(|x| x as u8 - 1)
.map(|c| Card::from(c))
.map(|c| Hand::from(c))
.fold(Hand::empty(), Hand::add),
),
_ => panic!("at the disco"),
}
}
}
impl From<Action> for u32 {
fn from(action: Action) -> Self {
match action {
Action::Fold => 0,
Action::Check => 1,
Action::Call(bets) => 2 | ((bets as u32) << BITS),
Action::Raise(bets) => 3 | ((bets as u32) << BITS),
Action::Shove(bets) => 4 | ((bets as u32) << BITS),
Action::Blind(bets) => 5 | ((bets as u32) << BITS),
Action::Draw(hand) => {
6 | (hand
.into_iter()
.take(3)
.map(|c| u8::from(c))
.map(|c| c as u32 + 1)
.enumerate()
.map(|(i, x)| x << (i as u32 * BITS))
.fold(0u32, |hand, card| hand | card)
<< BITS)
}
}
}
}
impl TryFrom<&str> for Action {
type Error = &'static str;
fn try_from(s: &str) -> Result<Self, Self::Error> {
let parts: Vec<&str> = s.split_whitespace().collect();
match parts[0].to_uppercase().as_str() {
"CHECK" => Ok(Action::Check),
"FOLD" => Ok(Action::Fold),
"CALL" => parts
.get(1)
.and_then(|n| n.parse().ok())
.map(Action::Call)
.ok_or("invalid call amount"),
"RAISE" => parts
.get(1)
.and_then(|n| n.parse().ok())
.map(Action::Raise)
.ok_or("invalid raise amount"),
"SHOVE" => parts
.get(1)
.and_then(|n| n.parse().ok())
.map(Action::Shove)
.ok_or("invalid shove amount"),
"BLIND" => parts
.get(1)
.and_then(|n| n.parse().ok())
.map(Action::Blind)
.ok_or("invalid blind amount"),
"DEAL" => Hand::try_from(parts[1..].join(" ").as_str())
.map(Action::Draw)
.map_err(|_| "invalid deal cards"),
_ => Err("invalid action type"),
}
}
}
impl std::fmt::Display for Action {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Action::Fold => write!(f, "FOLD"),
Action::Check => write!(f, "CHECK"),
Action::Draw(hand) => write!(f, "DEAL {}", hand),
Action::Call(amount) => write!(f, "CALL {}", amount),
Action::Blind(amount) => write!(f, "BLIND {}", amount),
Action::Raise(amount) => write!(f, "RAISE {}", amount),
Action::Shove(amount) => write!(f, "SHOVE {}", amount),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bijective_u32() {
for action in [
Action::Raise(1),
Action::Blind(2),
Action::Call(32767),
Action::Shove(1738),
Action::Draw(Hand::try_from("2c Th As").unwrap()),
] {
assert!(action == Action::from(u32::from(action)));
}
}
}