*: small refactors and test changes

This commit is contained in:
2026-04-16 11:45:34 +01:00
committed by oreodave
parent f1d26acb0b
commit 79366d60fe
5 changed files with 63 additions and 54 deletions

View File

@@ -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) => {

View File

@@ -273,11 +273,18 @@ mod test_impls {
#[test]
fn rank() {
let ranks = Rank::iter_all().into_iter().collect::<HashSet<_>>();
let ranks = Rank::all().into_iter().collect::<HashSet<_>>();
// 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::<HashSet<_>>();
// 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::<HashSet<_>>();
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())
{

View File

@@ -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))

View File

@@ -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()
.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)
Rank::all()
.into_iter()
.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::<Vec<_>>();
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);
}

View File

@@ -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<B>(
/// For each x in `self`: for each y in `other`: yield (x, y).
fn zip_cartesian<Other>(
self,
b: B,
) -> impl Iterator<Item = (Self::Item, B::Item)> + Clone
other: Other,
) -> impl Iterator<Item = (Self::Item, Other::Item)> + 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))
})
}
}