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 {
/// Generate an iterator over all ranks.
pub fn iter_all() -> impl ExactSizeIterator<Item = Rank> + Clone {
(0i32..13)
.map(|n| n as i64)
.map(|n| Rank::try_from(n).unwrap())
pub fn iter_all() -> [Rank; 13] {
[
Rank::Three,
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
/// cards are all default initialised w.r.t. deck (0).
pub fn cards(self) -> impl ExactSizeIterator<Item = Card> + Clone {
let n = self as i32;
((n * 4)..((n + 1) * 4)).map(|x| Card::from(x as i64))
pub fn cards(self) -> [Card; 4] {
Suit::iter_all().map(move |suit| Card::make_playing_card(0, self, suit))
}
}
impl Suit {
/// Generate an iterator over all suits.
pub fn iter_all() -> impl ExactSizeIterator<Item = Suit> + Clone {
(0i32..4).map(|n| Suit::try_from(n as i64).unwrap())
pub fn iter_all() -> [Suit; 4] {
[Suit::Diamond, Suit::Club, Suit::Heart, Suit::Spade]
}
/// Generate an iterator over all cards within a suit, ordered by Rank. The
/// 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))
}
}
@@ -47,11 +58,12 @@ impl PlayingCard {
/// Generate an iterator over all Playing Cards in the `nth` deck. By
/// construction this is in ascending order.
pub fn iter_all(n: i64) -> impl ExactSizeIterator<Item = Self> + Clone {
(0i32..52)
.map(|x| x as i64)
.map(move |x| x + (52 * n))
.map(|x| PlayingCard::try_from(x).unwrap())
pub fn iter_all(n: i64) -> [PlayingCard; 52] {
let mut cards: [PlayingCard; 52] = [PlayingCard::default(); 52];
for i in 0..52 {
cards[i] = PlayingCard::try_from((i as i64) + (52 * n)).unwrap();
}
cards
}
}

View File

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

View File

@@ -108,14 +108,18 @@ mod tests {
// TEST: Non pair tests.
for (c1, c2) in Rank::iter_all()
.into_iter()
// Generate tuples (r1, r2) where r1 != r2
.flat_map(|r1| {
Rank::iter_all()
.into_iter()
.filter(move |&r2| r2 != r1)
.map(move |r2| (r1, r2))
})
// 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
let pair = Pair::new(c1, c2);
@@ -150,6 +154,7 @@ mod tests {
;
PlayingCard::iter_all(0)
.map(Card::PlayingCard)
.into_iter()
// Flat Map every Playing Card (c1) into combinations (Card of same
// rank as c1, c1)
.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
for (c1, c2) in
Rank::iter_all().flat_map(|r| r.cards().zip_cartesian(r.cards()))
{
for (c1, c2) in Rank::iter_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.
@@ -189,12 +189,16 @@ mod tests {
// Iterate over all pairs of cards with differing ranks
for (c1, c2) in Rank::iter_all()
.into_iter()
.flat_map(|r1| {
Rank::iter_all()
.into_iter()
.filter(move |&r2| r2 != r1)
.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
// cards
@@ -206,9 +210,13 @@ mod tests {
}
// Iterate over all triples of cards (regardless of rank)
for (c1, (c2, c3)) in PlayingCard::iter_all(0).zip_cartesian(
PlayingCard::iter_all(0).zip_cartesian(PlayingCard::iter_all(0)),
) {
for (c1, (c2, c3)) in
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 trip = Triple::new(c1, c2, c3);