modes:pair:tests: use zip_cartesian and simplify tests.

This commit is contained in:
2026-04-14 18:21:08 +01:00
committed by oreodave
parent 501410f725
commit 8282bd877a

View File

@@ -4,13 +4,12 @@ use crate::{card::Card, helper::ordered};
pub struct Pair(Card, Card); pub struct Pair(Card, Card);
impl Pair { impl Pair {
/** Create a new pair utilising two cards, `c1` and `c2`. Will return None /// Create a new pair utilising two cards, `c1` and `c2`. Will return None
if a Pair cannot be constructed out of the two cards. /// if a Pair cannot be constructed out of the two cards.
///
NOTE: By construction, if the Pair includes a Joker, that Joker will be the /// NOTE: By construction, if the Pair includes a Joker, that Joker will be
first member of the pair. In other words, Pair::1 will always be a valid /// the first member of the pair. In other words, Pair::1 will always be a
playing card. /// valid playing card.
*/
pub fn new(c1: Card, c2: Card) -> Option<Pair> { pub fn new(c1: Card, c2: Card) -> Option<Pair> {
let [c1, c2] = ordered([c1, c2]); let [c1, c2] = ordered([c1, c2]);
@@ -61,8 +60,8 @@ impl Hand for Pair {
let s1 = Single::new(self.1).unwrap(); let s1 = Single::new(self.1).unwrap();
let s2 = Single::new(b.1).unwrap(); let s2 = Single::new(b.1).unwrap();
match s1.footstool(&s2) { match s1.footstool(&s2) {
Footstool::None => Footstool::None, Footstool::Full => Footstool::Half,
_ => Footstool::Half, _ => Footstool::None,
} }
} }
} }
@@ -92,6 +91,7 @@ mod tests {
use crate::{ use crate::{
card::{PlayingCard, Rank}, card::{PlayingCard, Rank},
modes::tests::test_footstool, modes::tests::test_footstool,
zipcartesian::ZipCatersianExt,
}; };
#[test] #[test]
@@ -107,63 +107,52 @@ mod tests {
assert_eq!(Pair::new(Card::make_joker(), Card::make_joker()), None); assert_eq!(Pair::new(Card::make_joker(), Card::make_joker()), None);
// TEST: Non pair tests. // TEST: Non pair tests.
Rank::iter_all() for (c1, c2) in Rank::iter_all()
// Generate a mapping (r1, r2) where r1, r2 are ranks and r1 != r2 // Generate tuples (r1, r2) where r1 != r2
.flat_map(|r1| { .flat_map(|r1| {
Rank::iter_all() Rank::iter_all()
.filter(move |&r2| r2 != r1) .filter(move |&r2| r2 != r1)
.map(move |r2| (r1, r2)) .map(move |r2| (r1, r2))
}) })
// Flat Map for combinations of (cards of rank r1, cards of rank r2) // Generate all cards where their ranks differ
.flat_map(|(r1, r2)| { .flat_map(|(r1, r2)| r1.cards().zip_cartesian(r2.cards()))
r1.cards() {
.flat_map(move |c1| r2.cards().map(move |c2| (c1, c2))) // TEST: Two cards of differing rank cannot be a pair
}) let pair = Pair::new(c1, c2);
.for_each(|(c1, c2)| { assert!(
// TEST: Two cards of differing rank cannot be a pair pair.is_none(),
let pair = Pair::new(c1, c2); "Expected cards {c1} and {c2} to never form a pair."
assert!( );
pair.is_none(), }
"Expected cards {c1} and {c2} to never form a pair."
)
});
// TEST: Improper pair tests. // TEST: Improper pair tests.
PlayingCard::iter_all(0) for c1 in PlayingCard::iter_all(0).map(Card::PlayingCard) {
.map(Card::PlayingCard) // TEST: Any card with one joker can be made into a valid pair.
.map(|c1| { let c2 = Card::make_joker();
// TEST: Any card with one joker can be made into a valid pair. let pair = Pair::new(c1, c2);
let c2 = Card::make_joker(); assert!(pair.is_some(), "Expected ({c1}, {c2}) to be a valid pair",);
let pair = Pair::new(c1, c2);
assert!(
pair.is_some(),
"Expected ({c1}, {c2}) to be a valid pair",
);
pair.unwrap()
})
.for_each(|pair| {
// TEST: Pairs with a joker are improper.
assert!(pair.is_improper(), "Expected {pair} to be improper");
// TEST: Improper pairs have a Joker in Pair::0. let pair = pair.unwrap();
assert!(pair.0.is_joker(), "Expected {} to be a joker", pair.0);
// TEST: Improper pairs have a playing card in Pair::1. // TEST: Pairs with a joker are improper.
assert!( assert!(pair.is_improper(), "Expected {pair} to be improper");
!pair.1.is_joker(),
"Expected {} to be a playing card", let Pair(c1, c2) = pair;
pair.1
); // TEST: Improper pairs have a Joker in Pair::0 (c1).
}); assert!(c1.is_joker(), "Expected {} to be a joker", pair.0);
// TEST: Improper pairs have a playing card in Pair::1 (c2).
assert!(!c2.is_joker(), "Expected {} to be a playing card", pair.1);
}
// TEST: Proper pair tests // TEST: Proper pair tests
;
PlayingCard::iter_all(0) PlayingCard::iter_all(0)
// Flat Map every Playing Card (c1) into combinations (Card c1, Card .map(Card::PlayingCard)
// of same rank as c1) // Flat Map every Playing Card (c1) into combinations (Card of same
.flat_map(|c1| { // rank as c1, c1)
let card_c1 = Card::PlayingCard(c1); .flat_map(|c1| c1.rank().unwrap().cards().map(move |c2| (c1, c2)))
c1.rank.cards().map(move |c2| (card_c1, c2))
})
.map(|(c1, c2)| { .map(|(c1, c2)| {
// TEST: Two cards of similar rank make a valid pair. // TEST: Two cards of similar rank make a valid pair.
let pair = { let pair = {
@@ -174,31 +163,33 @@ mod tests {
); );
pair.unwrap() pair.unwrap()
}; };
// TEST: Pairs of two playing cards are proper
assert!(pair.is_proper(), "Expected {pair} to be proper.");
(c1, c2, pair) (c1, c2, pair)
}) })
.for_each(|(c1, c2, Pair(p1, p2))| { .for_each(|(c1, c2, pair)| {
// TEST: Pairs of two playing cards are proper
assert!(
pair.is_proper(),
"Expected {pair} from {c1} and {c2} to be proper."
);
let Pair(p1, p2) = pair;
// TEST: Pairs always sort their cards in strength. // TEST: Pairs always sort their cards in strength.
let pair_cards = [p1, p2]; let pair_cards = [p1, p2];
let sorted_cards = ordered([c1, c2]); let sorted_cards = ordered([c1, c2]);
assert_eq!( assert_eq!(
pair_cards, pair_cards, sorted_cards,
sorted_cards, "Expected {} to be equivalent to ({}, {})",
"Expected ({}, {}) to be ({}, {})", pair, sorted_cards[0], sorted_cards[1]
pair_cards[0],
pair_cards[1],
sorted_cards[0],
sorted_cards[1]
); );
}); });
} }
/** Create an exhaustive set of pairs for one deck. */ /// Create an exhaustive set of pairs for one deck.
fn exhaustive_pairs_deck() -> impl Iterator<Item = Pair> { fn exhaustive_pairs_deck() -> impl Iterator<Item = Pair> + Clone {
Card::iter_all(1).flat_map(|c1| { Card::iter_all(1)
Card::iter_all(1).filter_map(move |c2| Pair::new(c1, c2)) .zip_cartesian(Card::iter_all(1))
}) .filter_map(|(c1, c2)| Pair::new(c1, c2))
} }
#[test] #[test]
@@ -215,8 +206,7 @@ mod tests {
} }
exhaustive_pairs_deck() exhaustive_pairs_deck()
// Create a flat map of all combinations of two valid pairs. .zip_cartesian(exhaustive_pairs_deck())
.flat_map(|p1| exhaustive_pairs_deck().map(move |p2| (p1, p2)))
.for_each(|(p1, p2)| { .for_each(|(p1, p2)| {
// TEST: For any two valid pairs we expect them to have the // TEST: For any two valid pairs we expect them to have the
// `expected_ordering_relation`. // `expected_ordering_relation`.
@@ -227,7 +217,7 @@ mod tests {
#[test] #[test]
fn footstool() { fn footstool() {
exhaustive_pairs_deck() exhaustive_pairs_deck()
.flat_map(|p1| exhaustive_pairs_deck().map(move |p2| (p1, p2))) .zip_cartesian(exhaustive_pairs_deck())
.for_each(|(p1, p2)| { .for_each(|(p1, p2)| {
// TEST: Expected footstool condition // TEST: Expected footstool condition
test_footstool(&p1, &p2); test_footstool(&p1, &p2);