From 79366d60fec6307e85b445d2291c3aa0fa092f10 Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Thu, 16 Apr 2026 11:45:34 +0100 Subject: [PATCH] *: small refactors and test changes --- src/card/iters.rs | 4 +-- src/card/tests.rs | 17 +++++++--- src/modes/pair.rs | 4 +-- src/modes/triple.rs | 77 +++++++++++++++++++++++---------------------- src/zipcartesian.rs | 15 +++++---- 5 files changed, 63 insertions(+), 54 deletions(-) diff --git a/src/card/iters.rs b/src/card/iters.rs index 177a14c..92e49a8 100644 --- a/src/card/iters.rs +++ b/src/card/iters.rs @@ -17,7 +17,7 @@ impl Iterator for CardIterator { suit: Suit::Spade, deck, }) => { - self.0 = Card::Joker((deck + 1) * -1); + self.0 = Card::Joker(-(deck + 1)); None } Card::PlayingCard(pc) => { @@ -40,7 +40,7 @@ impl DoubleEndedIterator for CardIterator { suit: Suit::Diamond, deck, }) => { - self.0 = Card::Joker((deck + 1) * -1); + self.0 = Card::Joker(-(deck + 1)); None } Card::PlayingCard(pc) => { diff --git a/src/card/tests.rs b/src/card/tests.rs index 82411ef..bfa989f 100644 --- a/src/card/tests.rs +++ b/src/card/tests.rs @@ -273,11 +273,18 @@ mod test_impls { #[test] fn rank() { - let ranks = Rank::iter_all().into_iter().collect::>(); + let ranks = Rank::all().into_iter().collect::>(); // TEST: Rank::iter_all produces all 13 unique ranks. assert_eq!(ranks.len(), 13); - for rank in Rank::iter_all() { + for (ind, rank) in Rank::all().iter().enumerate() { + let rest = rank.iter_rest().collect::>(); + // TEST: Rank::iter_rest should generate the remaining ranks after + // the current rank in order. + assert_eq!(rest.len(), 13 - ind - 1); + } + + for rank in Rank::all() { let cards = rank.cards().into_iter().collect::>(); assert_eq!(cards.len(), 4, "Expected 4 cards per rank"); for c in cards { @@ -293,8 +300,8 @@ mod test_impls { // Since there are 4 unique cards generated by rank.cards(), and // they all have the same deck (0) and rank (rank), they must differ - // by Suit by virtue of ord. 4 suits suggests (by pigeonhole - // principle) that all suits must have been used. + // by Suit. 4 suits suggests (by pigeonhole principle) that all + // suits must have been used. // So rank.cards() generates all cards of deck 0 that have that rank // (which is precisely 4 cards). QED. @@ -408,7 +415,7 @@ mod test_impls { counter }; - for (rank, suit) in Rank::iter_all() + for (rank, suit) in Rank::all() .into_iter() .zip_cartesian(Suit::iter_all().into_iter()) { diff --git a/src/modes/pair.rs b/src/modes/pair.rs index 454f2c1..5a8c6c1 100644 --- a/src/modes/pair.rs +++ b/src/modes/pair.rs @@ -113,11 +113,11 @@ mod tests { assert_eq!(Pair::new(Card::make_joker(), Card::make_joker()), None); // TEST: Non pair tests. - for (c1, c2) in Rank::iter_all() + for (c1, c2) in Rank::all() .into_iter() // Generate tuples (r1, r2) where r1 != r2 .flat_map(|r1| { - Rank::iter_all() + Rank::all() .into_iter() .filter(move |&r2| r2 != r1) .map(move |r2| (r1, r2)) diff --git a/src/modes/triple.rs b/src/modes/triple.rs index 0b42ead..6a14a41 100644 --- a/src/modes/triple.rs +++ b/src/modes/triple.rs @@ -150,18 +150,22 @@ mod tests { // Triple::2. assert_eq!( trip.2, card, - "Expected the highest card of the triple ({}) to be the sole PlayingCard ({card})", - trip.2 + "Expected Triple::2 to be the sole PlayingCard" + ); + + assert_eq!( + trip.high_card(), + card, + "Expected Triple::HighCard = card" ); } // Iterate over all pairs of cards with similar ranks - for (c1, c2) in Rank::iter_all().into_iter().flat_map(|r| { + for (c1, c2) in Rank::all().into_iter().flat_map(|r| { r.cards().into_iter().zip_cartesian(r.cards().into_iter()) }) { let trip = Triple::new(c1, c2, joker); - // TEST: Any two similar rank cards with 1 joker are a - // Triple. + // TEST: Any two similar rank cards with 1 joker is a Triple. assert_ne!( trip, None, "Expected ({c1}, {c2}, Joker) to make a Triple" @@ -190,13 +194,16 @@ mod tests { trip.1, trip.2, ); + + // TEST: Expect triple high card to be the highest card of c1,c2. + assert_eq!(trip.high_card(), c2); } // Iterate over all pairs of cards with differing ranks - for (c1, c2) in Rank::iter_all() + for (c1, c2) in Rank::all() .into_iter() .flat_map(|r1| { - Rank::iter_all() + Rank::all() .into_iter() .filter(move |&r2| r2 != r1) .map(move |r2| (r1, r2)) @@ -246,6 +253,10 @@ mod tests { let [c1, c2, c3] = ordered([c1, c2, c3]); + // TEST: Triple::high_card should be c3 (the highest card of the + // ordered set). + assert_eq!(trip.high_card(), c3); + // TEST: If a triple is formed of 3 playing cards, they are // ordered s.t. Triple::2 > Triple::1 > Triple::0. assert_eq!( @@ -286,18 +297,11 @@ mod tests { let joker = Card::make_joker(); for (t1, t2) in - // Iterate through all pairs of differing ranks (r1, r2) where r2 > - // r1 - Rank::iter_all() + // Generate an exhaustive set of triples where rank(t1) < + // rank(t2) + Rank::all() .into_iter() - .flat_map(|r1| { - Rank::iter_all() - .into_iter() - .filter(move |&r2| r2 > r1) - .map(move |r2| (r1, r2)) - }) - // Generate an exhaustive set of triples where rank(t1) < - // rank(t2) + .flat_map(move |r1| r1.iter_rest().map(move |r2| (r1, r2))) .flat_map(|(r1, r2)| { exhaustive_triples_rank(r1) .zip_cartesian(exhaustive_triples_rank(r2)) @@ -310,39 +314,38 @@ mod tests { } // So high card rank determines ordering between differing ranked - // triples. + // triples. Let's test what happens within triples of the same high + // card rank. // Iterate through all ranks - for rank in Rank::iter_all() { + for rank in Rank::all() { let cards = rank.cards(); - // All possible 2 joker triples for this rank. - let two_joker_triples = cards - .map(|c| Triple::new(c, joker, joker)) - .map(Option::unwrap); - - for triple in - exhaustive_triples_rank(rank).filter(|x| x.count_jokers() < 2) - { - for two_joker_trip in two_joker_triples { - // TEST: A two joker triple is always worse than any triples - // in the same rank that have at most 1 joker. - assert!(two_joker_trip < triple); - } - } - let [diamond, _, _, spade] = cards; // NOTE: By new test this should be safe to unwrap. let minima = Triple::new(diamond, joker, joker).unwrap(); let maxima = Triple::new(spade, spade, spade).unwrap(); + // All possible 2 joker triples for this rank. + let two_joker_triples = cards + .map(|c| Triple::new(c, joker, joker)) + .map(Option::unwrap); + for triple in exhaustive_triples_rank(rank) { // TEST: The lowest possible triple in a rank is a diamond + 2 // jokers assert!(minima <= triple); // TEST: The highest possible triple in a rank is 3 spades assert!(maxima >= triple); + + if triple.count_jokers() < 2 { + for two_joker_trip in two_joker_triples { + // TEST: A two joker triple is always worse than any + // triples in the same rank that have at most 1 joker. + assert!(two_joker_trip < triple); + } + } } } } @@ -350,8 +353,8 @@ mod tests { #[test] fn footstool() { let triples = exhaustive_triples_deck().collect::>(); - for t1 in &triples { - for t2 in &triples { + for (ind, t1) in triples.iter().enumerate() { + for t2 in &triples[ind..] { // TEST: Expected footstool condition test_footstool(t1, t2); } diff --git a/src/zipcartesian.rs b/src/zipcartesian.rs index 0aae18b..f16e581 100644 --- a/src/zipcartesian.rs +++ b/src/zipcartesian.rs @@ -19,18 +19,17 @@ where I::Item: Copy, { /// Exhaustive coupling of two iterators. - /// For each x in `self`: for each y in `b`: yield (x, y). - /// b: B must implement `Clone`. - fn zip_cartesian( + /// For each x in `self`: for each y in `other`: yield (x, y). + fn zip_cartesian( self, - b: B, - ) -> impl Iterator + Clone + other: Other, + ) -> impl Iterator + Clone where - B::Item: Copy, - B: Iterator + Clone, + Other::Item: Copy, + Other: Iterator + Clone, { self.flat_map(move |a_item| { - b.clone().map(move |b_item| (a_item, b_item)) + other.clone().map(move |b_item| (a_item, b_item)) }) } }