card:impls: *::iter_all and *::cards now return arrays

We know the sizes of the results of these functions beforehand.
Fitting it into an ESI was a code smell really.  Just return the
arrays!  Of course, now we need to do array::into_iter for iterator
use, but this is way better.

Excluding Card::iter_all since we can't know the size of it at compile
time.  PITA that we can't make it an ESI either.
This commit is contained in:
2026-04-14 21:21:14 +01:00
committed by oreodave
parent 0bab322f3a
commit e72f8b2c3a
4 changed files with 59 additions and 38 deletions

View File

@@ -7,29 +7,40 @@ ExactSizeIterator => Map<Range<i64>> is not an ESI. But Range<i32> is an ESI.
impl Rank { impl Rank {
/// Generate an iterator over all ranks. /// Generate an iterator over all ranks.
pub fn iter_all() -> impl ExactSizeIterator<Item = Rank> + Clone { pub fn iter_all() -> [Rank; 13] {
(0i32..13) [
.map(|n| n as i64) Rank::Three,
.map(|n| Rank::try_from(n).unwrap()) Rank::Four,
Rank::Five,
Rank::Six,
Rank::Seven,
Rank::Eight,
Rank::Nine,
Rank::Ten,
Rank::Jack,
Rank::Queen,
Rank::King,
Rank::Ace,
Rank::Two,
]
} }
/// Generate an iterator over all cards within a rank, ordered by Suit. The /// Generate an iterator over all cards within a rank, ordered by Suit. The
/// cards are all default initialised w.r.t. deck (0). /// cards are all default initialised w.r.t. deck (0).
pub fn cards(self) -> impl ExactSizeIterator<Item = Card> + Clone { pub fn cards(self) -> [Card; 4] {
let n = self as i32; Suit::iter_all().map(move |suit| Card::make_playing_card(0, self, suit))
((n * 4)..((n + 1) * 4)).map(|x| Card::from(x as i64))
} }
} }
impl Suit { impl Suit {
/// Generate an iterator over all suits. /// Generate an iterator over all suits.
pub fn iter_all() -> impl ExactSizeIterator<Item = Suit> + Clone { pub fn iter_all() -> [Suit; 4] {
(0i32..4).map(|n| Suit::try_from(n as i64).unwrap()) [Suit::Diamond, Suit::Club, Suit::Heart, Suit::Spade]
} }
/// Generate an iterator over all cards within a suit, ordered by Rank. The /// Generate an iterator over all cards within a suit, ordered by Rank. The
/// cards are all default initialised in terms of deck (0). /// cards are all default initialised in terms of deck (0).
pub fn cards(self) -> impl ExactSizeIterator<Item = Card> + Clone { pub fn cards(self) -> [Card; 13] {
Rank::iter_all().map(move |rank| Card::make_playing_card(0, rank, self)) Rank::iter_all().map(move |rank| Card::make_playing_card(0, rank, self))
} }
} }
@@ -47,11 +58,12 @@ impl PlayingCard {
/// Generate an iterator over all Playing Cards in the `nth` deck. By /// Generate an iterator over all Playing Cards in the `nth` deck. By
/// construction this is in ascending order. /// construction this is in ascending order.
pub fn iter_all(n: i64) -> impl ExactSizeIterator<Item = Self> + Clone { pub fn iter_all(n: i64) -> [PlayingCard; 52] {
(0i32..52) let mut cards: [PlayingCard; 52] = [PlayingCard::default(); 52];
.map(|x| x as i64) for i in 0..52 {
.map(move |x| x + (52 * n)) cards[i] = PlayingCard::try_from((i as i64) + (52 * n)).unwrap();
.map(|x| PlayingCard::try_from(x).unwrap()) }
cards
} }
} }

View File

