card:impls:rank: new iter_rest function

This commit is contained in:
2026-04-16 11:44:48 +01:00
committed by oreodave
parent 980c7ca58b
commit f1d26acb0b

View File

@@ -7,41 +7,34 @@ 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() -> [Rank; 13] { pub fn all() -> [Rank; 13] {
[ std::array::from_fn(|i| Rank::try_from(i as i64).unwrap())
Rank::Three, }
Rank::Four,
Rank::Five, /// Generate an iterator over all ranks after the current one.
Rank::Six, pub fn iter_rest(self) -> impl ExactSizeIterator<Item = Rank> + Clone {
Rank::Seven, (((self as i32) + 1)..13).map(|x| Rank::try_from(x as i64).unwrap())
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) -> [Card; 4] { pub fn cards(self) -> [Card; 4] {
Suit::iter_all().map(move |suit| Card::make_playing_card(0, self, suit)) let n = self as usize;
std::array::from_fn(|x| Card::from((x + (n * 4)) as i64))
} }
} }
impl Suit { impl Suit {
/// Generate an iterator over all suits. /// Generate an iterator over all suits.
pub fn iter_all() -> [Suit; 4] { pub fn iter_all() -> [Suit; 4] {
[Suit::Diamond, Suit::Club, Suit::Heart, Suit::Spade] std::array::from_fn(|i| Suit::try_from(i as i64).unwrap())
} }
/// 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 w.r.t. deck (0).
pub fn cards(self) -> [Card; 13] { pub fn cards(self) -> [Card; 13] {
Rank::iter_all().map(move |rank| Card::make_playing_card(0, rank, self)) let n = self as usize;
std::array::from_fn(|x| Card::from((n + (x * 4)) as i64))
} }
} }
@@ -50,6 +43,12 @@ impl PlayingCard {
Self { deck, rank, suit } Self { deck, rank, suit }
} }
/// Return the index of this playing card in [0, 52), ignoring the deck it
/// belongs to.
///
/// This means any two playing cards from different decks may be equivalent
/// under PlayingCard::abs if their ranks and suits match, which is used in
/// the Ordering implementation [[file:ord.rs::impl Ord for PlayingCard {]].
pub fn abs(&self) -> i64 { pub fn abs(&self) -> i64 {
let rank = self.rank as i64; let rank = self.rank as i64;
let suit = self.suit as i64; let suit = self.suit as i64;
@@ -59,11 +58,10 @@ 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) -> [PlayingCard; 52] { pub fn iter_all(n: i64) -> [PlayingCard; 52] {
let mut cards: [PlayingCard; 52] = [PlayingCard::default(); 52]; assert!(n >= 0 && (n * 53) < i64::MAX);
for i in 0..52 { std::array::from_fn(|x| {
cards[i] = PlayingCard::try_from((i as i64) + (52 * n)).unwrap(); PlayingCard::try_from((x as i64) + (n * 52)).unwrap()
} })
cards
} }
} }
@@ -87,12 +85,16 @@ impl Card {
} }
} }
/// Return the rank of the current Card. Returns None iff the current Card
/// is a Joker.
pub fn rank(&self) -> Option<Rank> { pub fn rank(&self) -> Option<Rank> {
self.playing_card().and_then(|pc| Some(pc.rank)) self.playing_card().map(|pc| pc.rank)
} }
/// Return the suit of the current Card. Returns None iff the current Card
/// is a Joker.
pub fn suit(&self) -> Option<Suit> { pub fn suit(&self) -> Option<Suit> {
self.playing_card().and_then(|pc| Some(pc.suit)) self.playing_card().map(|pc| pc.suit)
} }
pub fn deck_abs(&self) -> i64 { pub fn deck_abs(&self) -> i64 {
@@ -102,15 +104,21 @@ impl Card {
} }
} }
/// Generate an iterator over `n` decks of Cards. Each deck is concatenated /// Generate an iterator over `n` decks of Cards. By construction, the
/// together. By construction, each "deck" of the iterator is in ascending /// iterator is ordered.
/// order.
/// ///
/// Note that each deck gets two jokers. /// Note that each deck gets two jokers, so there are 2`n` jokers total.
pub fn iter_all(n: i64) -> impl Iterator<Item = Card> + Clone { pub fn iter_all(n: i64) -> impl Iterator<Item = Card> + Clone {
// NOTE: I cannot make this into an ExactSizeIterator using the i32 // We can't know the compile time size of this because we're taking the
// trick. Chain<ESI, ESI> is not an ESI, nor is FlatMap<T,U,T->U> // number of decks at runtime. So no arrays here.
// (where T and U are ESIs).
// We know the size of this iterator beforehand: it's 54 * n. Because
// of https://github.com/rust-lang/rust/pull/22299, Range<i64> is not an
// ExactSizeIterator => Map<Range<i64>> is not an ESI. Range<i32> is an
// ESI, but we can't safely make the range [-2n, 52n] into a Range<i32>
// because of n being i64. So we have to leave this as a generic
// iterator.
(-(n * 2)..(52 * n)).map(Card::from) (-(n * 2)..(52 * n)).map(Card::from)
} }
} }