From 564d49ca3ab59d5b48d2e7c269f83ce48547e710 Mon Sep 17 00:00:00 2001 From: Peter Goodspeed-Niklaus Date: Thu, 24 Aug 2017 10:46:56 +0200 Subject: [PATCH 1/8] Implement exercise poker From cf05f41726f36800418db5380724e27b46adbac1 Mon Sep 17 00:00:00 2001 From: Peter Goodspeed-Niklaus Date: Thu, 24 Aug 2017 13:39:33 +0200 Subject: [PATCH 2/8] Add new exercise: poker. Description and tests copied from https://github.com/exercism/problem-specifications/tree/master/exercises/poker A `lib.rs` stub is included so that users don't have to puzzle out the type signature. --- exercises/poker/.gitignore | 7 + exercises/poker/Cargo.toml | 6 + exercises/poker/README.md | 38 +++++ exercises/poker/src/lib.rs | 7 + exercises/poker/tests/poker.rs | 271 +++++++++++++++++++++++++++++++++ 5 files changed, 329 insertions(+) create mode 100644 exercises/poker/.gitignore create mode 100644 exercises/poker/Cargo.toml create mode 100644 exercises/poker/README.md create mode 100644 exercises/poker/src/lib.rs create mode 100644 exercises/poker/tests/poker.rs diff --git a/exercises/poker/.gitignore b/exercises/poker/.gitignore new file mode 100644 index 000000000..cb14a4206 --- /dev/null +++ b/exercises/poker/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/exercises/poker/Cargo.toml b/exercises/poker/Cargo.toml new file mode 100644 index 000000000..7ada23c5e --- /dev/null +++ b/exercises/poker/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "poker" +version = "1.0.0" +authors = ["Peter Goodspeed-Niklaus "] + +[dependencies] diff --git a/exercises/poker/README.md b/exercises/poker/README.md new file mode 100644 index 000000000..c9ee3bfaf --- /dev/null +++ b/exercises/poker/README.md @@ -0,0 +1,38 @@ +# Poker + +Pick the best hand(s) from a list of poker hands. + +See [wikipedia](https://en.wikipedia.org/wiki/List_of_poker_hands) for an overview of poker hands. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning resources. + +## Writing the Code + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests to pass again. The test file is located in the `tests` directory. You can also remove the ignore flag from all the tests to get them to run all at once if you wish. + +Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you haven't already, it will help you with organizing your files. + +## Feedback, Issues, Pull Requests + +The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! + +If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md). + +## Source + +J Dalbey's Programming Practice problems + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html +[help-page]: http://exercism.io/languages/rust diff --git a/exercises/poker/src/lib.rs b/exercises/poker/src/lib.rs new file mode 100644 index 000000000..e34909fec --- /dev/null +++ b/exercises/poker/src/lib.rs @@ -0,0 +1,7 @@ +/// Given a list of poker hands, return a list of those hands which win. +/// +/// Note the type signature: this function should return _the same_ reference to +/// the winning hand(s) as were passed in, not reconstructed strings which happen to be equal. +pub fn winning_hands<'a>(_: &[&'a str]) -> Option> { + unimplemented!() +} diff --git a/exercises/poker/tests/poker.rs b/exercises/poker/tests/poker.rs new file mode 100644 index 000000000..e96b50cf5 --- /dev/null +++ b/exercises/poker/tests/poker.rs @@ -0,0 +1,271 @@ +extern crate poker; +use std::collections::HashSet; +use poker::winning_hands; + +fn hs_from<'a>(input: &[&'a str]) -> HashSet<&'a str> { + let mut hs = HashSet::new(); + for item in input.iter() { + hs.insert(*item); + } + hs +} + +/// Test that the expected output is produced from the given input +/// using the `winning_hands` function. +/// +/// Note that the output can be in any order. Here, we use a HashSet to +/// abstract away the order of outputs. +fn test<'a, 'b>(input: &[&'a str], expected: &[&'b str]) { + assert_eq!( + hs_from(&winning_hands(input).expect( + "This test should produce Some value", + )), + hs_from(expected) + ) +} + +#[test] +fn test_single_hand_always_wins() { + test(&vec!["4S 5S 7H 8D JC"], &vec!["4S 5S 7H 8D JC"]) +} + +#[test] +fn test_highest_card_of_all_hands_wins() { + test( + &vec!["4D 5S 6S 8D 3C", "2S 4C 7S 9H 10H", "3S 4S 5D 6H JH"], + &vec!["3S 4S 5D 6H JH"], + ) +} + +#[test] +fn test_a_tie_has_multiple_winners() { + test( + &vec![ + "4D 5S 6S 8D 3C", + "2S 4C 7S 9H 10H", + "3S 4S 5D 6H JH", + "3H 4H 5C 6C JD", + ], + &vec!["3S 4S 5D 6H JH", "3H 4H 5C 6C JD"], + ) +} + +#[test] +fn test_high_card_can_be_low_card_in_an_otherwise_tie() { + // multiple hands with the same high cards, tie compares next highest ranked, + // down to last card + test( + &vec!["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"], + &vec!["3S 5H 6S 8D 7H"], + ) +} + +#[test] +fn test_one_pair_beats_high_card() { + test( + &vec!["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"], + &vec!["2S 4H 6S 4D JH"], + ) +} + +#[test] +fn test_highest_pair_wins() { + test( + &vec!["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"], + &vec!["2S 4H 6C 4D JD"], + ) +} + +#[test] +fn test_two_pairs_beats_one_pair() { + test( + &vec!["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"], + &vec!["4S 5H 4C 8C 5C"], + ) +} + +#[test] +fn test_two_pair_ranks() { + // both hands have two pairs, highest ranked pair wins + test( + &vec!["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"], + &vec!["2S 8H 2D 8D 3H"], + ) +} + +#[test] +fn test_two_pairs_second_pair_cascade() { + // both hands have two pairs, with the same highest ranked pair, + // tie goes to low pair + test( + &vec!["2S QS 2C QD JH", "JD QH JS 8D QC"], + &vec!["JD QH JS 8D QC"], + ) +} + +#[test] +fn test_two_pairs_last_card_cascade() { + // both hands have two identically ranked pairs, + // tie goes to remaining card (kicker) + test( + &vec!["JD QH JS 8D QC", "JS QS JC 2D QD"], + &vec!["JD QH JS 8D QC"], + ) +} + +#[test] +fn test_three_of_a_kind_beats_two_pair() { + test( + &vec!["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"], + &vec!["4S 5H 4C 8S 4H"], + ) +} + +#[test] +fn test_three_of_a_kind_ranks() { + //both hands have three of a kind, tie goes to highest ranked triplet + test( + &vec!["2S 2H 2C 8D JH", "4S AH AS 8C AD"], + &vec!["4S AH AS 8C AD"], + ) +} + +#[test] +fn test_three_of_a_kind_cascade_ranks() { + // with multiple decks, two players can have same three of a kind, + // ties go to highest remaining cards + test( + &vec!["4S AH AS 7C AD", "4S AH AS 8C AD"], + &vec!["4S AH AS 8C AD"], + ) +} + +#[test] +fn test_straight_beats_three_of_a_kind() { + test( + &vec!["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"], + &vec!["3S 4D 2S 6D 5C"], + ) +} + +#[test] +fn test_aces_can_end_a_straight_high() { + // aces can end a straight (10 J Q K A) + test( + &vec!["4S 5H 4C 8D 4H", "10D JH QS KD AC"], + &vec!["10D JH QS KD AC"], + ) +} + +#[test] +fn test_aces_can_end_a_straight_low() { + // aces can start a straight (A 2 3 4 5) + test( + &vec!["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"], + &vec!["4D AH 3S 2D 5C"], + ) +} + +#[test] +fn test_straight_cascade() { + // both hands with a straight, tie goes to highest ranked card + test( + &vec!["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"], + &vec!["5S 7H 8S 9D 6H"], + ) +} + +#[test] +fn test_straight_scoring() { + // even though an ace is usually high, a 5-high straight is the lowest-scoring straight + test( + &vec!["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"], + &vec!["2H 3C 4D 5D 6H"], + ) +} + +#[test] +fn test_flush_beats_a_straight() { + test( + &vec!["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"], + &vec!["2S 4S 5S 6S 7S"], + ) +} + +#[test] +fn test_flush_cascade() { + // both hands have a flush, tie goes to high card, down to the last one if necessary + test( + &vec!["4H 7H 8H 9H 6H", "2S 4S 5S 6S 7S"], + &vec!["4H 7H 8H 9H 6H"], + ) +} + +#[test] +fn test_full_house_beats_a_flush() { + test( + &vec!["3H 6H 7H 8H 5H", "4S 5C 4C 5D 4H"], + &vec!["4S 5C 4C 5D 4H"], + ) +} + +#[test] +fn test_full_house_ranks() { + // both hands have a full house, tie goes to highest-ranked triplet + test( + &vec!["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"], + &vec!["5H 5S 5D 8S 8D"], + ) +} + +#[test] +fn test_full_house_cascade() { + // with multiple decks, both hands have a full house with the same triplet, tie goes to the pair + test( + &vec!["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"], + &vec!["5H 5S 5D 9S 9D"], + ) +} + +#[test] +fn test_four_of_a_kind_beats_full_house() { + test( + &vec!["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"], + &vec!["3S 3H 2S 3D 3C"], + ) +} + +#[test] +fn test_four_of_a_kind_ranks() { + // both hands have four of a kind, tie goes to high quad + test( + &vec!["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"], + &vec!["4S 5H 5S 5D 5C"], + ) +} + +#[test] +fn test_four_of_a_kind_cascade() { + // with multiple decks, both hands with identical four of a kind, tie determined by kicker + test( + &vec!["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"], + &vec!["3S 3H 4S 3D 3C"], + ) +} + +#[test] +fn test_straight_flush_beats_four_of_a_kind() { + test( + &vec!["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"], + &vec!["7S 8S 9S 6S 10S"], + ) +} + +#[test] +fn test_straight_flush_ranks() { + // both hands have straight flush, tie goes to highest-ranked card + test( + &vec!["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"], + &vec!["5S 7S 8S 9S 6S"], + ) +} From 6b0213c061dbf16422ad43e344c802e9f047a3e4 Mon Sep 17 00:00:00 2001 From: Peter Goodspeed-Niklaus Date: Thu, 24 Aug 2017 20:09:00 +0200 Subject: [PATCH 3/8] Add example which passes all tests --- exercises/poker/Cargo-example.toml | 8 + exercises/poker/example.rs | 341 +++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) create mode 100644 exercises/poker/Cargo-example.toml create mode 100644 exercises/poker/example.rs diff --git a/exercises/poker/Cargo-example.toml b/exercises/poker/Cargo-example.toml new file mode 100644 index 000000000..fb30e27d7 --- /dev/null +++ b/exercises/poker/Cargo-example.toml @@ -0,0 +1,8 @@ +[package] +name = "poker" +version = "1.0.0" +authors = ["Peter Goodspeed-Niklaus "] + +[dependencies] +try_opt = "0.1.1" +counter = "0.1.0" diff --git a/exercises/poker/example.rs b/exercises/poker/example.rs new file mode 100644 index 000000000..2937d964f --- /dev/null +++ b/exercises/poker/example.rs @@ -0,0 +1,341 @@ +use std::fmt; +use std::cmp::Ordering; + +#[macro_use] +extern crate try_opt; + +extern crate counter; +use counter::Counter; + +/// Given a list of poker hands, return a list of those hands which win. +/// +/// Note the type signature: this function should return _the same_ reference to +/// the winning hand(s) as were passed in, not reconstructed strings which happen to be equal. +pub fn winning_hands<'a>(hands: &[&'a str]) -> Option> { + let mut hands = try_opt!( + hands + .iter() + .map(|source| Hand::try_from(source)) + .collect::>>() + ); + hands.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less)); + hands.last().map(|last| { + hands + .iter() + .rev() + .take_while(|&item| item.partial_cmp(last) == Some(Ordering::Equal)) + .map(|hand| hand.source) + .collect() + }) +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy, Hash)] +enum Suit { + Spades, + Clubs, + Diamonds, + Hearts, +} + +impl Suit { + fn try_from(source: &str) -> Option { + use Suit::*; + match source { + "S" => Some(Spades), + "C" => Some(Clubs), + "D" => Some(Diamonds), + "H" => Some(Hearts), + _ => None, + } + } +} + +impl fmt::Display for Suit { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Suit::*; + write!( + f, + "{}", + match *self { + Spades => "S", + Clubs => "C", + Diamonds => "D", + Hearts => "H", + } + ) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +enum Rank { + Number(u8), + Jack, + Queen, + King, + Ace, +} + +impl Rank { + fn try_from(source: &str) -> Option { + use Rank::*; + match source { + "A" => Some(Ace), + "K" => Some(King), + "Q" => Some(Queen), + "J" => Some(Jack), + "10" => Some(Number(10)), + "9" => Some(Number(9)), + "8" => Some(Number(8)), + "7" => Some(Number(7)), + "6" => Some(Number(6)), + "5" => Some(Number(5)), + "4" => Some(Number(4)), + "3" => Some(Number(3)), + "2" => Some(Number(2)), + _ => None, + } + } + + fn value(&self) -> usize { + use Rank::*; + match *self { + Ace => 14, + King => 13, + Queen => 12, + Jack => 11, + Number(n) => n as usize, + } + } +} + +impl fmt::Display for Rank { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Rank::*; + let num_str; // early declaration to placate NLL of Number case + write!( + f, + "{}", + match *self { + Ace => "A", + King => "K", + Queen => "Q", + Jack => "J", + Number(n) => { + num_str = n.to_string(); + &num_str + } + } + ) + } +} + +impl PartialOrd for Rank { + fn partial_cmp(&self, other: &Rank) -> Option { + Some(self.value().cmp(&other.value())) + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)] +struct Card { + rank: Rank, + suit: Suit, +} + +impl Card { + fn try_from_split(source: &str, split: usize) -> Option { + Some(Card { + rank: try_opt!(Rank::try_from(&source[..split])), + suit: try_opt!(Suit::try_from(&source[split..])), + }) + } + + fn try_from(source: &str) -> Option { + match source.len() { + 3 => Card::try_from_split(source, 2), + 2 => Card::try_from_split(source, 1), + _ => None, + } + } +} + +impl fmt::Display for Card { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}{}", self.rank, self.suit) + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +enum PokerHand { + HighCard, + OnePair, + TwoPair, + ThreeOfAKind, + Straight, + Flush, + FullHouse, + FourOfAKind, + StraightFlush, +} + +impl PokerHand { + fn is_ace_low_straight(cards: &[Card]) -> bool { + // special case: ace-low straight + // still depends on the sorted precondition + cards[0].rank.value() == 2 && cards[4].rank == Rank::Ace && + cards + .windows(2) + .take(3) // (0, 1), (1, 2), (2, 3) --> skips 4, ace + .map(|pair| pair[1].rank.value() - pair[0].rank.value()) + .all(|diff| diff == 1) + } + + fn analyze(cards: &[Card]) -> Option { + if cards.len() == 5 { + let suit_counter = Counter::init(cards.iter().map(|c| c.suit)); + let is_flush = suit_counter + .most_common() + .map(|(_suit, count)| count) + .next() == Some(5); + // Note that `is_straight` depends on a precondition: it only works + // if the input `cards` are sorted by rank value ascending. + let is_straight = cards + .windows(2) + .map(|pair| pair[1].rank.value() - pair[0].rank.value()) + .all(|diff| diff == 1) || + PokerHand::is_ace_low_straight(cards); + + if is_flush && is_straight { + return Some(PokerHand::StraightFlush); + } + + let rank_counter = Counter::init(cards.iter().map(|c| c.rank)); + let mut rc_iter = rank_counter.most_common().map(|(_rank, count)| count); + let rc_most = rc_iter.next(); + let rc_second = rc_iter.next(); + + if rc_most == Some(4) { + return Some(PokerHand::FourOfAKind); + } + if rc_most == Some(3) && rc_second == Some(2) { + return Some(PokerHand::FullHouse); + } + if is_flush { + return Some(PokerHand::Flush); + } + if is_straight { + return Some(PokerHand::Straight); + } + if rc_most == Some(3) { + return Some(PokerHand::ThreeOfAKind); + } + if rc_most == Some(2) && rc_second == Some(2) { + return Some(PokerHand::TwoPair); + } + if rc_most == Some(2) { + return Some(PokerHand::OnePair); + } + Some(PokerHand::HighCard) + } else { + None + } + } +} + +#[derive(Debug, PartialEq, Eq)] +struct Hand<'a> { + source: &'a str, + cards: [Card; 5], + hand_type: PokerHand, +} + +impl<'a> Hand<'a> { + fn try_from(source: &'a str) -> Option { + let mut cards = try_opt!( + source + .split_whitespace() + .map(|s| Card::try_from(s)) + .collect::>>() + ); + cards.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less)); + if cards.len() == 5 { + Some(Hand { + source: source, + cards: [cards[0], cards[1], cards[2], cards[3], cards[4]], + hand_type: try_opt!(PokerHand::analyze(&cards)), + }) + } else { + None + } + } + + fn cmp_high_card(&self, other: &Hand, card: usize) -> Ordering { + let mut ordering = self.cards[card].rank.value().cmp( + &other.cards[card].rank.value(), + ); + if card != 0 { + ordering = ordering.then_with(|| self.cmp_high_card(other, card - 1)); + } + ordering + } + + fn value_by_frequency(&self) -> (Option, Option, Option) { + let rank_counter = Counter::init(self.cards.iter().map(|c| c.rank)); + let mut rc_iter = rank_counter + .most_common_tiebreaker(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Less)) + .map(|(rank, _count)| rank); + (rc_iter.next(), rc_iter.next(), rc_iter.next()) + } + + fn cmp_cascade_by_freq(&self, other: &Hand) -> Ordering { + let (s1, s2, s3) = self.value_by_frequency(); + let (o1, o2, o3) = other.value_by_frequency(); + s1.partial_cmp(&o1) + .map(|c| { + c.then( + s2.partial_cmp(&o2) + .map(|c2| c2.then(s3.partial_cmp(&o3).unwrap_or(Ordering::Equal))) + .unwrap_or(Ordering::Equal), + ) + }) + .unwrap_or(Ordering::Equal) + } + + fn cmp_straight(&self, other: &Hand) -> Ordering { + let s = if PokerHand::is_ace_low_straight(&self.cards) { + 5 + } else { + self.cards[4].rank.value() + }; + let o = if PokerHand::is_ace_low_straight(&other.cards) { + 5 + } else { + other.cards[4].rank.value() + }; + s.cmp(&o) + } +} + +impl<'a> fmt::Display for Hand<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.source) + } +} + +impl<'a> PartialOrd for Hand<'a> { + fn partial_cmp(&self, other: &Hand) -> Option { + Some(self.hand_type.cmp(&other.hand_type).then_with(|| { + use PokerHand::*; + match self.hand_type { + HighCard => self.cmp_high_card(other, 4), + OnePair => self.cmp_cascade_by_freq(other), + TwoPair => self.cmp_cascade_by_freq(other), + ThreeOfAKind => self.cmp_cascade_by_freq(other), + Straight => self.cmp_straight(other), + Flush => self.cmp_high_card(other, 4), + FullHouse => self.cmp_cascade_by_freq(other), + FourOfAKind => self.cmp_cascade_by_freq(other), + StraightFlush => self.cmp_straight(other), + } + })) + } +} From 4f54f5a8986e670f98f108d63a50c76e3f4a57c9 Mon Sep 17 00:00:00 2001 From: Peter Goodspeed-Niklaus Date: Thu, 24 Aug 2017 20:25:40 +0200 Subject: [PATCH 4/8] Finish preparing exercise * Ignore all but the first test * Add entry to `config.json` and ensure `configlet lint` has no output Tried running `configlet generate . --only poker`, but that eliminated all problem-specific text from the README; reverted that change before commit. --- config.json | 14 ++++++++++++++ exercises/poker/tests/poker.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/config.json b/config.json index 266223c0b..10aa008e5 100644 --- a/config.json +++ b/config.json @@ -623,6 +623,20 @@ "topics": [ ] }, + { + "uuid": "0a33f3ac-cedd-4a40-a132-9d044b0e9977", + "slug": "poker", + "core": false, + "unlocked_by": null, + "difficulty": 6, + "topics": [ + "lifetimes", + "struct", + "string parsing", + "enum", + "traits" + ] + }, { "uuid": "f3172997-91f5-4941-a76e-91c4b8eed401", "slug": "anagram", diff --git a/exercises/poker/tests/poker.rs b/exercises/poker/tests/poker.rs index e96b50cf5..85f04dae0 100644 --- a/exercises/poker/tests/poker.rs +++ b/exercises/poker/tests/poker.rs @@ -30,6 +30,7 @@ fn test_single_hand_always_wins() { } #[test] +#[ignore] fn test_highest_card_of_all_hands_wins() { test( &vec!["4D 5S 6S 8D 3C", "2S 4C 7S 9H 10H", "3S 4S 5D 6H JH"], @@ -38,6 +39,7 @@ fn test_highest_card_of_all_hands_wins() { } #[test] +#[ignore] fn test_a_tie_has_multiple_winners() { test( &vec![ @@ -51,6 +53,7 @@ fn test_a_tie_has_multiple_winners() { } #[test] +#[ignore] fn test_high_card_can_be_low_card_in_an_otherwise_tie() { // multiple hands with the same high cards, tie compares next highest ranked, // down to last card @@ -61,6 +64,7 @@ fn test_high_card_can_be_low_card_in_an_otherwise_tie() { } #[test] +#[ignore] fn test_one_pair_beats_high_card() { test( &vec!["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"], @@ -69,6 +73,7 @@ fn test_one_pair_beats_high_card() { } #[test] +#[ignore] fn test_highest_pair_wins() { test( &vec!["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"], @@ -77,6 +82,7 @@ fn test_highest_pair_wins() { } #[test] +#[ignore] fn test_two_pairs_beats_one_pair() { test( &vec!["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"], @@ -85,6 +91,7 @@ fn test_two_pairs_beats_one_pair() { } #[test] +#[ignore] fn test_two_pair_ranks() { // both hands have two pairs, highest ranked pair wins test( @@ -94,6 +101,7 @@ fn test_two_pair_ranks() { } #[test] +#[ignore] fn test_two_pairs_second_pair_cascade() { // both hands have two pairs, with the same highest ranked pair, // tie goes to low pair @@ -104,6 +112,7 @@ fn test_two_pairs_second_pair_cascade() { } #[test] +#[ignore] fn test_two_pairs_last_card_cascade() { // both hands have two identically ranked pairs, // tie goes to remaining card (kicker) @@ -114,6 +123,7 @@ fn test_two_pairs_last_card_cascade() { } #[test] +#[ignore] fn test_three_of_a_kind_beats_two_pair() { test( &vec!["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"], @@ -122,6 +132,7 @@ fn test_three_of_a_kind_beats_two_pair() { } #[test] +#[ignore] fn test_three_of_a_kind_ranks() { //both hands have three of a kind, tie goes to highest ranked triplet test( @@ -131,6 +142,7 @@ fn test_three_of_a_kind_ranks() { } #[test] +#[ignore] fn test_three_of_a_kind_cascade_ranks() { // with multiple decks, two players can have same three of a kind, // ties go to highest remaining cards @@ -141,6 +153,7 @@ fn test_three_of_a_kind_cascade_ranks() { } #[test] +#[ignore] fn test_straight_beats_three_of_a_kind() { test( &vec!["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"], @@ -149,6 +162,7 @@ fn test_straight_beats_three_of_a_kind() { } #[test] +#[ignore] fn test_aces_can_end_a_straight_high() { // aces can end a straight (10 J Q K A) test( @@ -158,6 +172,7 @@ fn test_aces_can_end_a_straight_high() { } #[test] +#[ignore] fn test_aces_can_end_a_straight_low() { // aces can start a straight (A 2 3 4 5) test( @@ -167,6 +182,7 @@ fn test_aces_can_end_a_straight_low() { } #[test] +#[ignore] fn test_straight_cascade() { // both hands with a straight, tie goes to highest ranked card test( @@ -176,6 +192,7 @@ fn test_straight_cascade() { } #[test] +#[ignore] fn test_straight_scoring() { // even though an ace is usually high, a 5-high straight is the lowest-scoring straight test( @@ -185,6 +202,7 @@ fn test_straight_scoring() { } #[test] +#[ignore] fn test_flush_beats_a_straight() { test( &vec!["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"], @@ -193,6 +211,7 @@ fn test_flush_beats_a_straight() { } #[test] +#[ignore] fn test_flush_cascade() { // both hands have a flush, tie goes to high card, down to the last one if necessary test( @@ -202,6 +221,7 @@ fn test_flush_cascade() { } #[test] +#[ignore] fn test_full_house_beats_a_flush() { test( &vec!["3H 6H 7H 8H 5H", "4S 5C 4C 5D 4H"], @@ -210,6 +230,7 @@ fn test_full_house_beats_a_flush() { } #[test] +#[ignore] fn test_full_house_ranks() { // both hands have a full house, tie goes to highest-ranked triplet test( @@ -219,6 +240,7 @@ fn test_full_house_ranks() { } #[test] +#[ignore] fn test_full_house_cascade() { // with multiple decks, both hands have a full house with the same triplet, tie goes to the pair test( @@ -228,6 +250,7 @@ fn test_full_house_cascade() { } #[test] +#[ignore] fn test_four_of_a_kind_beats_full_house() { test( &vec!["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"], @@ -236,6 +259,7 @@ fn test_four_of_a_kind_beats_full_house() { } #[test] +#[ignore] fn test_four_of_a_kind_ranks() { // both hands have four of a kind, tie goes to high quad test( @@ -245,6 +269,7 @@ fn test_four_of_a_kind_ranks() { } #[test] +#[ignore] fn test_four_of_a_kind_cascade() { // with multiple decks, both hands with identical four of a kind, tie determined by kicker test( @@ -254,6 +279,7 @@ fn test_four_of_a_kind_cascade() { } #[test] +#[ignore] fn test_straight_flush_beats_four_of_a_kind() { test( &vec!["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"], @@ -262,6 +288,7 @@ fn test_straight_flush_beats_four_of_a_kind() { } #[test] +#[ignore] fn test_straight_flush_ranks() { // both hands have straight flush, tie goes to highest-ranked card test( From e241b085db0a5f1e94474f2a732fbb943bef8d95 Mon Sep 17 00:00:00 2001 From: Peter Goodspeed-Niklaus Date: Tue, 5 Sep 2017 13:34:38 +0200 Subject: [PATCH 5/8] Add hints to readme Possibly the simplest way to solve this problem is to create a PokerHand type which implements PartialOrd, and then sort the input and take the highest values. However, this may not be obvious to novice coders. The new hints in the readme should point people in the right direction. --- exercises/poker/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/exercises/poker/README.md b/exercises/poker/README.md index c9ee3bfaf..75e155405 100644 --- a/exercises/poker/README.md +++ b/exercises/poker/README.md @@ -4,6 +4,15 @@ Pick the best hand(s) from a list of poker hands. See [wikipedia](https://en.wikipedia.org/wiki/List_of_poker_hands) for an overview of poker hands. +## Hints + +- Ranking a list of poker hands can be considered a sorting problem. +- Rust provides the [sort](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort) method for `Vec where T: Ord`. +- [`Ord` types](https://doc.rust-lang.org/std/cmp/trait.Ord.html) are form a [total order](https://en.wikipedia.org/wiki/Total_order): exactly one of `a < b`, `a == b`, or `a > b` must be true. +- Poker hands do not conform to a total order: it is possible for two hands to be non-equal but have equal sort order. Example: `3S 4S 5D 6H JH"`, `"3H 4H 5C 6C JD"`. +- Rust provides the [`PartialOrd` trait](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) to handle the case of sortable things which do not have a total order. However, it doesn't provide a standard `sort` method for `Vec where T: PartialOrd`. The standard idiom to sort a vector in this case is `your_vec.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::{Less|Equal|Greater}));`, depending on your needs. ` +- You might consider implementing a type representing a poker hand which implements `PartialOrd`. + ## Rust Installation Refer to the [exercism help page][help-page] for Rust installation and learning resources. From 9b82d64c621103e67b2a3698ddff58629cce9743 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Mon, 18 Sep 2017 03:09:50 -0700 Subject: [PATCH 6/8] poker: put hints where they need to be --- exercises/poker/.meta/hints.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 exercises/poker/.meta/hints.md diff --git a/exercises/poker/.meta/hints.md b/exercises/poker/.meta/hints.md new file mode 100644 index 000000000..3eecd33e5 --- /dev/null +++ b/exercises/poker/.meta/hints.md @@ -0,0 +1,8 @@ +## Hints + +- Ranking a list of poker hands can be considered a sorting problem. +- Rust provides the [sort](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort) method for `Vec where T: Ord`. +- [`Ord` types](https://doc.rust-lang.org/std/cmp/trait.Ord.html) are form a [total order](https://en.wikipedia.org/wiki/Total_order): exactly one of `a < b`, `a == b`, or `a > b` must be true. +- Poker hands do not conform to a total order: it is possible for two hands to be non-equal but have equal sort order. Example: `3S 4S 5D 6H JH"`, `"3H 4H 5C 6C JD"`. +- Rust provides the [`PartialOrd` trait](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) to handle the case of sortable things which do not have a total order. However, it doesn't provide a standard `sort` method for `Vec where T: PartialOrd`. The standard idiom to sort a vector in this case is `your_vec.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::{Less|Equal|Greater}));`, depending on your needs. ` +- You might consider implementing a type representing a poker hand which implements `PartialOrd`. From 05b72d26e0bafd96fb13c4f32f72a46892f142d5 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Mon, 18 Sep 2017 03:21:16 -0700 Subject: [PATCH 7/8] poker: correctly list source --- exercises/poker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/poker/README.md b/exercises/poker/README.md index 75e155405..6656fe909 100644 --- a/exercises/poker/README.md +++ b/exercises/poker/README.md @@ -37,7 +37,7 @@ If you want to know more about Exercism, take a look at the [contribution guide] ## Source -J Dalbey's Programming Practice problems +Inspired by the training course from Udacity. [https://www.udacity.com/course/viewer#!/c-cs212/](https://www.udacity.com/course/viewer#!/c-cs212/) ## Submitting Incomplete Solutions From f4b2ae5c2dfbfe3b02d8a8151baa982d5ed0131a Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Mon, 18 Sep 2017 03:26:14 -0700 Subject: [PATCH 8/8] poker: README generator wrapped the lines --- exercises/poker/README.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/exercises/poker/README.md b/exercises/poker/README.md index 6656fe909..2f978d0eb 100644 --- a/exercises/poker/README.md +++ b/exercises/poker/README.md @@ -2,7 +2,8 @@ Pick the best hand(s) from a list of poker hands. -See [wikipedia](https://en.wikipedia.org/wiki/List_of_poker_hands) for an overview of poker hands. +See [wikipedia](https://en.wikipedia.org/wiki/List_of_poker_hands) for an +overview of poker hands. ## Hints @@ -13,9 +14,11 @@ See [wikipedia](https://en.wikipedia.org/wiki/List_of_poker_hands) for an overvi - Rust provides the [`PartialOrd` trait](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) to handle the case of sortable things which do not have a total order. However, it doesn't provide a standard `sort` method for `Vec where T: PartialOrd`. The standard idiom to sort a vector in this case is `your_vec.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::{Less|Equal|Greater}));`, depending on your needs. ` - You might consider implementing a type representing a poker hand which implements `PartialOrd`. + ## Rust Installation -Refer to the [exercism help page][help-page] for Rust installation and learning resources. +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. ## Writing the Code @@ -25,9 +28,14 @@ Execute the tests with: $ cargo test ``` -All but the first test have been ignored. After you get the first test to pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests to pass again. The test file is located in the `tests` directory. You can also remove the ignore flag from all the tests to get them to run all at once if you wish. +All but the first test have been ignored. After you get the first test to +pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests +to pass again. The test file is located in the `tests` directory. You can +also remove the ignore flag from all the tests to get them to run all at once +if you wish. -Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you haven't already, it will help you with organizing your files. +Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you +haven't already, it will help you with organizing your files. ## Feedback, Issues, Pull Requests @@ -35,13 +43,12 @@ The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is th If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md). +[help-page]: http://exercism.io/languages/rust +[crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html + ## Source Inspired by the training course from Udacity. [https://www.udacity.com/course/viewer#!/c-cs212/](https://www.udacity.com/course/viewer#!/c-cs212/) ## Submitting Incomplete Solutions - It's possible to submit an incomplete solution so you can see how others have completed the exercise. - -[crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html -[help-page]: http://exercism.io/languages/rust