finish 54 :)
This commit is contained in:
@@ -6,3 +6,4 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rstest = "*"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,116 @@
|
|||||||
|
use crate::suit::Suit;
|
||||||
|
use crate::value::{Value, Values};
|
||||||
|
use std::cmp::max;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
pub type Cards = [Card; 5];
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)]
|
||||||
|
pub struct Card {
|
||||||
|
pub value: Value,
|
||||||
|
pub suit: Suit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Card {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
assert_eq!(s.len(), 2);
|
||||||
|
let mut chars = s.chars();
|
||||||
|
let value = Value::from(chars.next().unwrap());
|
||||||
|
let suit = Suit::from(chars.next().unwrap());
|
||||||
|
Self { value, suit }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Contains<T> {
|
||||||
|
fn contains_value(&self, x: &T) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Contains<Value> for &Cards {
|
||||||
|
fn contains_value(&self, x: &Value) -> bool {
|
||||||
|
for card in self.iter() {
|
||||||
|
if card.value == *x {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CardTrait {
|
||||||
|
fn have_matching_suits(&self) -> bool;
|
||||||
|
fn contain_all_values_of(&self, values: &Values) -> bool;
|
||||||
|
fn get_highest_card(&self) -> Card;
|
||||||
|
fn have_consecutive_values(&self) -> bool;
|
||||||
|
fn to_value_hashset(&self) -> HashSet<Value>;
|
||||||
|
fn to_value_hashmap(&self) -> HashMap<Value, usize>;
|
||||||
|
fn to_suit_hashset(&self) -> HashSet<Suit>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CardTrait for Cards {
|
||||||
|
fn have_matching_suits(&self) -> bool {
|
||||||
|
self[1..]
|
||||||
|
.iter()
|
||||||
|
.zip(self[..4].iter())
|
||||||
|
.all(|(c1, c2)| c1.suit == c2.suit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contain_all_values_of(&self, values: &Values) -> bool {
|
||||||
|
values.iter().all(|v| self.contains_value(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_highest_card(&self) -> Card {
|
||||||
|
*self.iter().reduce(|accum, item| max(accum, item)).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn have_consecutive_values(&self) -> bool {
|
||||||
|
let mut count = 0;
|
||||||
|
for value in Value::iterator() {
|
||||||
|
if self.contains_value(value) {
|
||||||
|
count += 1;
|
||||||
|
if count == 5 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if count > 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_suit_hashset(&self) -> HashSet<Suit> {
|
||||||
|
self.iter().map(|c| c.suit).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value_hashset(&self) -> HashSet<Value> {
|
||||||
|
self.iter().map(|c| c.value).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value_hashmap(&self) -> HashMap<Value, usize> {
|
||||||
|
let mut hm = HashMap::new();
|
||||||
|
for value in self.to_value_hashset() {
|
||||||
|
hm.insert(value, 0);
|
||||||
|
}
|
||||||
|
for card in self {
|
||||||
|
*hm.get_mut(&card.value).unwrap() += 1;
|
||||||
|
}
|
||||||
|
hm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
// use super::Card;
|
||||||
|
use crate::hand::Hand;
|
||||||
|
|
||||||
|
use super::CardTrait;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_consecutive_values() {
|
||||||
|
let hand = Hand::from("2H 3C 4D 5D 6S");
|
||||||
|
assert!(hand.cards.have_consecutive_values());
|
||||||
|
let hand = Hand::from("2H 4D 3C 5D 6S");
|
||||||
|
assert!(hand.cards.have_consecutive_values());
|
||||||
|
let hand = Hand::from("2H 4D 8C 5D 6S");
|
||||||
|
assert!(!hand.cards.have_consecutive_values());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
use crate::card::{CardTrait, Cards};
|
||||||
|
use crate::value::Value;
|
||||||
|
|
||||||
|
impl From<&Cards> for Evaluation {
|
||||||
|
fn from(cards: &Cards) -> Self {
|
||||||
|
if let Some(value) = Self::is_royal_flush(cards) {
|
||||||
|
Self::RoyalFlush(value)
|
||||||
|
} else if let Some(value) = Self::is_straight_flush(cards) {
|
||||||
|
Self::StraightFlush(value)
|
||||||
|
} else if let Some(value) = Self::is_four_of_a_kind(cards) {
|
||||||
|
Self::FourOfAKind(value)
|
||||||
|
} else if let Some(value) = Self::is_full_house(cards) {
|
||||||
|
Self::FullHouse(value)
|
||||||
|
} else if let Some(value) = Self::is_flush(cards) {
|
||||||
|
Self::Flush(value)
|
||||||
|
} else if let Some(value) = Self::is_straight(cards) {
|
||||||
|
Self::Straight(value)
|
||||||
|
} else if let Some(value) = Self::is_three_of_a_kind(cards) {
|
||||||
|
Self::ThreeOfAKind(value)
|
||||||
|
} else if let Some(value) = Self::is_two_pairs(cards) {
|
||||||
|
Self::TwoPairs(value)
|
||||||
|
} else if let Some(value) = Self::is_one_pair(cards) {
|
||||||
|
Self::OnePair(value)
|
||||||
|
} else {
|
||||||
|
Self::High(cards.get_highest_card().value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Evaluation {
|
||||||
|
fn is_straight_flush(cards: &Cards) -> Option<Value> {
|
||||||
|
if cards.have_matching_suits() && cards.have_consecutive_values() {
|
||||||
|
Some(cards.get_highest_card().value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_royal_flush(cards: &Cards) -> Option<Value> {
|
||||||
|
if cards.have_matching_suits() && cards.contain_all_values_of(&ROYAL_FLUSH) {
|
||||||
|
Some(cards.get_highest_card().value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_four_of_a_kind(cards: &Cards) -> Option<Value> {
|
||||||
|
cards
|
||||||
|
.to_value_hashmap()
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, count)| **count == 4)
|
||||||
|
.map(|(value, _)| *value)
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_full_house(cards: &Cards) -> Option<Value> {
|
||||||
|
if cards
|
||||||
|
.to_value_hashmap()
|
||||||
|
.values()
|
||||||
|
.filter(|v| [2, 3].contains(v))
|
||||||
|
.count()
|
||||||
|
== 2
|
||||||
|
{
|
||||||
|
Self::is_three_of_a_kind(cards)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_flush(cards: &Cards) -> Option<Value> {
|
||||||
|
if cards.have_matching_suits() {
|
||||||
|
Some(cards.get_highest_card().value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_straight(cards: &Cards) -> Option<Value> {
|
||||||
|
if cards.have_consecutive_values() {
|
||||||
|
Some(cards.get_highest_card().value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_three_of_a_kind(cards: &Cards) -> Option<Value> {
|
||||||
|
cards
|
||||||
|
.to_value_hashmap()
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, count)| **count == 3)
|
||||||
|
.map(|(value, _)| *value)
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_two_pairs(cards: &Cards) -> Option<Value> {
|
||||||
|
let hm = cards.to_value_hashmap();
|
||||||
|
let values: Vec<_> = hm
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, count)| **count == 2)
|
||||||
|
.map(|(value, _)| value)
|
||||||
|
.collect();
|
||||||
|
if values.len() != 2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(std::cmp::max(*values[0], *values[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_one_pair(cards: &Cards) -> Option<Value> {
|
||||||
|
let hm = cards.to_value_hashmap();
|
||||||
|
if hm.len() == 4 {
|
||||||
|
hm.iter()
|
||||||
|
.filter(|(_, count)| **count == 2)
|
||||||
|
.map(|(value, _)| *value)
|
||||||
|
.next()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ROYAL_FLUSH: [Value; 5] = [
|
||||||
|
Value::Ten,
|
||||||
|
Value::Jack,
|
||||||
|
Value::Queen,
|
||||||
|
Value::King,
|
||||||
|
Value::Ace,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(PartialEq, PartialOrd, Debug)]
|
||||||
|
pub enum Evaluation {
|
||||||
|
High(Value),
|
||||||
|
OnePair(Value),
|
||||||
|
TwoPairs(Value),
|
||||||
|
ThreeOfAKind(Value),
|
||||||
|
Straight(Value),
|
||||||
|
Flush(Value),
|
||||||
|
FullHouse(Value),
|
||||||
|
FourOfAKind(Value),
|
||||||
|
StraightFlush(Value),
|
||||||
|
RoyalFlush(Value),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Evaluation as Eval;
|
||||||
|
use crate::hand::Hand;
|
||||||
|
#[test]
|
||||||
|
fn test_royal_flush() {
|
||||||
|
let hand = Hand::from("3D 6D 7D TD QD");
|
||||||
|
assert!(Eval::is_royal_flush(&hand.cards).is_none())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
use crate::{card::CardTrait, hand::Hand};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Player {
|
||||||
|
Player1,
|
||||||
|
Player2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Game {
|
||||||
|
pub player1: Hand,
|
||||||
|
pub player2: Hand,
|
||||||
|
pub winner: Player,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Game {
|
||||||
|
fn from(line: &str) -> Self {
|
||||||
|
assert_eq!(line.len(), 29);
|
||||||
|
let (p1, p2) = Self::divide_into_two_hands(line);
|
||||||
|
let player1 = Hand::from(p1);
|
||||||
|
let player2 = Hand::from(p2);
|
||||||
|
let winner = if player1.evaluation == player2.evaluation {
|
||||||
|
if player1.cards.get_highest_card() > player2.cards.get_highest_card() {
|
||||||
|
Player::Player1
|
||||||
|
} else {
|
||||||
|
Player::Player2
|
||||||
|
}
|
||||||
|
} else if player1.evaluation > player2.evaluation {
|
||||||
|
Player::Player1
|
||||||
|
} else {
|
||||||
|
Player::Player2
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
player1,
|
||||||
|
player2,
|
||||||
|
winner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Game {
|
||||||
|
fn divide_into_two_hands(line: &str) -> (&str, &str) {
|
||||||
|
let (p1, p2) = line.split_at(15);
|
||||||
|
(&p1[..14], p2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::game::{Game, Player as P};
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case("5H 5C 6S 7S KD 2C 3S 8S 8D TD", P::Player2)]
|
||||||
|
#[case("5D 8C 9S JS AC 2C 5C 7D 8S QH", P::Player1)]
|
||||||
|
#[case("2D 9C AS AH AC 3D 6D 7D TD QD", P::Player2)]
|
||||||
|
#[case("4D 6S 9H QH QC 3D 6D 7H QD QS", P::Player1)]
|
||||||
|
#[case("2H 2D 4C 4D 4S 3C 3D 3S 9S 9D", P::Player1)]
|
||||||
|
fn run_example(#[case] line: &str, #[case] winner: P) {
|
||||||
|
let game = Game::from(line);
|
||||||
|
assert_eq!(
|
||||||
|
game.winner, winner,
|
||||||
|
"player 1: {:#?}\nplayer 2: {:#?}",
|
||||||
|
game.player1.evaluation, game.player2.evaluation
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use crate::card::{Card, Cards};
|
||||||
|
use crate::evaluation::Evaluation;
|
||||||
|
|
||||||
|
pub struct Hand {
|
||||||
|
pub cards: Cards,
|
||||||
|
pub evaluation: Evaluation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Hand {
|
||||||
|
fn from(line: &str) -> Self {
|
||||||
|
assert_eq!(line.len(), 14);
|
||||||
|
let vec: Vec<_> = line.split_whitespace().map(Card::from).collect();
|
||||||
|
let cards: [Card; 5] = vec.try_into().unwrap_or_else(|_| panic!());
|
||||||
|
let evaluation = Evaluation::from(&cards);
|
||||||
|
Self { cards, evaluation }
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-17
@@ -1,5 +1,7 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
mod card;
|
mod card;
|
||||||
mod evaluation;
|
mod evaluation;
|
||||||
mod game;
|
mod game;
|
||||||
@@ -8,22 +10,12 @@ mod suit;
|
|||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
let mut p1_wins = 0;
|
||||||
}
|
let buffer = BufReader::new(std::fs::File::open("poker.txt").unwrap());
|
||||||
#[cfg(test)]
|
for line in buffer.lines() {
|
||||||
mod test {
|
if game::Game::from(line.unwrap().as_ref()).winner == game::Player::Player1 {
|
||||||
use crate::game::{Game, Player as P};
|
p1_wins += 1;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn example1() {
|
|
||||||
let line = "5H 5C 6S 7S KD 2C 3S 8S 8D TD";
|
|
||||||
let game = Game::from(line);
|
|
||||||
assert_eq!(
|
|
||||||
game.winner,
|
|
||||||
P::Player2,
|
|
||||||
"player 1: {:#?}\nplayer 2: {:#?}",
|
|
||||||
game.player1.evaluation,
|
|
||||||
game.player2.evaluation
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
println!("{p1_wins}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord, Copy)]
|
||||||
|
pub enum Suit {
|
||||||
|
Diamonds,
|
||||||
|
Hearts,
|
||||||
|
Spades,
|
||||||
|
Clubs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<char> for Suit {
|
||||||
|
fn from(c: char) -> Self {
|
||||||
|
match c {
|
||||||
|
'D' => Self::Diamonds,
|
||||||
|
'H' => Self::Hearts,
|
||||||
|
'S' => Self::Spades,
|
||||||
|
'C' => Self::Clubs,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
use std::slice::Iter;
|
||||||
|
|
||||||
|
pub type Values = [Value; 5];
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Hash, Copy)]
|
||||||
|
pub enum Value {
|
||||||
|
Two,
|
||||||
|
Three,
|
||||||
|
Four,
|
||||||
|
Five,
|
||||||
|
Six,
|
||||||
|
Seven,
|
||||||
|
Eight,
|
||||||
|
Nine,
|
||||||
|
Ten,
|
||||||
|
Jack,
|
||||||
|
Queen,
|
||||||
|
King,
|
||||||
|
Ace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<char> for Value {
|
||||||
|
fn from(c: char) -> Self {
|
||||||
|
match c {
|
||||||
|
'2' => Self::Two,
|
||||||
|
'3' => Self::Three,
|
||||||
|
'4' => Self::Four,
|
||||||
|
'5' => Self::Five,
|
||||||
|
'6' => Self::Six,
|
||||||
|
'7' => Self::Seven,
|
||||||
|
'8' => Self::Eight,
|
||||||
|
'9' => Self::Nine,
|
||||||
|
'T' => Self::Ten,
|
||||||
|
'J' => Self::Jack,
|
||||||
|
'Q' => Self::Queen,
|
||||||
|
'K' => Self::King,
|
||||||
|
'A' => Self::Ace,
|
||||||
|
_ => panic!("{c}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn iterator() -> Iter<'static, Value> {
|
||||||
|
VALUES.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUES: [Value; 13] = [
|
||||||
|
Value::Two,
|
||||||
|
Value::Three,
|
||||||
|
Value::Four,
|
||||||
|
Value::Five,
|
||||||
|
Value::Six,
|
||||||
|
Value::Seven,
|
||||||
|
Value::Eight,
|
||||||
|
Value::Nine,
|
||||||
|
Value::Ten,
|
||||||
|
Value::Jack,
|
||||||
|
Value::Queen,
|
||||||
|
Value::King,
|
||||||
|
Value::Ace,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Value;
|
||||||
|
#[test]
|
||||||
|
fn test_order() {
|
||||||
|
let mut c = vec![Value::from('8'), Value::from('7')];
|
||||||
|
c.sort();
|
||||||
|
assert_eq!(c, vec![Value::from('7'), Value::from('8')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user