refactor CardValue into Rank enum
This commit is contained in:
+95
-45
@@ -16,6 +16,27 @@ pub trait Game {
|
|||||||
fn is_win(&self) -> bool;
|
fn is_win(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// card_game supports up to 4 identifiably separate decks.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub enum Deck {
|
||||||
|
Deck1,
|
||||||
|
Deck2,
|
||||||
|
Deck3,
|
||||||
|
Deck4,
|
||||||
|
}
|
||||||
|
impl Deck {
|
||||||
|
pub const fn new(deck: u8) -> Option<Self> {
|
||||||
|
use Deck::*;
|
||||||
|
Some(match deck {
|
||||||
|
1 => Deck1,
|
||||||
|
2 => Deck2,
|
||||||
|
3 => Deck3,
|
||||||
|
4 => Deck4,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
pub enum Suit {
|
pub enum Suit {
|
||||||
Spades = 0b00,
|
Spades = 0b00,
|
||||||
@@ -36,38 +57,66 @@ impl Suit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct CardValue(u8);
|
pub enum Rank {
|
||||||
impl CardValue {
|
Ace = 1,
|
||||||
pub const ACE: Self = CardValue(1);
|
Two = 2,
|
||||||
pub const TWO: Self = CardValue(2);
|
Three = 3,
|
||||||
pub const THREE: Self = CardValue(3);
|
Four = 4,
|
||||||
pub const FOUR: Self = CardValue(4);
|
Five = 5,
|
||||||
pub const FIVE: Self = CardValue(5);
|
Six = 6,
|
||||||
pub const SIX: Self = CardValue(6);
|
Seven = 7,
|
||||||
pub const SEVEN: Self = CardValue(7);
|
Eight = 8,
|
||||||
pub const EIGHT: Self = CardValue(8);
|
Nine = 9,
|
||||||
pub const NINE: Self = CardValue(9);
|
Ten = 10,
|
||||||
pub const TEN: Self = CardValue(10);
|
Jack = 11,
|
||||||
pub const JACK: Self = CardValue(11);
|
Queen = 12,
|
||||||
pub const QUEEN: Self = CardValue(12);
|
King = 13,
|
||||||
pub const KING: Self = CardValue(13);
|
|
||||||
pub fn get(self) -> u8 {
|
|
||||||
self.0
|
|
||||||
}
|
}
|
||||||
pub fn checked_add(self, offset: u8) -> Option<CardValue> {
|
impl Rank {
|
||||||
let new_value = self.0.checked_add(offset)?;
|
pub const RANKS: [Self; 13] = [
|
||||||
if 13 < new_value {
|
Self::Ace,
|
||||||
None
|
Self::Two,
|
||||||
} else {
|
Self::Three,
|
||||||
Some(CardValue(new_value))
|
Self::Four,
|
||||||
|
Self::Five,
|
||||||
|
Self::Six,
|
||||||
|
Self::Seven,
|
||||||
|
Self::Eight,
|
||||||
|
Self::Nine,
|
||||||
|
Self::Ten,
|
||||||
|
Self::Jack,
|
||||||
|
Self::Queen,
|
||||||
|
Self::King,
|
||||||
|
];
|
||||||
|
pub const fn new(rank: u8) -> Option<Self> {
|
||||||
|
use Rank::*;
|
||||||
|
Some(match rank {
|
||||||
|
1 => Ace,
|
||||||
|
2 => Two,
|
||||||
|
3 => Three,
|
||||||
|
4 => Four,
|
||||||
|
5 => Five,
|
||||||
|
6 => Six,
|
||||||
|
7 => Seven,
|
||||||
|
8 => Eight,
|
||||||
|
9 => Nine,
|
||||||
|
10 => Ten,
|
||||||
|
11 => Jack,
|
||||||
|
12 => Queen,
|
||||||
|
13 => King,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub const fn checked_add(self, offset: u8) -> Option<Rank> {
|
||||||
|
match (self as u8).checked_add(offset) {
|
||||||
|
Some(rank) => Self::new(rank),
|
||||||
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn checked_sub(self, offset: u8) -> Option<CardValue> {
|
pub const fn checked_sub(self, offset: u8) -> Option<Rank> {
|
||||||
let new_value = self.0.checked_sub(offset)?;
|
match (self as u8).checked_sub(offset) {
|
||||||
if new_value < 1 {
|
Some(rank) => Self::new(rank),
|
||||||
None
|
None => None,
|
||||||
} else {
|
|
||||||
Some(CardValue(new_value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,16 +126,17 @@ impl CardValue {
|
|||||||
/// 4 bits for card Value
|
/// 4 bits for card Value
|
||||||
/// TODO: better encoding for slightly more decks
|
/// TODO: better encoding for slightly more decks
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct Card(u8);
|
pub struct Card(core::num::NonZeroU8);
|
||||||
impl Card {
|
impl Card {
|
||||||
pub fn new(deck: u8, suit: Suit, CardValue(value): CardValue) -> Self {
|
pub const fn new(deck: Deck, suit: Suit, rank: Rank) -> Self {
|
||||||
Self(deck << 6 | (suit as u8) << 4 | value)
|
let packed = (deck as u8) << 6 | (suit as u8) << 4 | (rank as u8);
|
||||||
|
Self(core::num::NonZeroU8::new(packed).unwrap())
|
||||||
}
|
}
|
||||||
pub fn value(&self) -> CardValue {
|
pub const fn rank(&self) -> Rank {
|
||||||
let masked = self.0 & 0b1111;
|
let masked = self.0.get() & 0b1111;
|
||||||
CardValue(masked)
|
Rank::new(masked).unwrap()
|
||||||
}
|
}
|
||||||
pub fn suit(&self) -> Suit {
|
pub const fn suit(&self) -> Suit {
|
||||||
let red = self.is_red();
|
let red = self.is_red();
|
||||||
let kiki = self.is_kiki();
|
let kiki = self.is_kiki();
|
||||||
match (kiki, red) {
|
match (kiki, red) {
|
||||||
@@ -97,15 +147,15 @@ impl Card {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the suit red.
|
/// Is the suit red.
|
||||||
pub fn is_red(&self) -> bool {
|
pub const fn is_red(&self) -> bool {
|
||||||
self.0 & 0b010000 != 0
|
self.0.get() & 0b010000 != 0
|
||||||
}
|
}
|
||||||
/// Is the suit shape spikey. (Bouba/kiki)
|
/// Is the suit shape spikey. (Bouba/kiki)
|
||||||
pub fn is_kiki(&self) -> bool {
|
pub const fn is_kiki(&self) -> bool {
|
||||||
self.0 & 0b100000 != 0
|
self.0.get() & 0b100000 != 0
|
||||||
}
|
}
|
||||||
pub fn deck(&self) -> u8 {
|
pub const fn deck(&self) -> Deck {
|
||||||
self.0 >> 6
|
Deck::new(self.0.get() >> 6).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,11 +171,11 @@ impl<const CAP: usize> Stack<CAP> {
|
|||||||
}
|
}
|
||||||
impl Stack<52> {
|
impl Stack<52> {
|
||||||
/// Generate a full deck of cards with the specified deck id.
|
/// Generate a full deck of cards with the specified deck id.
|
||||||
pub fn full_deck(deck: u8) -> Self {
|
pub fn full_deck(deck: Deck) -> Self {
|
||||||
let mut stack = arrayvec::ArrayVec::new();
|
let mut stack = arrayvec::ArrayVec::new();
|
||||||
for suit in Suit::SUITS {
|
for suit in Suit::SUITS {
|
||||||
for value in 1..=13 {
|
for rank in Rank::RANKS {
|
||||||
stack.push(Card::new(deck, suit, CardValue(value)));
|
stack.push(Card::new(deck, suit, rank));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stack(stack)
|
Stack(stack)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use card_game::{Card, CardValue, Game, Pile, Session, SessionStats, Suit};
|
use card_game::{Card, Game, Pile, Rank, Session, SessionStats, Suit};
|
||||||
use klondike::{
|
use klondike::{
|
||||||
DstFoundation, DstTableau, Foundation, Klondike, KlondikeConfig, KlondikeInstruction,
|
DstFoundation, DstTableau, Foundation, Klondike, KlondikeConfig, KlondikeInstruction,
|
||||||
KlondikePile, KlondikePileStack, KlondikeStats, SkipCards, Tableau, TableauStack,
|
KlondikePile, KlondikePileStack, KlondikeStats, SkipCards, Tableau, TableauStack,
|
||||||
@@ -9,12 +9,12 @@ struct Displayed<T>(T);
|
|||||||
|
|
||||||
impl Display for Displayed<&Card> {
|
impl Display for Displayed<&Card> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self.0.value().get() {
|
match self.0.rank() {
|
||||||
1 => write!(f, "A"),
|
Rank::Ace => write!(f, "A"),
|
||||||
11 => write!(f, "J"),
|
Rank::Jack => write!(f, "J"),
|
||||||
12 => write!(f, "Q"),
|
Rank::Queen => write!(f, "Q"),
|
||||||
13 => write!(f, "K"),
|
Rank::King => write!(f, "K"),
|
||||||
other => write!(f, "{other}"),
|
other => write!(f, "{}", other as u8),
|
||||||
}?;
|
}?;
|
||||||
match self.0.suit() {
|
match self.0.suit() {
|
||||||
Suit::Spades => write!(f, "♠"),
|
Suit::Spades => write!(f, "♠"),
|
||||||
@@ -236,7 +236,7 @@ fn get_good_move(state: &Klondike) -> Option<KlondikeInstruction> {
|
|||||||
|| state
|
|| state
|
||||||
.state()
|
.state()
|
||||||
.stack_bottom_card(dst_tableau.src)
|
.stack_bottom_card(dst_tableau.src)
|
||||||
.is_some_and(|card| card.value() != CardValue::KING) =>
|
.is_some_and(|card| card.rank() != Rank::King) =>
|
||||||
{
|
{
|
||||||
2
|
2
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
pub type Rng = rand::rngs::ThreadRng;
|
pub type Rng = rand::rngs::ThreadRng;
|
||||||
|
|
||||||
use card_game::{Card, CardValue, Game, Pile, Stack};
|
use card_game::{Card, Game, Pile, Rank, Stack};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
@@ -475,10 +475,10 @@ impl KlondikeState {
|
|||||||
// suit matches?
|
// suit matches?
|
||||||
src_card.suit() == dst_card.suit()
|
src_card.suit() == dst_card.suit()
|
||||||
// value is +1?
|
// value is +1?
|
||||||
&& dst_card.value().checked_add(1) == Some(src_card.value())
|
&& dst_card.rank().checked_add(1) == Some(src_card.rank())
|
||||||
}
|
}
|
||||||
// only ace is allowed to go onto empty foundation
|
// only ace is allowed to go onto empty foundation
|
||||||
None => src_card.value() == CardValue::ACE,
|
None => src_card.rank() == Rank::Ace,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@@ -494,10 +494,10 @@ impl KlondikeState {
|
|||||||
// red-ness is opposite?
|
// red-ness is opposite?
|
||||||
src_card.is_red() != dst_card.is_red()
|
src_card.is_red() != dst_card.is_red()
|
||||||
// value is -1?
|
// value is -1?
|
||||||
&& dst_card.value().checked_sub(1) == Some(src_card.value())
|
&& dst_card.rank().checked_sub(1) == Some(src_card.rank())
|
||||||
}
|
}
|
||||||
// only king is allowed to go onto empty tableau
|
// only king is allowed to go onto empty tableau
|
||||||
None => src_card.value() == CardValue::KING,
|
None => src_card.rank() == Rank::King,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@@ -536,7 +536,7 @@ impl Klondike {
|
|||||||
}
|
}
|
||||||
pub fn new(mut seed: Rng) -> Self {
|
pub fn new(mut seed: Rng) -> Self {
|
||||||
// shuffle a new deck
|
// shuffle a new deck
|
||||||
let mut deck = Stack::full_deck(0);
|
let mut deck = Stack::full_deck(card_game::Deck::Deck1);
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
deck.shuffle(&mut seed);
|
deck.shuffle(&mut seed);
|
||||||
let mut deck = deck.into_iter();
|
let mut deck = deck.into_iter();
|
||||||
|
|||||||
Reference in New Issue
Block a user