@@ -268,18 +268,17 @@ mod test_impls {
use crate::{ use crate::{
card::{Card, PlayingCard, Rank, Suit}, card::{Card, PlayingCard, Rank, Suit},
exactsizearr::ExactSizedArr,
zipcartesian::ZipCartesianExt, zipcartesian::ZipCartesianExt,
}; };
#[test] #[test]
fn rank() { fn rank() {
let ranks = Rank::iter_all().collect::<HashSet<_>>(); let ranks = Rank::iter_all().into_iter().collect::<HashSet<_>>();
// TEST: Rank::iter_all produces all 13 unique ranks. // TEST: Rank::iter_all produces all 13 unique ranks.
assert_eq!(ranks.len(), 13); assert_eq!(ranks.len(), 13);
for rank in Rank::iter_all() { for rank in Rank::iter_all() {
let cards = rank.cards().collect::<HashSet<_>>(); let cards = rank.cards().into_iter().collect::<HashSet<_>>();
assert_eq!(cards.len(), 4, "Expected 4 cards per rank"); assert_eq!(cards.len(), 4, "Expected 4 cards per rank");
for c in cards { for c in cards {
// TEST: rank.cards() generates Playing Cards of the same rank // TEST: rank.cards() generates Playing Cards of the same rank
@@ -304,12 +303,12 @@ mod test_impls {
#[test] #[test]
fn suit() { fn suit() {
let suits = Suit::iter_all().collect::<HashSet<_>>(); let suits = Suit::iter_all().into_iter().collect::<HashSet<_>>();
// TEST: Suit::iter_all produces all 4 unique suits. // TEST: Suit::iter_all produces all 4 unique suits.
assert_eq!(suits.len(), 4); assert_eq!(suits.len(), 4);
for suit in Suit::iter_all() { for suit in Suit::iter_all() {
let cards = suit.cards().collect::<HashSet<_>>(); let cards = suit.cards().into_iter().collect::<HashSet<_>>();
assert_eq!(cards.len(), 13, "Expected 13 cards per suit"); assert_eq!(cards.len(), 13, "Expected 13 cards per suit");
for c in cards { for c in cards {
@@ -331,8 +330,9 @@ mod test_impls {
#[test] #[test]
fn playing_card() { fn playing_card() {
for deck in 0..10 { for deck in 0..10 {
let playing_cards = let playing_cards = PlayingCard::iter_all(deck)
PlayingCard::iter_all(deck).collect::<HashSet<_>>(); .into_iter()
.collect::<HashSet<_>>();
// TEST: Expected 52 cards to be generated by PlayingCard::iter_all. // TEST: Expected 52 cards to be generated by PlayingCard::iter_all.
assert_eq!( assert_eq!(
@@ -341,13 +341,7 @@ mod test_impls {
"Expected 52 cards in a playing card deck" "Expected 52 cards in a playing card deck"
); );
for card in PlayingCard::iter_all(deck) for card in PlayingCard::iter_all(deck) {
.into_array::<52>()
.unwrap_or_else(|_| {
unreachable!(
"Look at previous assertion; there must be 52 cards in the iterator."
)
}) {
// TEST: card.deck must match the input deck // TEST: card.deck must match the input deck
assert_eq!(card.deck, deck); assert_eq!(card.deck, deck);
let numeral = i64::from(card); let numeral = i64::from(card);
@@ -414,7 +408,9 @@ mod test_impls {
counter counter
}; };
for (rank, suit) in Rank::iter_all().zip_cartesian(Suit::iter_all()) for (rank, suit) in Rank::iter_all()
.into_iter()
.zip_cartesian(Suit::iter_all().into_iter())
{ {
// TEST: We expect `decks` instances of a (rank, suit) // TEST: We expect `decks` instances of a (rank, suit)
// combination in Card::iter_all(decks). // combination in Card::iter_all(decks).

View File

@@ -108,14 +108,18 @@ mod tests {
// TEST: Non pair tests. // TEST: Non pair tests.
for (c1, c2) in Rank::iter_all() for (c1, c2) in Rank::iter_all()
.into_iter()
// Generate tuples (r1, r2) where r1 != r2 // Generate tuples (r1, r2) where r1 != r2
.flat_map(|r1| { .flat_map(|r1| {
Rank::iter_all() Rank::iter_all()
.into_iter()
.filter(move |&r2| r2 != r1) .filter(move |&r2| r2 != r1)
.map(move |r2| (r1, r2)) .map(move |r2| (r1, r2))
}) })
// Generate all cards where their ranks differ // Generate all cards where their ranks differ
.flat_map(|(r1, r2)| r1.cards().zip_cartesian(r2.cards())) .flat_map(|(r1, r2)| {
r1.cards().into_iter().zip_cartesian(r2.cards().into_iter())
})
{ {
// TEST: Two cards of differing rank cannot be a pair // TEST: Two cards of differing rank cannot be a pair
let pair = Pair::new(c1, c2); let pair = Pair::new(c1, c2);
@@ -150,6 +154,7 @@ mod tests {
; ;
PlayingCard::iter_all(0) PlayingCard::iter_all(0)
.map(Card::PlayingCard) .map(Card::PlayingCard)
.into_iter()
// Flat Map every Playing Card (c1) into combinations (Card of same // Flat Map every Playing Card (c1) into combinations (Card of same
// rank as c1, c1) // rank as c1, c1)
.flat_map(|c1| c1.rank().unwrap().cards().map(move |c2| (c1, c2))) .flat_map(|c1| c1.rank().unwrap().cards().map(move |c2| (c1, c2)))

View File

@@ -151,9 +151,9 @@ mod tests {
} }
// Iterate over all pairs of cards with similar ranks // Iterate over all pairs of cards with similar ranks
for (c1, c2) in for (c1, c2) in Rank::iter_all().into_iter().flat_map(|r| {
Rank::iter_all().flat_map(|r| r.cards().zip_cartesian(r.cards())) r.cards().into_iter().zip_cartesian(r.cards().into_iter())
{ }) {
let trip = Triple::new(c1, c2, joker); let trip = Triple::new(c1, c2, joker);
// TEST: Any two similar rank cards with 1 joker are a // TEST: Any two similar rank cards with 1 joker are a
// Triple. // Triple.
@@ -189,12 +189,16 @@ mod tests {
// Iterate over all pairs of cards with differing ranks // Iterate over all pairs of cards with differing ranks
for (c1, c2) in Rank::iter_all() for (c1, c2) in Rank::iter_all()
.into_iter()
.flat_map(|r1| { .flat_map(|r1| {
Rank::iter_all() Rank::iter_all()
.into_iter()
.filter(move |&r2| r2 != r1) .filter(move |&r2| r2 != r1)
.map(move |r2| (r1, r2)) .map(move |r2| (r1, r2))
}) })
.flat_map(|(r1, r2)| r1.cards().zip_cartesian(r2.cards())) .flat_map(|(r1, r2)| {
r1.cards().into_iter().zip_cartesian(r2.cards().into_iter())
})
{ {
// TEST: Cannot make a triple out of 1 joker and two different rank // TEST: Cannot make a triple out of 1 joker and two different rank
// cards // cards
@@ -206,9 +210,13 @@ mod tests {
} }
// Iterate over all triples of cards (regardless of rank) // Iterate over all triples of cards (regardless of rank)
for (c1, (c2, c3)) in PlayingCard::iter_all(0).zip_cartesian( for (c1, (c2, c3)) in
PlayingCard::iter_all(0).zip_cartesian(PlayingCard::iter_all(0)), PlayingCard::iter_all(0).into_iter().zip_cartesian(
) { PlayingCard::iter_all(0)
.into_iter()
.zip_cartesian(PlayingCard::iter_all(0).into_iter()),
)
{
let [c1, c2, c3] = [c1, c2, c3].map(Card::PlayingCard); let [c1, c2, c3] = [c1, c2, c3].map(Card::PlayingCard);
let trip = Triple::new(c1, c2, c3); let trip = Triple::new(c1, c2, c3);