Compare commits

..

4 Commits

Author SHA1 Message Date
899edd7c7f Rewriting things based on traits! 2024-04-22 00:12:57 -04:00
6ab8839d6c wip 2024-04-19 20:04:58 -04:00
ca47f2df2b Add more tests 2024-03-23 23:31:55 -04:00
30a2280268 Write tests for hand_type and correct exposed bugs 2024-03-23 18:28:04 -04:00
6 changed files with 784 additions and 11 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"rust-analyzer.showUnlinkedFileNotification": false
}

View File

@@ -26,7 +26,7 @@ pub enum CardValue {
Ace, Ace,
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Clone, Copy)]
pub struct PlayingCard { pub struct PlayingCard {
pub value: CardValue, pub value: CardValue,
pub suit: CardSuit, pub suit: CardSuit,

90
src/game.rs Normal file
View File

@@ -0,0 +1,90 @@
use crate::card::{create_deck, draw_card, PlayingCard};
use crate::player::{self, Player};
use rand::Rng;
enum GameStage {
PreFlop,
Flop,
Turn,
River,
Reveal,
}
struct BettingPot {
players: Vec<Box<dyn Player>>,
pennies: u32,
}
impl BettingPot {
pub fn distribute(&mut self) {
let player_count = self.players.len() as u32;
let split = self.pennies / player_count;
let remainder = self.pennies % player_count;
for p in &mut self.players {
p.pay(split);
}
if remainder > 0 {
let num = rand::thread_rng().gen_range(0..player_count) as usize;
self.players[num].pay(remainder);
}
}
}
pub struct PokerGame {
players: Vec<Box<dyn Player>>,
pots: Vec<BettingPot>,
big_blind: u32, // Index of player who should post the Big Blind
pot: u32, // Money in pennies
stage: GameStage,
deck: Vec<PlayingCard>,
}
impl PokerGame {
pub fn new(players: Vec<Box<dyn Player>>) -> PokerGame {
let first_player = 0;
PokerGame {
players,
pots: vec![],
big_blind: 0,
pot: 0,
stage: GameStage::PreFlop,
deck: create_deck(),
}
}
pub fn play_stage(&mut self) {
match self.stage {
GameStage::PreFlop => self.play_preflop(),
GameStage::Flop => self.play_flop(),
GameStage::Turn => self.play_turn_or_river(),
GameStage::River => self.play_turn_or_river(),
GameStage::Reveal => self.play_reveal(),
}
}
/// Preflop:
/// - Deal cards to all of the players
/// - Collect big and small blinds
/// - Players make their first round of actions
fn play_preflop(&mut self) {
for player in &mut self.players {
let dealt_cards = vec![draw_card(&mut self.deck), draw_card(&mut self.deck)];
player.deal_cards(dealt_cards);
}
}
/// Flop:
/// - Play three cards to the table
/// - Players make a round of bets
fn play_flop(&self) {}
/// Turn:
/// - Play another card to the table
/// - Players make a round of bets
fn play_turn_or_river(&self) {}
/// Reveal:
/// - All players cards are revealed
/// - Hands are compared, winner is decided
/// - Payout is presented
fn play_reveal(&self) {}
}

View File

@@ -2,7 +2,7 @@ use crate::card::{CardValue, PlayingCard};
use core::fmt; use core::fmt;
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] #[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)]
pub enum HandType { pub enum HandType {
HighCard, HighCard,
OnePair, OnePair,
@@ -16,7 +16,7 @@ pub enum HandType {
} }
#[derive(PartialEq)] #[derive(PartialEq)]
struct PokerHand { pub struct PokerHand {
cards: Vec<PlayingCard>, cards: Vec<PlayingCard>,
} }
@@ -47,7 +47,7 @@ impl fmt::Display for PokerHand {
impl PartialOrd for PokerHand { impl PartialOrd for PokerHand {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if self.hand_type() > other.hand_type() { if self.hand_type() != other.hand_type() {
return self.hand_type().partial_cmp(&other.hand_type()); return self.hand_type().partial_cmp(&other.hand_type());
} }
let selfmap = self.hash_map(); let selfmap = self.hash_map();
@@ -143,7 +143,7 @@ impl PartialOrd for PokerHand {
} }
impl PokerHand { impl PokerHand {
fn new(mut cards: Vec<PlayingCard>) -> Self { pub fn new(mut cards: Vec<PlayingCard>) -> Self {
assert!(cards.len() == 5); assert!(cards.len() == 5);
// Sorted highest card first // Sorted highest card first
@@ -172,7 +172,7 @@ impl PokerHand {
count count
} }
fn hand_type(&self) -> HandType { pub fn hand_type(&self) -> HandType {
if is_straight_flush(self) { if is_straight_flush(self) {
return HandType::StraightFlush; return HandType::StraightFlush;
} }
@@ -214,8 +214,8 @@ fn is_straight_flush(hand: &PokerHand) -> bool {
assert!(hand.len() == 5); assert!(hand.len() == 5);
let suit = hand[0].suit; let suit = hand[0].suit;
let mut min_val = CardValue::Two; let mut min_val = CardValue::Ace;
let mut max_val = CardValue::Ace; let mut max_val = CardValue::Two;
// Must all be the same suit // Must all be the same suit
for i in 0..5 { for i in 0..5 {
@@ -294,7 +294,7 @@ fn is_flush(hand: &PokerHand) -> bool {
fn is_straight(hand: &PokerHand) -> bool { fn is_straight(hand: &PokerHand) -> bool {
for i in 1..4 { for i in 1..4 {
let current_card = hand.cards[i].value as u8; let current_card = hand.cards[i].value as u8;
let previous_card = hand.cards[i].value as u8; let previous_card = hand.cards[i - 1].value as u8;
if current_card != previous_card - 1 { if current_card != previous_card - 1 {
return false; return false;
} }
@@ -325,3 +325,422 @@ fn is_one_pair(hand: &PokerHand) -> bool {
// https://stackoverflow.com/questions/45353757/how-to-count-the-elements-in-a-vector-with-some-value-without-looping // https://stackoverflow.com/questions/45353757/how-to-count-the-elements-in-a-vector-with-some-value-without-looping
count.iter().filter(|&n| *n == 2).count() == 1 count.iter().filter(|&n| *n == 2).count() == 1
} }
#[cfg(test)]
mod tests {
use crate::card::CardSuit;
use super::*;
#[test]
fn one_pair_test() {
let card1 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Ace,
};
let card2 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Ace,
};
let card3 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Two,
};
let card4 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Five,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
let pair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(pair);
assert_eq!(hand.hand_type(), HandType::OnePair);
let card1 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Ace,
};
let card2 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::King,
};
let card3 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Two,
};
let card4 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Five,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
let nopair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(nopair);
assert_eq!(hand.hand_type(), HandType::HighCard);
}
#[test]
fn two_pair_test() {
let card1 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Ace,
};
let card2 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Ace,
};
let card3 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Two,
};
let card4 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Five,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Five,
};
let pair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(pair);
assert_eq!(hand.hand_type(), HandType::TwoPair);
}
#[test]
fn three_of_a_kind_tests() {
let card1 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Ace,
};
let card2 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Ace,
};
let card3 = PlayingCard {
suit: CardSuit::Spades,
value: CardValue::Ace,
};
let card4 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Five,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Two,
};
let pair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(pair);
assert_eq!(hand.hand_type(), HandType::ThreeOfAKind);
}
#[test]
fn staight_test() {
let card1 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Six,
};
let card2 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
let card3 = PlayingCard {
suit: CardSuit::Spades,
value: CardValue::Seven,
};
let card4 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Five,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Nine,
};
let pair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(pair);
assert_eq!(hand.hand_type(), HandType::Straight);
}
#[test]
fn flush_test() {
let card1 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Six,
};
let card2 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Two,
};
let card3 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Queen,
};
let card4 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Jack,
};
let card5 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Ace,
};
let pair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(pair);
assert_eq!(hand.hand_type(), HandType::Flush);
}
#[test]
fn full_house_test() {
let card1 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Six,
};
let card2 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Six,
};
let card3 = PlayingCard {
suit: CardSuit::Spades,
value: CardValue::Two,
};
let card4 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Six,
};
let card5 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Two,
};
let pair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(pair);
assert_eq!(hand.hand_type(), HandType::FullHouse);
}
#[test]
fn four_of_a_kind_test() {
let card1 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Six,
};
let card2 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Six,
};
let card3 = PlayingCard {
suit: CardSuit::Spades,
value: CardValue::Six,
};
let card4 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Six,
};
let card5 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Two,
};
let pair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(pair);
assert_eq!(hand.hand_type(), HandType::FourOfAKind);
}
#[test]
fn straight_flush_test() {
let card1 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Jack,
};
let card2 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Ten,
};
let card3 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Nine,
};
let card4 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Seven,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
let pair = vec![card1, card2, card3, card4, card5];
let hand = PokerHand::new(pair);
assert_eq!(hand.hand_type(), HandType::StraightFlush);
}
#[test]
fn comp_test1() {
// Hand 1
let card1 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Jack,
};
let card2 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Jack,
};
let card3 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Nine,
};
let card4 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Ace,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
// Hand 2
let card6 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Queen,
};
let card7 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Ten,
};
let card8 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Queen,
};
let card9 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Two,
};
let card10 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
let cardset1 = vec![card1, card2, card3, card4, card5];
let cardset2 = vec![card6, card7, card8, card9, card10];
let flush = PokerHand::new(cardset1);
let onepair = PokerHand::new(cardset2);
assert!(flush > onepair);
}
#[test]
fn straight_flush_ord_test() {
// Hand 1
let card1 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Jack,
};
let card2 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Ten,
};
let card3 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Nine,
};
let card4 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Seven,
};
// Hand 2
let card6 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Queen,
};
let card7 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Jack,
};
let card8 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Ten,
};
let card9 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Nine,
};
let card10 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
let cardset1 = vec![card1, card2, card3, card4, card5];
let cardset2 = vec![card6, card7, card8, card9, card10];
let hand1 = PokerHand::new(cardset1);
let hand2 = PokerHand::new(cardset2);
assert!(hand1 < hand2);
assert!(hand1 >= hand1);
assert!(hand2 <= hand2);
}
#[test]
fn four_kind_ord_test() {
// Hand 1
let card1 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Jack,
};
let card2 = PlayingCard {
suit: CardSuit::Hearts,
value: CardValue::Jack,
};
let card3 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Jack,
};
let card4 = PlayingCard {
suit: CardSuit::Spades,
value: CardValue::Jack,
};
let card5 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Seven,
};
// Hand 2
let card6 = PlayingCard {
suit: CardSuit::Spades,
value: CardValue::Queen,
};
let card7 = PlayingCard {
suit: CardSuit::Hearts,
value: CardValue::Queen,
};
let card8 = PlayingCard {
suit: CardSuit::Clubs,
value: CardValue::Queen,
};
let card9 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Queen,
};
let card10 = PlayingCard {
suit: CardSuit::Diamonds,
value: CardValue::Eight,
};
let cardset1 = vec![card1, card2, card3, card4, card5];
let cardset2 = vec![card6, card7, card8, card9, card10];
let hand1 = PokerHand::new(cardset1);
let hand2 = PokerHand::new(cardset2);
assert!(hand1 < hand2);
assert!(hand1 >= hand1);
assert!(hand2 <= hand2);
}
}

