From 06f722ba7f6007198b35b2561e3cc5e9a9eaac3b Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Thu, 18 Aug 2022 18:01:42 +0200 Subject: [PATCH] add 54 benchmarks, small performance improvement --- src/euler54/Cargo.toml | 18 ++++++ src/euler54/benches/benchmark.rs | 29 ++++++++++ src/euler54/src/card.rs | 98 +++++++++++++++++++++----------- src/euler54/src/evaluation.rs | 19 ++++--- src/euler54/src/game.rs | 2 +- src/euler54/src/hand.rs | 5 +- src/euler54/src/lib.rs | 6 ++ src/euler54/src/main.rs | 9 +-- 8 files changed, 132 insertions(+), 54 deletions(-) create mode 100644 src/euler54/benches/benchmark.rs create mode 100644 src/euler54/src/lib.rs diff --git a/src/euler54/Cargo.toml b/src/euler54/Cargo.toml index 4fdbd22..bda6e5d 100644 --- a/src/euler54/Cargo.toml +++ b/src/euler54/Cargo.toml @@ -3,7 +3,25 @@ name = "euler54" version = "0.1.0" edition = "2021" +[lib] +name = "poker" +path = "src/lib.rs" + +[[bin]] +name = "euler54" +path = "src/main.rs" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] rstest = "*" + +[dev-dependencies] +criterion = "*" + +[profile.release] +debug = true + +[[bench]] +name = "benchmark" +harness = false diff --git a/src/euler54/benches/benchmark.rs b/src/euler54/benches/benchmark.rs new file mode 100644 index 0000000..ae7ebbc --- /dev/null +++ b/src/euler54/benches/benchmark.rs @@ -0,0 +1,29 @@ +use std::io::BufRead; +use std::io::BufReader; + +use criterion::{criterion_group, criterion_main, Criterion}; + +use poker::game::Game; + +fn bench_game(c: &mut Criterion) { + c.bench_function("example game", |b| { + b.iter(|| Game::from("5H 5C 6S 7S KD 2C 3S 8S 8D TD")) + }); +} + +fn bench_all(c: &mut Criterion) { + c.bench_function("all games", |b| { + let content: Vec<_> = BufReader::new(std::fs::File::open("poker.txt").unwrap()) + .lines() + .map(|l| l.unwrap()) + .collect(); + b.iter(|| { + for line in &content { + let _ = Game::from(line.as_ref()); + } + }) + }); +} + +criterion_group!(benches, bench_game, bench_all); +criterion_main!(benches); diff --git a/src/euler54/src/card.rs b/src/euler54/src/card.rs index d82c2ae..087be29 100644 --- a/src/euler54/src/card.rs +++ b/src/euler54/src/card.rs @@ -3,7 +3,13 @@ use crate::value::{Value, Values}; use std::cmp::max; use std::collections::{HashMap, HashSet}; -pub type Cards = [Card; 5]; +pub struct Cards { + pub array: CardsArray, + pub suit_hashset: HashSet, + pub value_hashmap: HashMap, +} + +type CardsArray = [Card; 5]; #[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)] pub struct Card { @@ -21,48 +27,49 @@ impl From<&str> for Card { } } -trait Contains { - fn contains_value(&self, x: &T) -> bool; -} - -impl Contains for &Cards { - fn contains_value(&self, x: &Value) -> bool { - for card in self.iter() { - if card.value == *x { - return true; - } - } - false +impl From<&str> for Cards { + fn from(s: &str) -> Self { + let cards_array: CardsArray = s + .split_whitespace() + .map(Card::from) + .collect::>() + .try_into() + .unwrap_or_else(|_| panic!()); + Cards::new(cards_array) } } -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; - fn to_value_hashmap(&self) -> HashMap; - fn to_suit_hashset(&self) -> HashSet; -} +impl Cards { + fn new(cards: CardsArray) -> Self { + let suit_hashset = cards.to_suit_hashset(); + let value_hashmap = cards.to_value_hashmap(); + Self { + array: cards, + suit_hashset, + value_hashmap, + } + } -impl CardTrait for Cards { - fn have_matching_suits(&self) -> bool { - self[1..] + pub fn have_matching_suits(&self) -> bool { + self.array[1..] .iter() - .zip(self[..4].iter()) + .zip(self.array[..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)) + pub fn contain_all_values_of(&self, values: &Values) -> bool { + values.iter().all(|v| self.value_hashmap.contains_key(v)) } - fn get_highest_card(&self) -> Card { - *self.iter().reduce(|accum, item| max(accum, item)).unwrap() + pub fn get_highest_card(&self) -> Card { + *self + .array + .iter() + .reduce(|accum, item| max(accum, item)) + .unwrap() } - fn have_consecutive_values(&self) -> bool { + pub fn have_consecutive_values(&self) -> bool { let mut count = 0; for value in Value::iterator() { if self.contains_value(value) { @@ -77,6 +84,33 @@ impl CardTrait for Cards { true } + fn contains_value(&self, value: &Value) -> bool { + self.value_hashmap.contains_key(value) + } +} + +trait Contains { + fn contains_value(&self, x: &T) -> bool; +} + +impl Contains for &CardsArray { + fn contains_value(&self, x: &Value) -> bool { + for card in self.iter() { + if card.value == *x { + return true; + } + } + false + } +} + +pub trait CardTrait { + fn to_value_hashmap(&self) -> HashMap; + fn to_suit_hashset(&self) -> HashSet; + fn to_value_hashset(&self) -> HashSet; +} + +impl CardTrait for CardsArray { fn to_suit_hashset(&self) -> HashSet { self.iter().map(|c| c.suit).collect() } @@ -102,8 +136,6 @@ 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"); diff --git a/src/euler54/src/evaluation.rs b/src/euler54/src/evaluation.rs index b659ee5..7d5e221 100644 --- a/src/euler54/src/evaluation.rs +++ b/src/euler54/src/evaluation.rs @@ -1,4 +1,4 @@ -use crate::card::{CardTrait, Cards}; +use crate::card::Cards; use crate::value::Value; impl From<&Cards> for Evaluation { @@ -46,7 +46,7 @@ impl Evaluation { fn is_four_of_a_kind(cards: &Cards) -> Option { cards - .to_value_hashmap() + .value_hashmap .iter() .filter(|(_, count)| **count == 4) .map(|(value, _)| *value) @@ -55,7 +55,7 @@ impl Evaluation { fn is_full_house(cards: &Cards) -> Option { if cards - .to_value_hashmap() + .value_hashmap .values() .filter(|v| [2, 3].contains(v)) .count() @@ -85,7 +85,7 @@ impl Evaluation { fn is_three_of_a_kind(cards: &Cards) -> Option { cards - .to_value_hashmap() + .value_hashmap .iter() .filter(|(_, count)| **count == 3) .map(|(value, _)| *value) @@ -93,8 +93,8 @@ impl Evaluation { } fn is_two_pairs(cards: &Cards) -> Option { - let hm = cards.to_value_hashmap(); - let values: Vec<_> = hm + let values: Vec<_> = cards + .value_hashmap .iter() .filter(|(_, count)| **count == 2) .map(|(value, _)| value) @@ -106,9 +106,10 @@ impl Evaluation { } fn is_one_pair(cards: &Cards) -> Option { - let hm = cards.to_value_hashmap(); - if hm.len() == 4 { - hm.iter() + if cards.value_hashmap.len() == 4 { + cards + .value_hashmap + .iter() .filter(|(_, count)| **count == 2) .map(|(value, _)| *value) .next() diff --git a/src/euler54/src/game.rs b/src/euler54/src/game.rs index 8f06534..9ada368 100644 --- a/src/euler54/src/game.rs +++ b/src/euler54/src/game.rs @@ -1,4 +1,4 @@ -use crate::{card::CardTrait, hand::Hand}; +use crate::hand::Hand; #[derive(Debug, PartialEq)] pub enum Player { diff --git a/src/euler54/src/hand.rs b/src/euler54/src/hand.rs index 8e2d72a..f705daa 100644 --- a/src/euler54/src/hand.rs +++ b/src/euler54/src/hand.rs @@ -1,4 +1,4 @@ -use crate::card::{Card, Cards}; +use crate::card::Cards; use crate::evaluation::Evaluation; pub struct Hand { @@ -9,8 +9,7 @@ pub struct Hand { 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 cards = Cards::from(line); let evaluation = Evaluation::from(&cards); Self { cards, evaluation } } diff --git a/src/euler54/src/lib.rs b/src/euler54/src/lib.rs new file mode 100644 index 0000000..c119f5d --- /dev/null +++ b/src/euler54/src/lib.rs @@ -0,0 +1,6 @@ +pub mod card; +pub mod evaluation; +pub mod game; +pub mod hand; +pub mod suit; +pub mod value; diff --git a/src/euler54/src/main.rs b/src/euler54/src/main.rs index 1e2af74..4e164f7 100644 --- a/src/euler54/src/main.rs +++ b/src/euler54/src/main.rs @@ -1,13 +1,6 @@ -#![allow(dead_code)] - use std::io::{BufRead, BufReader}; -mod card; -mod evaluation; -mod game; -mod hand; -mod suit; -mod value; +use poker::game; fn main() { let mut p1_wins = 0;