Skip to content

Commit

Permalink
refactor Stack to hold values instead of refs
Browse files Browse the repository at this point in the history
  • Loading branch information
bhollier committed Dec 19, 2023
1 parent e7fe6a3 commit bedf859
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 147 deletions.
34 changes: 8 additions & 26 deletions src/card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,19 @@ pub trait Card<const N: usize>: Copy + Clone + Eq + Ord + Hash {
/// A Deck of [Card]s
pub type Deck<C, const N: usize> = [C; N];

/// A "Stack" of [Card] references, usually the [Card]s referencing the elements of a [Deck]
/// A "Stack" of [Card]s
// todo change this to be backed by an array and benchmark
pub type Stack<'d, C> = Vec<&'d C>;
pub type Stack<C> = Vec<C>;

/// Trait extension for adding `from_*` functions to [Stack]
pub trait StackFrom<C> {
/// Convenience function to create a [Stack] from a slice of `C` (usually a [Deck] or slice of [Card]).
/// Intended to be used to construct from a deck, as a stack usually contains reference to cards within a deck
pub trait StackFrom<C: Card<N>, const N: usize> {
/// Convenience function to create a [Stack] from a slice of `C` (usually a [Deck] or slice of [Card])
fn from_slice(cs: &[C]) -> Stack<C> {
cs.iter().collect()
}

/// Convenience function to create a [Stack] from a [Vec<C>]
fn from_vec(cs: &Vec<C>) -> Stack<C> {
Self::from_slice(cs.as_slice())
cs.iter().cloned().collect()
}
}

impl<'d, C> StackFrom<C> for Stack<'d, C> {}
impl<C: Card<N>, const N: usize> StackFrom<C, N> for Stack<C> {}

/// Shuffles the given deck mutably, using [rand::thread_rng()]
pub fn shuffle<C: Card<N>, const N: usize>(d: &mut Deck<C, N>) {
Expand All @@ -49,15 +43,9 @@ pub fn take_n_slice<T>(slice: &[T], n: usize) -> (&[T], &[T]) {
(&slice[0..slice.len() - n], &slice[slice.len() - n..])
}

/// Creates two [Vec] from the given [Vec], as a tuple of the `(remaining, taken)`
pub fn take_n_vec<'a, T>(cs: &Vec<&'a T>, n: usize) -> (Vec<&'a T>, Vec<&'a T>) {
let (rest, cs) = take_n_slice(cs.as_slice(), n);
(rest.iter().cloned().collect(), cs.iter().cloned().collect())
}

/// Creates `n` elements from the given [Vec] and returns it,
/// modifying the given [Vec] in the process
pub fn take_n_vec_mut<'a, T>(cs: &mut Vec<&'a T>, n: usize) -> Vec<&'a T> {
pub fn take_n_vec_mut<T>(cs: &mut Vec<T>, n: usize) -> Vec<T> {
cs.split_off(cs.len() - n)
}

Expand All @@ -67,13 +55,7 @@ pub fn take_one_slice<T>(cs: &[T]) -> (&[T], &T) {
(rest, &cs[0])
}

/// Returns the "top" card and a [Vec] of the remaining elements
pub fn take_one_vec<'a, T>(cs: &Vec<&'a T>) -> (Vec<&'a T>, &'a T) {
let (rest, cs) = take_n_slice(cs.as_slice(), 1);
(rest.iter().cloned().collect(), cs[0])
}

/// Returns the "top" card and removes the element from the given [Vec]
pub fn take_one_vec_mut<'a, T>(cs: &mut Vec<&'a T>) -> &'a T {
pub fn take_one_vec_mut<T>(cs: &mut Vec<T>) -> T {
cs.pop().unwrap()
}
6 changes: 3 additions & 3 deletions src/game_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ use thiserror;
pub trait PileRef: Eq {}

/// Trait for the state of a Solitaire game
pub trait GameState<'d, C: Card<N>, const N: usize, P: PileRef>: Sized + Clone + Eq {
pub trait GameState<C: Card<N>, const N: usize, P: PileRef>: Sized + Clone + Eq {
/// Retrieve a reference to the [Stack] at the given [PileRef]
fn get_stack(&self, p: P) -> Option<&Stack<'d, C>>;
fn get_stack(&self, p: P) -> Option<&Stack<C>>;

/// Retrieve a mutable reference to the [Stack] at the given [PileRef]
fn get_stack_mut(&mut self, p: P) -> Option<&mut Stack<'d, C>>;
fn get_stack_mut(&mut self, p: P) -> Option<&mut Stack<C>>;
}

