diff --git a/src/card/impls.rs b/src/card/impls.rs index ed5922a..0bea06a 100644 --- a/src/card/impls.rs +++ b/src/card/impls.rs @@ -7,41 +7,34 @@ ExactSizeIterator => Map> is not an ESI. But Range is an ESI. impl Rank { /// Generate an iterator over all ranks. - 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, - ] + pub fn all() -> [Rank; 13] { + std::array::from_fn(|i| Rank::try_from(i as i64).unwrap()) + } + + /// Generate an iterator over all ranks after the current one. + pub fn iter_rest(self) -> impl ExactSizeIterator + Clone { + (((self as i32) + 1)..13).map(|x| Rank::try_from(x as i64).unwrap()) } /// 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) -> [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 { /// Generate an iterator over all suits. 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 - /// 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] { - 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 } } + /// 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 { let rank = self.rank 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 /// construction this is in ascending order. 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 + assert!(n >= 0 && (n * 53) < i64::MAX); + std::array::from_fn(|x| { + PlayingCard::try_from((x as i64) + (n * 52)).unwrap() + }) } } @@ -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 { - 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 { - self.playing_card().and_then(|pc| Some(pc.suit)) + self.playing_card().map(|pc| pc.suit) } pub fn deck_abs(&self) -> i64 { @@ -102,15 +104,21 @@ impl Card { } } - /// Generate an iterator over `n` decks of Cards. Each deck is concatenated - /// together. By construction, each "deck" of the iterator is in ascending - /// order. + /// Generate an iterator over `n` decks of Cards. By construction, the + /// iterator is ordered. /// - /// 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 + Clone { - // NOTE: I cannot make this into an ExactSizeIterator using the i32 - // trick. Chain is not an ESI, nor is FlatMapU> - // (where T and U are ESIs). + // We can't know the compile time size of this because we're taking the + // number of decks at runtime. So no arrays here. + + // We know the size of this iterator beforehand: it's 54 * n. Because + // of https://github.com/rust-lang/rust/pull/22299, Range is not an + // ExactSizeIterator => Map> is not an ESI. Range is an + // ESI, but we can't safely make the range [-2n, 52n] into a Range + // because of n being i64. So we have to leave this as a generic + // iterator. + (-(n * 2)..(52 * n)).map(Card::from) } }