-#![allow(non_upper_case_globals)]
-
use std::fmt::{self, Display, Formatter};
use self::Rank::*;
}
}
-pub const TwoOfClubs: Card = Card{rank: Two, suit: Clubs};
-pub const ThreeOfClubs: Card = Card{rank: Three, suit: Clubs};
-pub const FourOfClubs: Card = Card{rank: Four, suit: Clubs};
-pub const FiveOfClubs: Card = Card{rank: Five, suit: Clubs};
-pub const SixOfClubs: Card = Card{rank: Six, suit: Clubs};
-pub const SevenOfClubs: Card = Card{rank: Seven, suit: Clubs};
-pub const EightOfClubs: Card = Card{rank: Eight, suit: Clubs};
-pub const NineOfClubs: Card = Card{rank: Nine, suit: Clubs};
-pub const TenOfClubs: Card = Card{rank: Ten, suit: Clubs};
-pub const JackOfClubs: Card = Card{rank: Jack, suit: Clubs};
-pub const QueenOfClubs: Card = Card{rank: Queen, suit: Clubs};
-pub const KingOfClubs: Card = Card{rank: King, suit: Clubs};
-pub const AceOfClubs: Card = Card{rank: Ace, suit: Clubs};
+pub const TWO_OF_CLUBS: Card = Card{rank: Two, suit: Clubs};
+pub const THREE_OF_CLUBS: Card = Card{rank: Three, suit: Clubs};
+pub const FOUR_OF_CLUBS: Card = Card{rank: Four, suit: Clubs};
+pub const FIVE_OF_CLUBS: Card = Card{rank: Five, suit: Clubs};
+pub const SIX_OF_CLUBS: Card = Card{rank: Six, suit: Clubs};
+pub const SEVEN_OF_CLUBS: Card = Card{rank: Seven, suit: Clubs};
+pub const EIGHT_OF_CLUBS: Card = Card{rank: Eight, suit: Clubs};
+pub const NINE_OF_CLUBS: Card = Card{rank: Nine, suit: Clubs};
+pub const TEN_OF_CLUBS: Card = Card{rank: Ten, suit: Clubs};
+pub const JACK_OF_CLUBS: Card = Card{rank: Jack, suit: Clubs};
+pub const QUEEN_OF_CLUBS: Card = Card{rank: Queen, suit: Clubs};
+pub const KING_OF_CLUBS: Card = Card{rank: King, suit: Clubs};
+pub const ACE_OF_CLUBS: Card = Card{rank: Ace, suit: Clubs};
-pub const TwoOfDiamonds: Card = Card{rank: Two, suit: Diamonds};
-pub const ThreeOfDiamonds: Card = Card{rank: Three, suit: Diamonds};
-pub const FourOfDiamonds: Card = Card{rank: Four, suit: Diamonds};
-pub const FiveOfDiamonds: Card = Card{rank: Five, suit: Diamonds};
-pub const SixOfDiamonds: Card = Card{rank: Six, suit: Diamonds};
-pub const SevenOfDiamonds: Card = Card{rank: Seven, suit: Diamonds};
-pub const EightOfDiamonds: Card = Card{rank: Eight, suit: Diamonds};
-pub const NineOfDiamonds: Card = Card{rank: Nine, suit: Diamonds};
-pub const TenOfDiamonds: Card = Card{rank: Ten, suit: Diamonds};
-pub const JackOfDiamonds: Card = Card{rank: Jack, suit: Diamonds};
-pub const QueenOfDiamonds: Card = Card{rank: Queen, suit: Diamonds};
-pub const KingOfDiamonds: Card = Card{rank: King, suit: Diamonds};
-pub const AceOfDiamonds: Card = Card{rank: Ace, suit: Diamonds};
+pub const TWO_OF_DIAMONDS: Card = Card{rank: Two, suit: Diamonds};
+pub const THREE_OF_DIAMONDS: Card = Card{rank: Three, suit: Diamonds};
+pub const FOUR_OF_DIAMONDS: Card = Card{rank: Four, suit: Diamonds};
+pub const FIVE_OF_DIAMONDS: Card = Card{rank: Five, suit: Diamonds};
+pub const SIX_OF_DIAMONDS: Card = Card{rank: Six, suit: Diamonds};
+pub const SEVEN_OF_DIAMONDS: Card = Card{rank: Seven, suit: Diamonds};
+pub const EIGHT_OF_DIAMONDS: Card = Card{rank: Eight, suit: Diamonds};
+pub const NINE_OF_DIAMONDS: Card = Card{rank: Nine, suit: Diamonds};
+pub const TEN_OF_DIAMONDS: Card = Card{rank: Ten, suit: Diamonds};
+pub const JACK_OF_DIAMONDS: Card = Card{rank: Jack, suit: Diamonds};
+pub const QUEEN_OF_DIAMONDS: Card = Card{rank: Queen, suit: Diamonds};
+pub const KING_OF_DIAMONDS: Card = Card{rank: King, suit: Diamonds};
+pub const ACE_OF_DIAMONDS: Card = Card{rank: Ace, suit: Diamonds};
-pub const TwoOfHearts: Card = Card{rank: Two, suit: Hearts};
-pub const ThreeOfHearts: Card = Card{rank: Three, suit: Hearts};
-pub const FourOfHearts: Card = Card{rank: Four, suit: Hearts};
-pub const FiveOfHearts: Card = Card{rank: Five, suit: Hearts};
-pub const SixOfHearts: Card = Card{rank: Six, suit: Hearts};
-pub const SevenOfHearts: Card = Card{rank: Seven, suit: Hearts};
-pub const EightOfHearts: Card = Card{rank: Eight, suit: Hearts};
-pub const NineOfHearts: Card = Card{rank: Nine, suit: Hearts};
-pub const TenOfHearts: Card = Card{rank: Ten, suit: Hearts};
-pub const JackOfHearts: Card = Card{rank: Jack, suit: Hearts};
-pub const QueenOfHearts: Card = Card{rank: Queen, suit: Hearts};
-pub const KingOfHearts: Card = Card{rank: King, suit: Hearts};
-pub const AceOfHearts: Card = Card{rank: Ace, suit: Hearts};
+pub const TWO_OF_HEARTS: Card = Card{rank: Two, suit: Hearts};
+pub const THREE_OF_HEARTS: Card = Card{rank: Three, suit: Hearts};
+pub const FOUR_OF_HEARTS: Card = Card{rank: Four, suit: Hearts};
+pub const FIVE_OF_HEARTS: Card = Card{rank: Five, suit: Hearts};
+pub const SIX_OF_HEARTS: Card = Card{rank: Six, suit: Hearts};
+pub const SEVEN_OF_HEARTS: Card = Card{rank: Seven, suit: Hearts};
+pub const EIGHT_OF_HEARTS: Card = Card{rank: Eight, suit: Hearts};
+pub const NINE_OF_HEARTS: Card = Card{rank: Nine, suit: Hearts};
+pub const TEN_OF_HEARTS: Card = Card{rank: Ten, suit: Hearts};
+pub const JACK_OF_HEARTS: Card = Card{rank: Jack, suit: Hearts};
+pub const QUEEN_OF_HEARTS: Card = Card{rank: Queen, suit: Hearts};
+pub const KING_OF_HEARTS: Card = Card{rank: King, suit: Hearts};
+pub const ACE_OF_HEARTS: Card = Card{rank: Ace, suit: Hearts};
-pub const TwoOfSpades: Card = Card{rank: Two, suit: Spades};
-pub const ThreeOfSpades: Card = Card{rank: Three, suit: Spades};
-pub const FourOfSpades: Card = Card{rank: Four, suit: Spades};
-pub const FiveOfSpades: Card = Card{rank: Five, suit: Spades};
-pub const SixOfSpades: Card = Card{rank: Six, suit: Spades};
-pub const SevenOfSpades: Card = Card{rank: Seven, suit: Spades};
-pub const EightOfSpades: Card = Card{rank: Eight, suit: Spades};
-pub const NineOfSpades: Card = Card{rank: Nine, suit: Spades};
-pub const TenOfSpades: Card = Card{rank: Ten, suit: Spades};
-pub const JackOfSpades: Card = Card{rank: Jack, suit: Spades};
-pub const QueenOfSpades: Card = Card{rank: Queen, suit: Spades};
-pub const KingOfSpades: Card = Card{rank: King, suit: Spades};
-pub const AceOfSpades: Card = Card{rank: Ace, suit: Spades};
+pub const TWO_OF_SPADES: Card = Card{rank: Two, suit: Spades};
+pub const THREE_OF_SPADES: Card = Card{rank: Three, suit: Spades};
+pub const FOUR_OF_SPADES: Card = Card{rank: Four, suit: Spades};
+pub const FIVE_OF_SPADES: Card = Card{rank: Five, suit: Spades};
+pub const SIX_OF_SPADES: Card = Card{rank: Six, suit: Spades};
+pub const SEVEN_OF_SPADES: Card = Card{rank: Seven, suit: Spades};
+pub const EIGHT_OF_SPADES: Card = Card{rank: Eight, suit: Spades};
+pub const NINE_OF_SPADES: Card = Card{rank: Nine, suit: Spades};
+pub const TEN_OF_SPADES: Card = Card{rank: Ten, suit: Spades};
+pub const JACK_OF_SPADES: Card = Card{rank: Jack, suit: Spades};
+pub const QUEEN_OF_SPADES: Card = Card{rank: Queen, suit: Spades};
+pub const KING_OF_SPADES: Card = Card{rank: King, suit: Spades};
+pub const ACE_OF_SPADES: Card = Card{rank: Ace, suit: Spades};
pub const FIFTY_TWO_CARD_DECK: [Card; 52] = [
- AceOfSpades,
- AceOfHearts,
- AceOfDiamonds,
- AceOfClubs,
- KingOfSpades,
- KingOfHearts,
- KingOfDiamonds,
- KingOfClubs,
- QueenOfSpades,
- QueenOfHearts,
- QueenOfDiamonds,
- QueenOfClubs,
- JackOfSpades,
- JackOfHearts,
- JackOfDiamonds,
- JackOfClubs,
- TenOfSpades,
- TenOfHearts,
- TenOfDiamonds,
- TenOfClubs,
- NineOfSpades,
- NineOfHearts,
- NineOfDiamonds,
- NineOfClubs,
- EightOfSpades,
- EightOfHearts,
- EightOfDiamonds,
- EightOfClubs,
- SevenOfSpades,
- SevenOfHearts,
- SevenOfDiamonds,
- SevenOfClubs,
- SixOfSpades,
- SixOfHearts,
- SixOfDiamonds,
- SixOfClubs,
- FiveOfSpades,
- FiveOfHearts,
- FiveOfDiamonds,
- FiveOfClubs,
- FourOfSpades,
- FourOfHearts,
- FourOfDiamonds,
- FourOfClubs,
- ThreeOfSpades,
- ThreeOfHearts,
- ThreeOfDiamonds,
- ThreeOfClubs,
- TwoOfSpades,
- TwoOfHearts,
- TwoOfDiamonds,
- TwoOfClubs,
+ ACE_OF_SPADES,
+ ACE_OF_HEARTS,
+ ACE_OF_DIAMONDS,
+ ACE_OF_CLUBS,
+ KING_OF_SPADES,
+ KING_OF_HEARTS,
+ KING_OF_DIAMONDS,
+ KING_OF_CLUBS,
+ QUEEN_OF_SPADES,
+ QUEEN_OF_HEARTS,
+ QUEEN_OF_DIAMONDS,
+ QUEEN_OF_CLUBS,
+ JACK_OF_SPADES,
+ JACK_OF_HEARTS,
+ JACK_OF_DIAMONDS,
+ JACK_OF_CLUBS,
+ TEN_OF_SPADES,
+ TEN_OF_HEARTS,
+ TEN_OF_DIAMONDS,
+ TEN_OF_CLUBS,
+ NINE_OF_SPADES,
+ NINE_OF_HEARTS,
+ NINE_OF_DIAMONDS,
+ NINE_OF_CLUBS,
+ EIGHT_OF_SPADES,
+ EIGHT_OF_HEARTS,
+ EIGHT_OF_DIAMONDS,
+ EIGHT_OF_CLUBS,
+ SEVEN_OF_SPADES,
+ SEVEN_OF_HEARTS,
+ SEVEN_OF_DIAMONDS,
+ SEVEN_OF_CLUBS,
+ SIX_OF_SPADES,
+ SIX_OF_HEARTS,
+ SIX_OF_DIAMONDS,
+ SIX_OF_CLUBS,
+ FIVE_OF_SPADES,
+ FIVE_OF_HEARTS,
+ FIVE_OF_DIAMONDS,
+ FIVE_OF_CLUBS,
+ FOUR_OF_SPADES,
+ FOUR_OF_HEARTS,
+ FOUR_OF_DIAMONDS,
+ FOUR_OF_CLUBS,
+ THREE_OF_SPADES,
+ THREE_OF_HEARTS,
+ THREE_OF_DIAMONDS,
+ THREE_OF_CLUBS,
+ TWO_OF_SPADES,
+ TWO_OF_HEARTS,
+ TWO_OF_DIAMONDS,
+ TWO_OF_CLUBS,
];
}
}
(ClientState::LoggedIn{ref username, state: LoggedInState::InGame{ref mut game}}, ClientMessage::TakeAction{action}) => {
+ if action.is_initiated_server_side_only() {
+ return ServerMessage::TakeActionFailure{reason: "Not authorised".to_string()};
+ }
let action = UserAction{username: username.clone(), action};
loop {
let len = game.actions_len();
use std::collections::HashSet;
use std::fmt::Debug;
-use crate::card::Card;
+use crate::card::{Card, Suit};
use crate::gamestate::GameState;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GameSettings {
Chatroom {
title: String,
- max_users: u32,
+ max_players: u32,
},
- TexasHoldEm {
- // TODO
+ KnockOutWhist {
+ title: String,
+ max_players: u32,
},
}
pub enum Action {
Join { seat: u32, chips: u64 },
AddOn { chips: u64 },
- NextToAct,
+ NextToDeal,
CommunityCard { card: Card },
ReceiveCard { card: Option<Card> },
+ EndDeal,
RevealCard { card: Card },
+ PlayCard { card: Card },
+ ChooseTrumps { suit: Suit },
Fold,
Bet { chips: u64 },
- WinPot { chips: u64 },
+ WinTrick,
+ WinHand { chips: u64 },
+ WinGame,
Message { message: String },
Leave,
}
action => action.clone(),
}
}
+
+ pub fn is_initiated_server_side_only(&self) -> bool {
+ match self {
+ Action::Join{..} | Action::AddOn{..} | Action::RevealCard{..} | Action::PlayCard{..} |
+ Action::ChooseTrumps{..} | Action::Fold | Action::Bet{..} | Action::Message{..} | Action::Leave => false,
+ Action::NextToDeal | Action::CommunityCard{..} | Action::ReceiveCard{..} | Action::EndDeal |
+ Action::WinTrick | Action::WinHand{..} | Action::WinGame => true,
+ }
+ }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ActionError {
NotAuthorised,
AlreadyJoined,
+ GameHasStarted,
NoSeatAvailable,
OutOfTurn,
+ CardNotPlayable,
InvalidActionForGameType,
}
match self {
ActionError::NotAuthorised => f.write_str("NotAuthorised"),
ActionError::AlreadyJoined => f.write_str("AlreadyJoined"),
+ ActionError::GameHasStarted => f.write_str("GameHasStarted"),
ActionError::NoSeatAvailable => f.write_str("NoSeatAvailable"),
ActionError::OutOfTurn => f.write_str("OutOfTurn"),
+ ActionError::CardNotPlayable => f.write_str("CardNotPlayable"),
ActionError::InvalidActionForGameType => f.write_str("InvalidActionForGameType"),
}
}
pub fn verify(&self, UserAction{ref username, ref action}: &UserAction) -> Result<(), ActionError> {
debug!("Verifying action: UserAction {{ username: {:?}, action: {:?} }}", username, action);
match self.summary.settings {
- GameSettings::Chatroom{max_users, ..} => match action {
+ GameSettings::Chatroom{max_players, ..} => match action {
Action::Join{seat: 0, chips: 0} if self.state.user_has_joined(username) => Err(ActionError::AlreadyJoined),
- Action::Join{seat: 0, chips: 0} if self.state.num_players() + 1 > max_users => Err(ActionError::NoSeatAvailable),
+ Action::Join{seat: 0, chips: 0} if self.state.num_players() + 1 > max_players => Err(ActionError::NoSeatAvailable),
Action::Join{seat: 0, chips: 0} => Ok(()),
Action::Message{..} if self.state.user_has_joined(username) => Ok(()),
Action::Message{..} => Err(ActionError::NotAuthorised),
Action::Leave => Err(ActionError::NotAuthorised),
_ => Err(ActionError::InvalidActionForGameType),
},
- GameSettings::TexasHoldEm{..} => {
- // TODO
- Err(ActionError::NotAuthorised)
+ GameSettings::KnockOutWhist{max_players, ..} => match action {
+ Action::Join{seat, chips: 0} if self.state.user_has_joined(username) => Err(ActionError::AlreadyJoined),
+ Action::Join{seat, chips: 0} if self.state.has_started() => Err(ActionError::GameHasStarted),
+ Action::Join{seat, chips: 0} if !self.state.seat_is_available(*seat) => Err(ActionError::NoSeatAvailable),
+ Action::Join{seat, chips: 0} => Ok(()),
+ Action::Leave if self.state.user_has_joined(username) && !self.state.has_started() => Ok(()),
+ Action::Leave if self.state.user_has_joined(username) => Err(ActionError::GameHasStarted),
+ Action::Leave => Err(ActionError::NotAuthorised),
+ Action::ReceiveCard{card: Some(card)} if self.state.is_dealing_to(username) => Ok(()),
+ Action::ReceiveCard{..} => Err(ActionError::OutOfTurn),
+ Action::NextToDeal => Ok(()),
+ Action::CommunityCard{..} if self.state.is_dealing_community_cards() => Ok(()),
+ Action::CommunityCard{..} => Err(ActionError::OutOfTurn),
+ Action::PlayCard{card} if self.state.player_is_active(username) && self.state.player_has_card(username, *card) => Ok(()),
+ Action::PlayCard{card} if !self.state.player_is_active(username) => Err(ActionError::OutOfTurn),
+ Action::PlayCard{..} => Err(ActionError::CardNotPlayable),
+ Action::WinTrick{..} => Ok(()),
+ Action::WinHand{..} => Ok(()),
+ Action::WinGame => Ok(()),
+ _ => Err(ActionError::InvalidActionForGameType),
}
}
}
-use std::collections::HashSet;
+use std::collections::{BTreeMap, HashSet};
+use crate::card::*;
use crate::game::{Action, UserAction};
+use crate::seats::Seats;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GameState {
num_players
}
+ pub fn is_dealing_to(&self, username: &str) -> bool {
+ let mut is_dealing_to = None;
+ let mut seats = Seats::new();
+ for action in &self.actions {
+ match &action.action {
+ Action::Join{seat, ..} => seats.add_player(*seat, &action.username),
+ Action::Leave => seats.remove_player(&action.username),
+ Action::NextToDeal => is_dealing_to = seats.player_after(&action.username),
+ Action::ReceiveCard{..} if is_dealing_to.as_ref().map(String::as_str) == Some(&action.username) => is_dealing_to = seats.player_after(&action.username),
+ Action::ReceiveCard{..} => error!("Expected {:?} to be dealt a card, but {:?} received one", is_dealing_to, username),
+ Action::WinHand{..} => is_dealing_to = seats.player_after(&action.username),
+ Action::EndDeal | Action::WinGame => is_dealing_to = None,
+ _ => {},
+ }
+ }
+ is_dealing_to.as_ref().map(String::as_str) == Some(username)
+ }
+
+ pub fn seat_is_available(&self, seat: u32) -> bool {
+ let mut seats = Seats::new();
+ for action in &self.actions {
+ match &action.action {
+ Action::Join{seat, ..} => seats.add_player(*seat, &action.username),
+ Action::Leave => seats.remove_player(&action.username),
+ _ => {},
+ }
+ }
+ seats.seat_is_available(seat)
+ }
+
+ pub fn is_choosing_next_to_deal(&self) -> bool {
+ for action in &self.actions {
+ if let Action::NextToDeal = &action.action {
+ return false;
+ }
+ }
+ true
+ }
+
+ pub fn has_started(&self) -> bool {
+ for action in &self.actions {
+ if let Action::NextToDeal = &action.action {
+ return true;
+ }
+ }
+ false
+ }
+
+ pub fn player_is_active(&self, username: &str) -> bool {
+ let mut dealer = None;
+ let mut active_player = None;
+ let mut next_active_player = None;
+ let mut seats = Seats::new();
+ let mut players_in_hand = Seats::new();
+ for action in &self.actions {
+ println!("processing action: {:?}", action);
+ match &action.action {
+ Action::Join{seat, ..} => seats.add_player(*seat, &action.username),
+ Action::Leave => seats.remove_player(&action.username),
+ Action::NextToDeal => {
+ players_in_hand = seats.clone();
+ next_active_player = players_in_hand.player_after(&action.username);
+ dealer = Some(action.username.clone());
+ }
+ Action::PlayCard{..} | Action::Bet{..} => {
+ active_player = players_in_hand.player_after(&action.username);
+ }
+ Action::Fold => {
+ active_player = players_in_hand.player_after(&action.username);
+ players_in_hand.remove_player(&action.username);
+ }
+ Action::WinHand{..} => active_player = None,
+ Action::EndDeal => active_player = next_active_player.clone(),
+ Action::WinGame => active_player = None,
+ _ => {},
+ }
+ println!("dealer: {:?}", dealer);
+ println!("active_player: {:?}", active_player);
+ println!("next_active_player: {:?}", next_active_player);
+ println!("seats: {:?}", seats);
+ }
+ active_player.as_ref().map(String::as_str) == Some(username)
+ }
+
+ pub fn player_has_card(&self, username: &str, card: Card) -> bool {
+ false // TODO
+ }
+
+ pub fn is_dealing_community_cards(&self) -> bool {
+ false // TODO
+ }
+
pub fn take_action(&mut self, action: UserAction) {
self.actions.push(action);
}
state.take_action(UserAction{username: "user".to_string(), action: Action::Leave});
assert_eq!(1, state.num_players());
}
+
+ #[test]
+ fn players() {
+ let mut state = GameState::new();
+ state.take_action(UserAction{username: "user".to_string(), action: Action::Join{seat: 0, chips: 10000}});
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 10000}});
+ state.take_action(UserAction{username: "user3".to_string(), action: Action::Join{seat: 2, chips: 10000}});
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::Leave});
+ assert_eq!(vec!["user", "user3"].into_iter().collect::<HashSet<_>>(), state.players());
+ }
+
+ #[test]
+ fn is_dealing_to() {
+ let mut state = GameState::new();
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::Join{seat: 0, chips: 10000}});
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 10000}});
+ state.take_action(UserAction{username: "user3".to_string(), action: Action::Join{seat: 2, chips: 10000}});
+ assert!(!state.is_dealing_to("user1"));
+ assert!(!state.is_dealing_to("user2"));
+ assert!(!state.is_dealing_to("user3"));
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::NextToDeal});
+ assert!(state.is_dealing_to("user2"));
+ state.take_action(UserAction{
+ username: "user2".to_string(),
+ action: Action::ReceiveCard{card: Some(THREE_OF_SPADES)},
+ });
+ assert!(state.is_dealing_to("user3"));
+ state.take_action(UserAction{
+ username: "user3".to_string(),
+ action: Action::ReceiveCard{card: Some(ACE_OF_HEARTS)},
+ });
+ assert!(state.is_dealing_to("user1"));
+ state.take_action(UserAction{
+ username: "user1".to_string(),
+ action: Action::ReceiveCard{card: Some(KING_OF_CLUBS)},
+ });
+ assert!(state.is_dealing_to("user2"));
+ state.take_action(UserAction{username: "dealer".to_string(), action: Action::EndDeal});
+ assert!(!state.is_dealing_to("user2"));
+ }
+
+ #[test]
+ fn player_is_active() {
+ let mut state = GameState::new();
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::Join{seat: 0, chips: 10000}});
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 10000}});
+ state.take_action(UserAction{username: "user3".to_string(), action: Action::Join{seat: 2, chips: 10000}});
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::NextToDeal});
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(ACE_OF_SPADES)}});
+ state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(KING_OF_HEARTS)}});
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(TWO_OF_DIAMONDS)}});
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(QUEEN_OF_SPADES)}});
+ state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(THREE_OF_SPADES)}});
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(FOUR_OF_CLUBS)}});
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::EndDeal});
+ assert!(state.player_is_active("user2"));
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::Bet{chips: 50}});
+ assert!(state.player_is_active("user3"));
+ state.take_action(UserAction{username: "user3".to_string(), action: Action::Bet{chips: 100}});
+ assert!(state.player_is_active("user1"));
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::Fold});
+ assert!(state.player_is_active("user2"));
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::Bet{chips: 250}});
+ assert!(state.player_is_active("user3"));
+ state.take_action(UserAction{username: "user3".to_string(), action: Action::Bet{chips: 300}});
+ assert!(state.player_is_active("user2"));
+ state.take_action(UserAction{username: "user2".to_string(), action: Action::Bet{chips: 300}});
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::CommunityCard{card: ACE_OF_HEARTS}});
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::CommunityCard{card: KING_OF_CLUBS}});
+ state.take_action(UserAction{username: "user1".to_string(), action: Action::CommunityCard{card: THREE_OF_HEARTS}});
+ }
}
mod client;
mod game;
mod gamestate;
+mod seats;
mod server;
use crate::api::ServerMessage;
let handle_client_interest = handle_client_interest(pubsub, register_update_stream_rx);
- let listener = TlsListener::build()
+ /*let listener = TlsListener::build()
.addrs("localhost:4433")
.cert("cert/cert.pem")
.key("cert/key.pem");
- let app = app.listen(listener);
+ let app = app.listen(listener);*/
+ let app = app.listen("0.0.0.0:8080");
pin_mut!(app, handle_client_interest, signal_handler);
select(select(app, handle_client_interest).map(|f| f.factor_first().0), signal_handler).await.factor_first().0?;
--- /dev/null
+use std::collections::BTreeMap;
+
+#[derive(Debug, Clone)]
+pub struct Seats {
+ players: BTreeMap<u32, String>,
+}
+
+impl Seats {
+ pub fn new() -> Self {
+ Self {
+ players: BTreeMap::new(),
+ }
+ }
+
+ pub fn add_player(&mut self, seat: u32, username: &str) {
+ self.players.insert(seat, username.to_owned());
+ }
+
+ pub fn remove_player(&mut self, username: &str) {
+ if let Some(seat) = self.players.iter().find(|(_, player)| &**player == username).map(|(&seat, _)| seat) {
+ self.players.remove(&seat);
+ }
+ }
+
+ fn player_after_seat(&self, seat: u32) -> Option<String> {
+ if let Some((_, name)) = self.players.range(seat+1..).next() {
+ Some(name.to_owned())
+ } else if let Some((_, name)) = self.players.range(..seat).next() {
+ Some(name.to_owned())
+ } else {
+ None
+ }
+ }
+
+ pub fn player_after(&self, username: &str) -> Option<String> {
+ for (&seat, player) in &self.players {
+ if player == username {
+ return self.player_after_seat(seat);
+ }
+ }
+ None
+ }
+
+ pub fn seat_is_available(&self, seat: u32) -> bool {
+ self.players.get(&seat).is_none()
+ }
+
+ pub fn players_len(&self) -> usize {
+ self.players.len()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn player_after_absent_player_is_none() {
+ let mut seats = Seats::new();
+ seats.add_player(1, "Player 1");
+ seats.add_player(2, "Player 2");
+ seats.add_player(3, "Player 3");
+ assert_eq!(None, seats.player_after("Player 4"));
+ assert!(seats.seat_is_available(0));
+ assert!(!seats.seat_is_available(1));
+ assert!(!seats.seat_is_available(2));
+ assert!(!seats.seat_is_available(3));
+ assert!(seats.seat_is_available(4));
+ }
+
+ #[test]
+ fn player_after_single_player_is_none() {
+ let mut seats = Seats::new();
+ seats.add_player(1, "Player 1");
+ assert_eq!(None, seats.player_after("Player 1"));
+ assert!(seats.seat_is_available(0));
+ assert!(!seats.seat_is_available(1));
+ assert!(seats.seat_is_available(2));
+ }
+
+ #[test]
+ fn player_after_simple() {
+ let mut seats = Seats::new();
+ seats.add_player(1, "Player 1");
+ seats.add_player(2, "Player 2");
+ seats.add_player(3, "Player 3");
+ assert_eq!("Player 2", seats.player_after("Player 1").unwrap());
+ assert_eq!("Player 3", seats.player_after("Player 2").unwrap());
+ }
+
+ #[test]
+ fn player_after_wrap_around() {
+ let mut seats = Seats::new();
+ seats.add_player(1, "Player 1");
+ seats.add_player(5, "Player 5");
+ seats.add_player(8, "Player 8");
+ assert_eq!("Player 1", seats.player_after("Player 8").unwrap());
+ }
+
+ #[test]
+ fn player_after_two_player() {
+ let mut seats = Seats::new();
+ seats.add_player(1, "Player 1");
+ seats.add_player(4, "Player 4");
+ assert_eq!("Player 4", seats.player_after("Player 1").unwrap());
+ assert_eq!("Player 1", seats.player_after("Player 4").unwrap());
+ }
+
+ #[test]
+ fn remove_player() {
+ let mut seats = Seats::new();
+ seats.add_player(1, "Player 1");
+ seats.add_player(2, "Player 2");
+ seats.add_player(3, "Player 3");
+ seats.remove_player("Player 2");
+ assert_eq!("Player 3", seats.player_after("Player 1").unwrap());
+ assert_eq!(None, seats.player_after("Player 2"));
+ assert_eq!("Player 1", seats.player_after("Player 3").unwrap());
+ assert!(seats.seat_is_available(0));
+ assert!(!seats.seat_is_available(1));
+ assert!(seats.seat_is_available(2));
+ assert!(!seats.seat_is_available(3));
+ assert!(seats.seat_is_available(4));
+ }
+}