/// Enum of all the possible errors that may occur while operating on a [GameState]
Expand Down
2 changes: 1 addition & 1 deletion src/std/card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,4 @@ impl solitaire::Card<{ Card::N }> for Card {
pub type Deck = solitaire::Deck<Card, { Card::N }>;

/// Convenience type alias for a [Stack](solitaire::Stack) of [Card]
pub type Stack<'d> = solitaire::Stack<'d, Card>;
pub type Stack = solitaire::Stack<Card>;
71 changes: 43 additions & 28 deletions src/std/game_state.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate as solitaire;
use crate::{Card, GameState, Stack, StackFrom};
use crate::{shuffle, Card, GameState, Stack, StackFrom, Deck};

/// "Standard" solitaire piles
#[derive(Eq, PartialEq, Copy, Clone)]
Expand All @@ -24,55 +24,63 @@ impl solitaire::PileRef for PileRef {}

/// Struct for the initial [GameState] with just the [Stock](PileRef::Stock) pile
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InitialGameState<'d, C: Card<N>, const N: usize> {
pub struct InitialGameState<C: Card<N>, const N: usize> {
/// The stock, see [Stock](PileRef::Stock)
pub stock: Stack<'d, C>,
pub stock: Stack<C>,
}

impl<'d, C: Card<N>, const N: usize> GameState<'d, C, N, PileRef> for InitialGameState<'d, C, N> {
fn get_stack(&self, p: PileRef) -> Option<&Stack<'d, C>> {
impl<C: Card<N>, const N: usize> GameState<C, N, PileRef> for InitialGameState<C, N> {
fn get_stack(&self, p: PileRef) -> Option<&Stack<C>> {
match p {
PileRef::Stock => Some(&self.stock),
_ => None,
}
}

fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<'d, C>> {
fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<C>> {
match p {
PileRef::Stock => Some(&mut self.stock),
_ => None,
}
}
}

impl<'d, C: Card<N>, const N: usize> InitialGameState<'d, C, N> {
pub fn new(deck: &'d [C]) -> InitialGameState<'d, C, N> {
impl<C: Card<N>, const N: usize> InitialGameState<C, N> {
pub fn new() -> InitialGameState<C, N> {
let mut d = C::new_deck();
shuffle(&mut d);
InitialGameState::from(d)
}
}

impl<C: Card<N>, const N: usize> From<Deck<C, N>> for InitialGameState<C, N> {
fn from(d: Deck<C, N>) -> Self {
InitialGameState {
stock: Stack::from_slice(deck),
stock: Stack::from_slice(&d),
}
}
}

/// Struct for a mid-game "playing" [GameState] with four [piles](PileRef) of generic [Card]s
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PlayingGameState<'d, C: Card<NC>, const NC: usize, const NT: usize, const NF: usize> {
pub struct PlayingGameState<C: Card<NC>, const NC: usize, const NT: usize, const NF: usize> {
/// The tableau, see [Tableau](PileRef::Tableau)
pub tableau: [Stack<'d, C>; NT],
pub tableau: [Stack<C>; NT],

/// The foundations, see [Foundation](PileRef::Foundation)
pub foundations: [Stack<'d, C>; NF],
pub foundations: [Stack<C>; NF],

/// The stock, see [Stock](PileRef::Stock)
pub stock: Stack<'d, C>,
pub stock: Stack<C>,

/// The talon, see [Talon](PileRef::Talon)
pub talon: Stack<'d, C>,
pub talon: Stack<C>,
}

impl<'d, C: Card<NC>, const NC: usize, const NT: usize, const NF: usize>
GameState<'d, C, NC, PileRef> for PlayingGameState<'d, C, NC, NT, NF>
impl<C: Card<NC>, const NC: usize, const NT: usize, const NF: usize> GameState<C, NC, PileRef>
for PlayingGameState<C, NC, NT, NF>
{
fn get_stack(&self, p: PileRef) -> Option<&Stack<'d, C>> {
fn get_stack(&self, p: PileRef) -> Option<&Stack<C>> {
match p {
PileRef::Tableau(n) => self.tableau.get(n),
PileRef::Foundation(n) => self.foundations.get(n),
Expand All @@ -81,7 +89,7 @@ impl<'d, C: Card<NC>, const NC: usize, const NT: usize, const NF: usize>
}
}

fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<'d, C>> {
fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<C>> {
match p {
PileRef::Tableau(n) => self.tableau.get_mut(n),
PileRef::Foundation(n) => self.foundations.get_mut(n),
Expand All @@ -93,33 +101,40 @@ impl<'d, C: Card<NC>, const NC: usize, const NT: usize, const NF: usize>

/// Struct for a win [GameState] with just the [Foundation](PileRef::Foundation) piles
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WinGameState<'d, C: Card<NC>, const NC: usize, const NF: usize> {
pub struct WinGameState<C: Card<NC>, const NC: usize, const NF: usize> {
/// The foundations, see [Foundation](PileRef::Foundation)
pub foundations: [Stack<'d, C>; NF],
pub foundations: [Stack<C>; NF],
}

impl<'d, C: Card<NC>, const NC: usize, const NF: usize> GameState<'d, C, NC, PileRef>
for WinGameState<'d, C, NC, NF>
impl<'d, C: Card<NC>, const NC: usize, const NF: usize> GameState<C, NC, PileRef>
for WinGameState<C, NC, NF>
{
fn get_stack(&self, p: PileRef) -> Option<&Stack<'d, C>> {
fn get_stack(&self, p: PileRef) -> Option<&Stack<C>> {
match p {
PileRef::Foundation(n) => self.foundations.get(n),
_ => None,
}
}

fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<'d, C>> {
fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<C>> {
match p {
PileRef::Foundation(n) => self.foundations.get_mut(n),
_ => None,
}
}
}

/// Enum for all possible [GameState]s
pub enum GameStateOption<C: Card<NC>, const NC: usize, const NT: usize, const NF: usize> {
Initial(InitialGameState<C, NC>),
Playing(PlayingGameState<C, NC, NT, NF>),
Win(WinGameState<C, NC, NF>),
}

/// Enum for the resulting [GameState] after making a move,
/// either [Playing] (game not finished) or [Win]
/// either [Playing](PlayingGameState) (game not finished) or [Win](WinGameState)
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum MoveResult<'d, C: Card<NC>, const NC: usize, const NT: usize, const NF: usize> {
Playing(PlayingGameState<'d, C, NC, NT, NF>),
Win(WinGameState<'d, C, NC, NF>),
pub enum MoveResult<C: Card<NC>, const NC: usize, const NT: usize, const NF: usize> {
Playing(PlayingGameState<C, NC, NT, NF>),
Win(WinGameState<C, NC, NF>),
}
29 changes: 16 additions & 13 deletions src/variant/klondike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@ pub const NUM_TABLEAU: usize = 7;
pub const NUM_FOUNDATIONS: usize = std::FrenchSuit::N;

/// The initial [GameState] for Klondike Solitaire with [std::Card]
pub type InitialGameState<'d> = std::InitialGameState<'d, std::Card, { std::Card::N }>;
pub type InitialGameState = std::InitialGameState<std::Card, { std::Card::N }>;

/// The mid-game "playing" [GameState] for Klondike Solitaire with [std::Card]
pub type PlayingGameState<'d> =
std::PlayingGameState<'d, std::Card, { std::Card::N }, NUM_TABLEAU, NUM_FOUNDATIONS>;
pub type PlayingGameState =
std::PlayingGameState<std::Card, { std::Card::N }, NUM_TABLEAU, NUM_FOUNDATIONS>;

/// The win [GameState] for Klondike Solitaire with [std::Card]
pub type WinGameState<'d> = std::WinGameState<'d, std::Card, { std::Card::N }, NUM_FOUNDATIONS>;
pub type WinGameState = std::WinGameState<std::Card, { std::Card::N }, NUM_FOUNDATIONS>;

/// Enum for all possible [GameState]s, for Klondike Solitaire with [std::Card]
pub type GameStateOption =
std::GameStateOption<std::Card, { std::Card::N }, NUM_TABLEAU, NUM_FOUNDATIONS>;

/// Enum for the resulting [GameState] after making a move,
/// for Klondike Solitaire with [std::Card]
pub type MoveResult<'d> =
std::MoveResult<'d, std::Card, { std::Card::N }, NUM_TABLEAU, NUM_FOUNDATIONS>;
pub type MoveResult = std::MoveResult<std::Card, { std::Card::N }, NUM_TABLEAU, NUM_FOUNDATIONS>;

/// The Game rules for Klondike Solitaire
pub struct GameRules;
Expand All @@ -37,7 +40,7 @@ impl GameRules {
talon: Stack::new(),
};

let mut card: &std::Card;
let mut card: std::Card;
for i in 0..NUM_TABLEAU {
for j in i..NUM_TABLEAU {
card = take_one_vec_mut(&mut new_state.stock);
Expand All @@ -50,8 +53,8 @@ impl GameRules {

/// Convenience function to create a new [InitialGameState]
/// and then deal the cards with [deal](Self::deal)
pub fn new_and_deal(d: &[std::Card]) -> PlayingGameState {
Self::deal(InitialGameState::new(d))
pub fn new_and_deal() -> PlayingGameState {
Self::deal(InitialGameState::new())
}

/// Draws `n` cards from the [Stock](PileRef::Stock) onto the [Talon](PileRef::Talon).
Expand All @@ -75,7 +78,7 @@ impl GameRules {
// Take the cards from the stock
let take = take_n_vec_mut(&mut new_state.stock, n);
// Transfer to the talon
take.iter().for_each(|c| new_state.talon.push(*c));
take.iter().cloned().for_each(|c| new_state.talon.push(c));
}
}
Ok(new_state)
Expand All @@ -87,10 +90,10 @@ impl GameRules {
/// - [Tableau](PileRef::Tableau): cards must be of alternating [Color](std::Color) and in King to Ace order
/// - [Stock](PileRef::Stock): always true
/// - [Talon](PileRef::Talon): always true
pub fn valid_seq(p: PileRef, cs: &[&std::Card]) -> bool {
pub fn valid_seq(p: PileRef, cs: &[std::Card]) -> bool {
match p {
PileRef::Tableau(_) => {
let mut prev_card = cs[0];
let mut prev_card = &cs[0];
for card in &cs[1..cs.len()] {
if card.suit.color() == prev_card.suit.color() {
return false;
Expand All @@ -103,7 +106,7 @@ impl GameRules {
return true;
}
PileRef::Foundation(_) => {
let mut prev_card = cs[0];
let mut prev_card = &cs[0];
for card in &cs[1..cs.len()] {
if card.suit != prev_card.suit {
return false;
Expand Down
20 changes: 4 additions & 16 deletions tests/card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,11 @@ fn test_take_n() {
assert_eq!(cs, &d[42..52]);
}

{
let (rest, cs) = take_n_vec(&Stack::from_slice(&d), 10);
assert_eq!(rest, d[..42].iter().collect::<std::Stack>());
assert_eq!(cs, d[42..52].iter().collect::<std::Stack>());
}

{
let mut d_vec = Stack::from_slice(&d);
let cs = take_n_vec_mut(&mut d_vec, 10);
assert_eq!(d_vec, d[..42].iter().collect::<std::Stack>());
assert_eq!(cs, d[42..52].iter().collect::<std::Stack>());
assert_eq!(d_vec, d[..42]);
assert_eq!(cs, d[42..52]);
}
}

Expand All @@ -33,16 +27,10 @@ fn test_take_one() {
assert_eq!(c, &d[51]);
}

{
let (rest, c) = take_one_vec(&Stack::from_slice(&d));
assert_eq!(rest, d[..51].iter().collect::<std::Stack>());
assert_eq!(c, &d[51]);
}

{
let mut d_vec: std::Stack = Stack::from_slice(&d);
let c = take_one_vec_mut(&mut d_vec);
assert_eq!(d_vec, d[..51].iter().collect::<std::Stack>());
assert_eq!(c, &d[51]);
assert_eq!(d_vec, d[..51]);
assert_eq!(c, d[51]);
}
}
Loading

0 comments on commit bedf859

Please sign in to comment.