View File

@@ -1,15 +1,17 @@
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_variables)]
use std::vec; use std::vec;
mod card; mod card;
mod game;
mod hand; mod hand;
mod player;
use crate::card::{create_deck, CardSuit, CardValue, PlayingCard}; use crate::card::{create_deck, CardSuit, CardValue, PlayingCard};
use crate::hand::HandType; use crate::hand::HandType;
fn main() { fn main() {
let mut deck = create_deck(); let deck = create_deck();
let card1: PlayingCard = PlayingCard { let card1: PlayingCard = PlayingCard {
suit: CardSuit::Clubs, suit: CardSuit::Clubs,

259
src/player.rs Normal file
View File

@@ -0,0 +1,259 @@
use crate::card::PlayingCard;
use crate::game::PokerGame;
use crate::hand::PokerHand;
use std::io;
pub enum TurnAction {
Fold,
Call,
Raise(u32),
}
pub trait Player {
fn display_name(&self) -> &str;
fn deal_cards(&mut self, cards: Vec<PlayingCard>);
fn get_move(&self, game_state: &PokerGame) -> TurnAction;
fn money(&self) -> u32;
fn pay(&mut self, pennies: u32);
fn deduct(&mut self, pennies: u32, message: Option<&str>);
fn get_hand(&self, table: Vec<PlayingCard>) -> PokerHand;
}
pub struct HumanPlayer {
name: String,
money: u32,
cards: Vec<PlayingCard>,
}
impl HumanPlayer {
pub fn new(money: u32) -> HumanPlayer {
let mut user_input = String::new();
print!("Enter your name\n> ");
let stdin = io::stdin();
let _ = stdin.read_line(&mut user_input);
HumanPlayer {
name: user_input,
money,
cards: vec![],
}
}
}
impl Player for HumanPlayer {
fn display_name(&self) -> &str {
&self.name
}
fn deal_cards(&mut self, mut cards: Vec<PlayingCard>) {
self.cards.clear();
self.cards.append(&mut cards);
}
fn get_move(&self, game_state: &PokerGame) -> TurnAction {
let mut user_input = String::new();
let stdin = io::stdin();
loop {
print!("What would you like to do?\nR: Raise\nC: Call\n:F\n Fold\n> ");
let _ = stdin.read_line(&mut user_input);
if user_input.len() == 0 {
continue;
}
let first_char = user_input.to_lowercase().chars().next().unwrap();
match first_char {
'f' => {
return TurnAction::Fold;
}
'r' => {
return TurnAction::Raise(self.get_raise());
}
'c' => {
return TurnAction::Call;
}
_ => {
continue;
}
}
}
}
fn money(&self) -> u32 {
self.money
}
fn pay(&mut self, pennies: u32) {
self.money = self.money + pennies;
println!("You received ${}.", (pennies as f64) / 100.0);
}
fn deduct(&mut self, pennies: u32, message: Option<&str>) {
if pennies > self.money {
panic!("Deducted more money than the player had!");
}
self.money = self.money - pennies;
if message.is_some() {
println!("{}", message.unwrap().to_string());
}
}
fn get_hand(&self, table: Vec<PlayingCard>) -> PokerHand {
let mut table_clone = table.clone();
let mut total_cards = self.cards.clone();
total_cards.append(&mut table_clone);
PokerHand::new(total_cards)
}
}
impl HumanPlayer {
fn get_raise(&self) -> u32 {
let mut user_input: String = String::new();
let stdin = io::stdin();
loop {
print!("Enter your raise.\n> ");
let _ = stdin.read_line(&mut user_input);
let user_input = user_input.replace("$", "");
if user_input.len() == 0 {
continue;
}
let money = user_input.parse::<f64>();
match money {
Ok(_) => {
let pennies = (money.unwrap() * 100.0).floor() as u32;
if pennies > self.money {
println!("You flat-footed dingus, you don't have that much money!");
}
return pennies;
}
Err(err) => {
print!("That's not a number, dufus. What, are you trying to bet your bitcoin wallet or something? Enter a number.\n>");
continue;
}
}
}
}
}
struct RemotePlayer {
name: String,
money: u32,
cards: Vec<PlayingCard>,
}
impl RemotePlayer {
pub fn new(name: &str, money: u32) -> RemotePlayer {
RemotePlayer {
name: name.to_string(),
money,
cards: vec![],
}
}
}
impl Player for RemotePlayer {
fn display_name(&self) -> &str {
&self.name
}
fn deal_cards(&mut self, mut cards: Vec<PlayingCard>) {
self.cards.clear();
self.cards.append(&mut cards);
}
fn get_move(&self, game_state: &PokerGame) -> TurnAction {
todo!()
}
fn money(&self) -> u32 {
self.money
}
fn pay(&mut self, pennies: u32) {
self.money = self.money + pennies;
}
fn deduct(&mut self, pennies: u32, message: Option<&str>) {
if pennies > self.money {
panic!("Deducted more money than the player had!");
}
self.money = self.money - pennies;
}
fn get_hand(&self, table: Vec<PlayingCard>) -> PokerHand {
let mut table_clone = table.clone();
let mut total_cards = self.cards.clone();
total_cards.append(&mut table_clone);
PokerHand::new(total_cards)
}
}
struct BotPlayer {
name: String,
money: u32,
cards: Vec<PlayingCard>,
}
impl BotPlayer {
pub fn new(name: &str, money: u32) -> BotPlayer {
BotPlayer {
name: format!("[BOT] {}", name.to_string()),
money,
cards: vec![],
}
}
}
impl Player for BotPlayer {
fn display_name(&self) -> &str {
&self.name
}
fn deal_cards(&mut self, mut cards: Vec<PlayingCard>) {
self.cards.clear();
self.cards.append(&mut cards);
}
fn get_move(&self, game_state: &PokerGame) -> TurnAction {
return TurnAction::Fold;
}
fn money(&self) -> u32 {
self.money
}
fn pay(&mut self, pennies: u32) {
self.money = self.money + pennies;
}
fn deduct(&mut self, pennies: u32, message: Option<&str>) {
if pennies > self.money {
panic!("Deducted more money than the player had!");
}
self.money = self.money - pennies;
}
fn get_hand(&self, table: Vec<PlayingCard>) -> PokerHand {
let mut table_clone = table.clone();
let mut total_cards = self.cards.clone();
total_cards.append(&mut table_clone);
PokerHand::new(total_cards)
}
}
// Ling Lallister
// Bao Amdahl
// Troy Cyberson
// Turbo Pocket
// Frances Netwall
// Nate Packer
// Lela Bacon
// Hinge Romton
// Deming Stellis
// Ryker Dedex
// Lillix Borg
// Alan Onishi
// Leeloo Phiser
// Cloud Sine
// Han Onishi
// Magda Hertz
// Akira Gallister
// Groat
// Tim