Compare commits

..

3 Commits

Author SHA1 Message Date
055bd860b2 WIP implementing betting logic 2024-04-27 22:02:40 -04:00
bc1a7380f0 Impliment betting pot as hashmap, add implementation 2024-04-27 14:49:58 -04:00
a9e90bc447 Add ID to player trait 2024-04-27 12:27:23 -04:00
2 changed files with 220 additions and 18 deletions

View File

@@ -1,3 +1,5 @@
use std::collections::{HashMap, VecDeque};
use crate::card::{create_deck, draw_card, PlayingCard}; use crate::card::{create_deck, draw_card, PlayingCard};
use crate::player::{self, Player}; use crate::player::{self, Player};
use rand::Rng; use rand::Rng;
@@ -10,30 +12,86 @@ enum GameStage {
} }
struct BettingPot { struct BettingPot {
players: Vec<Box<dyn Player>>, /// **Key**: Player ID
pennies: u32, ///
/// **Value**: Pennies
bets: HashMap<u32, u32>,
pot_total: u32,
/// If there are multiple pots, represents the max value of this pot.
/// Calls or raises above this go into the next pot.
pot_cap: Option<u32>,
} }
impl BettingPot { impl BettingPot {
pub fn distribute(&mut self) { pub fn new(players: Vec<u32>, starting_bets: Option<HashMap<u32, u32>>) -> BettingPot {
let player_count = self.players.len() as u32; let mut m: HashMap<u32, u32> = starting_bets.unwrap_or_default();
let split = self.pennies / player_count; for p in players {
let remainder = self.pennies % player_count; if !m.contains_key(&p) {
for p in &mut self.players { m.insert(p, 0);
}
}
BettingPot {
bets: m,
pot_total: 0,
pot_cap: None,
}
}
pub fn cap_pot(&mut self, pennies: u32) -> HashMap<u32, u32> {
self.pot_cap = Some(pennies);
let mut new_map: HashMap<u32, u32> = HashMap::new();
self.bets.iter_mut().for_each(|bet| {
if *bet.1 > pennies {
let difference = *bet.1 - pennies;
self.pot_total -= difference;
*bet.1 = pennies;
new_map.insert(*bet.0, difference);
}
});
new_map
}
pub fn declare_winner(&self, id: u32, game: &mut PokerGame) {
game.get_player_mut(id).pay(self.pot_total);
}
pub fn remove_player(&mut self, id: u32) {
self.bets.remove(&id);
}
pub fn add_bet(&mut self, id: u32, pennies: u32) {
if let Some(b) = self.bets.get_mut(&id) {
*b += pennies;
}
self.pot_total += pennies;
}
pub fn get_bet(&self, id: u32) -> Option<&u32> {
self.bets.get(&id)
}
pub fn distribute(&mut self, game: &mut PokerGame) {
let player_count = self.bets.len() as u32;
let split = self.pot_total / player_count;
let remainder = self.pot_total % player_count;
for b in &mut self.bets {
let p = game.get_player_mut(*b.0);
p.pay(split); p.pay(split);
} }
if remainder > 0 { if remainder > 0 {
let num = rand::thread_rng().gen_range(0..player_count) as usize; let num = rand::thread_rng().gen_range(0..player_count) as usize;
self.players[num].pay(remainder); let r: Vec<&u32> = self.bets.keys().collect();
let p = game.get_player_mut(*r[num]);
p.pay(split);
} }
} }
} }
pub struct PokerGame { pub struct PokerGame {
players: Vec<Box<dyn Player>>, players: Vec<Box<dyn Player>>,
pots: Vec<BettingPot>, table: Vec<PlayingCard>,
big_blind: u32, // Index of player who should post the Big Blind big_blind: u32, // Index of player who should post the Big Blind
pot: u32, // Money in pennies pots: Vec<BettingPot>,
stage: GameStage, stage: GameStage,
deck: Vec<PlayingCard>, deck: Vec<PlayingCard>,
} }
@@ -41,16 +99,123 @@ pub struct PokerGame {
impl PokerGame { impl PokerGame {
pub fn new(players: Vec<Box<dyn Player>>) -> PokerGame { pub fn new(players: Vec<Box<dyn Player>>) -> PokerGame {
let first_player = 0; let first_player = 0;
let mut p_ids: Vec<u32> = vec![];
players.iter().for_each(|p| p_ids.push(p.id()));
PokerGame { PokerGame {
players, players,
pots: vec![], table: vec![],
pots: vec![BettingPot::new(p_ids, None)],
big_blind: 0, big_blind: 0,
pot: 0,
stage: GameStage::PreFlop, stage: GameStage::PreFlop,
deck: create_deck(), deck: create_deck(),
} }
} }
fn remove_player(&mut self, id: u32) {
self.players.retain(|p| p.id() != id);
}
pub fn get_player(&self, id: u32) -> &Box<dyn Player> {
self.players
.iter()
.find(|p| p.id() == id)
.expect("No player found!")
}
pub fn get_player_mut(&mut self, id: u32) -> &mut Box<dyn Player> {
self.players
.iter_mut()
.find(|p| p.id() == id)
.expect("No player found!")
}
pub fn get_bets(&mut self) {
let ids: Vec<u32> = self.players.iter().map(|p| p.id()).collect();
// Queue of players that still need to bet. When this is empty,
// all players have placed there bets and the next round is ready to start.
let mut bet_queue: VecDeque<u32> = VecDeque::new();
self.players
.iter()
.for_each(|p| bet_queue.push_back(p.id()));
while !bet_queue.is_empty() {
let id = bet_queue.pop_front().unwrap();
let action = self.get_player(id).get_move(self);
let mut current_bet = 0;
match action {
player::TurnAction::Fold => {
// Remove player from this game
self.remove_player(id);
// Remove player from all pots
self.pots.iter_mut().for_each(|pot| pot.remove_player(id));
}
player::TurnAction::Call => {
if current_bet > 0 {
let message = format!("You called ${}.", (current_bet as f64) / 100.0);
self.get_player_mut(id).deduct(current_bet, Some(&message));
}
let mut bet = current_bet.clone();
self.pots.iter_mut().for_each(|pot| match pot.pot_cap {
Some(cap) => {
if cap > bet {
panic!("There's something wrong with the logic, bet is less than the cap??");
}
pot.add_bet(id, cap);
bet -= cap;
},
None => {
if bet > 0 {
pot.add_bet(id, bet);
}
}
});
self.pots.last_mut().unwrap().add_bet(id, current_bet);
}
player::TurnAction::AllIn(amount) => {
// Remove money from player and add it to the pot
let message = format!(
"You went all in with your last ${}.",
(current_bet as f64) / 100.0
);
self.get_player_mut(id).deduct(amount, Some(&message));
self.pots.last_mut().unwrap().add_bet(id, amount);
// Take the difference between the current bet and create a new pot
let pot_cap = *self.pots.last().unwrap().bets.get(&id).unwrap();
let new_betmap = self.pots.last_mut().unwrap().cap_pot(pot_cap);
let new_pot_players: Vec<u32> = self
.players
.iter()
.filter(|p| p.id() != id)
.map(|p| p.id())
.collect();
let new_pot = BettingPot::new(new_pot_players, Some(new_betmap));
self.pots.push(new_pot);
}
player::TurnAction::Raise(raise) => {
// New current bet
current_bet = current_bet + raise;
let message = format!("You raised ${}.", (current_bet as f64) / 100.0);
// Remove money from player
self.get_player_mut(id).deduct(current_bet, Some(&message));
// Add money to current pot
self.pots.last_mut().unwrap().add_bet(id, current_bet);
// Reset queue of players waiting to bet.
// Everyone now has to decide whether to match the raise.
self.players.iter().for_each(|p| {
if !bet_queue.contains(&p.id()) && p.id() != id {
bet_queue.push_back(p.id())
}
});
}
}
}
}
pub fn play_stage(&mut self) { pub fn play_stage(&mut self) {
match self.stage { match self.stage {
GameStage::PreFlop => self.play_preflop(), GameStage::PreFlop => self.play_preflop(),
@@ -70,17 +235,34 @@ impl PokerGame {
let dealt_cards = vec![draw_card(&mut self.deck), draw_card(&mut self.deck)]; let dealt_cards = vec![draw_card(&mut self.deck), draw_card(&mut self.deck)];
player.deal_cards(dealt_cards); player.deal_cards(dealt_cards);
} }
self.get_bets();
} }
/// Flop: /// Flop:
/// - Play three cards to the table /// - Play three cards to the table
/// - Players make a round of bets /// - Players make a round of bets
fn play_flop(&self) {} fn play_flop(&mut self) {
println!("The Flop:");
let mut flop_cards = vec![];
for _ in 0..3 {
let card = draw_card(&mut self.deck);
println!("{}", card);
flop_cards.push(card);
}
flop_cards.iter_mut().for_each(|c| self.table.push(*c));
self.get_bets();
}
/// Turn: /// Turn:
/// - Play another card to the table /// - Play another card to the table
/// - Players make a round of bets /// - Players make a round of bets
fn play_turn_or_river(&self) {} fn play_turn_or_river(&mut self) {
println!("Next up:");
let card = draw_card(&mut self.deck);
println!("{}", card);
self.table.push(card);
self.get_bets();
}
/// Reveal: /// Reveal:
/// - All players cards are revealed /// - All players cards are revealed

View File

@@ -6,10 +6,12 @@ use std::io;
pub enum TurnAction { pub enum TurnAction {
Fold, Fold,
Call, Call,
AllIn(u32),
Raise(u32), Raise(u32),
} }
pub trait Player { pub trait Player {
fn id(&self) -> u32;
fn display_name(&self) -> &str; fn display_name(&self) -> &str;
fn deal_cards(&mut self, cards: Vec<PlayingCard>); fn deal_cards(&mut self, cards: Vec<PlayingCard>);
fn get_move(&self, game_state: &PokerGame) -> TurnAction; fn get_move(&self, game_state: &PokerGame) -> TurnAction;
@@ -20,18 +22,20 @@ pub trait Player {
} }
pub struct HumanPlayer { pub struct HumanPlayer {
id: u32,
name: String, name: String,
money: u32, money: u32,
cards: Vec<PlayingCard>, cards: Vec<PlayingCard>,
} }
impl HumanPlayer { impl HumanPlayer {
pub fn new(money: u32) -> HumanPlayer { pub fn new(money: u32, id: u32) -> HumanPlayer {
let mut user_input = String::new(); let mut user_input = String::new();
print!("Enter your name\n> "); print!("Enter your name\n> ");
let stdin = io::stdin(); let stdin = io::stdin();
let _ = stdin.read_line(&mut user_input); let _ = stdin.read_line(&mut user_input);
HumanPlayer { HumanPlayer {
id,
name: user_input, name: user_input,
money, money,
cards: vec![], cards: vec![],
@@ -40,6 +44,10 @@ impl HumanPlayer {
} }
impl Player for HumanPlayer { impl Player for HumanPlayer {
fn id(&self) -> u32 {
self.id
}
fn display_name(&self) -> &str { fn display_name(&self) -> &str {
&self.name &self.name
} }
@@ -133,14 +141,16 @@ impl HumanPlayer {
} }
struct RemotePlayer { struct RemotePlayer {
id: u32,
name: String, name: String,
money: u32, money: u32,
cards: Vec<PlayingCard>, cards: Vec<PlayingCard>,
} }
impl RemotePlayer { impl RemotePlayer {
pub fn new(name: &str, money: u32) -> RemotePlayer { pub fn new(name: &str, money: u32, id: u32) -> RemotePlayer {
RemotePlayer { RemotePlayer {
id,
name: name.to_string(), name: name.to_string(),
money, money,
cards: vec![], cards: vec![],
@@ -149,6 +159,10 @@ impl RemotePlayer {
} }
impl Player for RemotePlayer { impl Player for RemotePlayer {
fn id(&self) -> u32 {
self.id
}
fn display_name(&self) -> &str { fn display_name(&self) -> &str {
&self.name &self.name
} }
@@ -186,14 +200,16 @@ impl Player for RemotePlayer {
} }
struct BotPlayer { struct BotPlayer {
id: u32,
name: String, name: String,
money: u32, money: u32,
cards: Vec<PlayingCard>, cards: Vec<PlayingCard>,
} }
impl BotPlayer { impl BotPlayer {
pub fn new(name: &str, money: u32) -> BotPlayer { pub fn new(name: &str, money: u32, id: u32) -> BotPlayer {
BotPlayer { BotPlayer {
id,
name: format!("[BOT] {}", name.to_string()), name: format!("[BOT] {}", name.to_string()),
money, money,
cards: vec![], cards: vec![],
@@ -202,6 +218,10 @@ impl BotPlayer {
} }
impl Player for BotPlayer { impl Player for BotPlayer {
fn id(&self) -> u32 {
self.id
}
fn display_name(&self) -> &str { fn display_name(&self) -> &str {
&self.name &self.name
} }