Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 68e891d3b7 | |||
| d800e9d33e |
+38
-17
@@ -1,3 +1,5 @@
|
||||
use core::ops::RangeBounds;
|
||||
|
||||
// TODO: pub struct ValidInstruction<I>(I);
|
||||
pub trait Game {
|
||||
type Instruction;
|
||||
@@ -103,8 +105,11 @@ impl Card {
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Stack<const CAP: usize>(arrayvec::ArrayVec<Card, CAP>);
|
||||
impl<const CAP: usize> Stack<CAP> {
|
||||
pub fn new() -> Self {
|
||||
Self(arrayvec::ArrayVec::new())
|
||||
pub const fn new() -> Self {
|
||||
Self(arrayvec::ArrayVec::new_const())
|
||||
}
|
||||
pub fn take_range<R: RangeBounds<usize>>(&mut self, range: R) -> Self {
|
||||
Stack::from_iter(self.drain(range))
|
||||
}
|
||||
}
|
||||
impl Stack<52> {
|
||||
@@ -124,6 +129,11 @@ impl<const CAP: usize> From<arrayvec::ArrayVec<Card, CAP>> for Stack<CAP> {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl<const CAP: usize> FromIterator<Card> for Stack<CAP> {
|
||||
fn from_iter<T: IntoIterator<Item = Card>>(iter: T) -> Self {
|
||||
Self(arrayvec::ArrayVec::from_iter(iter))
|
||||
}
|
||||
}
|
||||
impl<const CAP: usize> core::ops::Deref for Stack<CAP> {
|
||||
type Target = arrayvec::ArrayVec<Card, CAP>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -144,30 +154,23 @@ impl<const CAP: usize> IntoIterator for Stack<CAP> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Pile<const CAP: usize> {
|
||||
face_down: Stack<CAP>,
|
||||
face_up: Stack<CAP>,
|
||||
pub struct Pile<const DN: usize, const UP: usize> {
|
||||
face_down: Stack<DN>,
|
||||
face_up: Stack<UP>,
|
||||
}
|
||||
impl<const CAP: usize> Pile<CAP> {
|
||||
impl<const DN: usize, const UP: usize> Pile<DN, UP> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
face_down: Stack::new(),
|
||||
face_up: Stack::new(),
|
||||
}
|
||||
}
|
||||
pub fn new_face_down(stack: Stack<CAP>) -> Self {
|
||||
pub fn new_face_down(stack: Stack<DN>) -> Self {
|
||||
Self {
|
||||
face_down: stack,
|
||||
face_up: Stack::new(),
|
||||
}
|
||||
}
|
||||
pub fn flip_it_and_reverse_it(&mut self) {
|
||||
self.swap_up_down();
|
||||
self.face_down.reverse();
|
||||
}
|
||||
pub fn swap_up_down(&mut self) {
|
||||
core::mem::swap(&mut self.face_up, &mut self.face_down);
|
||||
}
|
||||
pub fn flip_up(&mut self) {
|
||||
if let Some(card) = self.face_down.pop() {
|
||||
self.face_up.push(card);
|
||||
@@ -179,16 +182,25 @@ impl<const CAP: usize> Pile<CAP> {
|
||||
pub fn pop(&mut self) -> Option<Card> {
|
||||
self.face_up.pop()
|
||||
}
|
||||
pub fn pop_flip_up(&mut self) -> Option<Card> {
|
||||
let card = self.pop()?;
|
||||
pub fn take_range<R: RangeBounds<usize>>(&mut self, range: R) -> Stack<UP> {
|
||||
// if self.face_up.get(range).is_none() {
|
||||
// return None;
|
||||
// }
|
||||
self.face_up.take_range(range)
|
||||
}
|
||||
pub fn take_range_flip_up<R: RangeBounds<usize>>(&mut self, range: R) -> Stack<UP> {
|
||||
let cards = self.take_range(range);
|
||||
if self.face_up.is_empty() {
|
||||
self.flip_up();
|
||||
}
|
||||
Some(card)
|
||||
cards
|
||||
}
|
||||
pub fn push(&mut self, card: Card) {
|
||||
self.face_up.push(card);
|
||||
}
|
||||
pub fn extend<I: IntoIterator<Item = Card>>(&mut self, cards: I) {
|
||||
self.face_up.extend(cards);
|
||||
}
|
||||
pub fn face_up(&self) -> &[Card] {
|
||||
&self.face_up
|
||||
}
|
||||
@@ -196,6 +208,15 @@ impl<const CAP: usize> Pile<CAP> {
|
||||
&self.face_down
|
||||
}
|
||||
}
|
||||
impl<const CAP: usize> Pile<CAP, CAP> {
|
||||
pub fn flip_it_and_reverse_it(&mut self) {
|
||||
self.swap_up_down();
|
||||
self.face_down.reverse();
|
||||
}
|
||||
pub fn swap_up_down(&mut self) {
|
||||
core::mem::swap(&mut self.face_up, &mut self.face_down);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Session<G: Game> {
|
||||
|
||||
+374
-58
@@ -1,5 +1,5 @@
|
||||
use crate::Rng;
|
||||
use crate::card_game::{CardValue, Game, Pile, Stack};
|
||||
use crate::card_game::{Card, CardValue, Game, Pile, Stack};
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct KlondikeConfig {}
|
||||
@@ -9,6 +9,7 @@ impl Default for KlondikeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum KlondikePileId {
|
||||
Tableau1,
|
||||
@@ -25,7 +26,7 @@ pub enum KlondikePileId {
|
||||
Stock,
|
||||
}
|
||||
impl KlondikePileId {
|
||||
fn next(self) -> Option<Self> {
|
||||
const fn next(self) -> Option<Self> {
|
||||
use KlondikePileId::*;
|
||||
Some(match self {
|
||||
Tableau1 => Tableau2,
|
||||
@@ -44,19 +45,207 @@ impl KlondikePileId {
|
||||
}
|
||||
}
|
||||
|
||||
/// high four bits for stack depth, low four bits for Pile Id
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct InstructionSrc(u8);
|
||||
impl InstructionSrc {
|
||||
const STOCK: Self = InstructionSrc::new(KlondikePileStack::Stock);
|
||||
pub const fn new(src: KlondikePileStack) -> Self {
|
||||
match src {
|
||||
KlondikePileStack::Tableau1(skip_cards) => {
|
||||
Self(KlondikePileId::Tableau1 as u8 + ((skip_cards as u8) << 4))
|
||||
}
|
||||
KlondikePileStack::Tableau2(skip_cards) => {
|
||||
Self(KlondikePileId::Tableau2 as u8 + ((skip_cards as u8) << 4))
|
||||
}
|
||||
KlondikePileStack::Tableau3(skip_cards) => {
|
||||
Self(KlondikePileId::Tableau3 as u8 + ((skip_cards as u8) << 4))
|
||||
}
|
||||
KlondikePileStack::Tableau4(skip_cards) => {
|
||||
Self(KlondikePileId::Tableau4 as u8 + ((skip_cards as u8) << 4))
|
||||
}
|
||||
KlondikePileStack::Tableau5(skip_cards) => {
|
||||
Self(KlondikePileId::Tableau5 as u8 + ((skip_cards as u8) << 4))
|
||||
}
|
||||
KlondikePileStack::Tableau6(skip_cards) => {
|
||||
Self(KlondikePileId::Tableau6 as u8 + ((skip_cards as u8) << 4))
|
||||
}
|
||||
KlondikePileStack::Tableau7(skip_cards) => {
|
||||
Self(KlondikePileId::Tableau7 as u8 + ((skip_cards as u8) << 4))
|
||||
}
|
||||
KlondikePileStack::Foundation1 => Self(KlondikePileId::Foundation1 as u8),
|
||||
KlondikePileStack::Foundation2 => Self(KlondikePileId::Foundation2 as u8),
|
||||
KlondikePileStack::Foundation3 => Self(KlondikePileId::Foundation3 as u8),
|
||||
KlondikePileStack::Foundation4 => Self(KlondikePileId::Foundation4 as u8),
|
||||
KlondikePileStack::Stock => Self(KlondikePileId::Stock as u8),
|
||||
}
|
||||
}
|
||||
const fn into_spec(self) -> KlondikePileStack {
|
||||
// SAFETY: there is no way to construct an invalid InstructionSrc
|
||||
let pile = unsafe { core::mem::transmute(self.0 & 0b1111) };
|
||||
match pile {
|
||||
KlondikePileId::Tableau1 => {
|
||||
KlondikePileStack::Tableau1(unsafe { core::mem::transmute(self.0 >> 4) })
|
||||
}
|
||||
KlondikePileId::Tableau2 => {
|
||||
KlondikePileStack::Tableau2(unsafe { core::mem::transmute(self.0 >> 4) })
|
||||
}
|
||||
KlondikePileId::Tableau3 => {
|
||||
KlondikePileStack::Tableau3(unsafe { core::mem::transmute(self.0 >> 4) })
|
||||
}
|
||||
KlondikePileId::Tableau4 => {
|
||||
KlondikePileStack::Tableau4(unsafe { core::mem::transmute(self.0 >> 4) })
|
||||
}
|
||||
KlondikePileId::Tableau5 => {
|
||||
KlondikePileStack::Tableau5(unsafe { core::mem::transmute(self.0 >> 4) })
|
||||
}
|
||||
KlondikePileId::Tableau6 => {
|
||||
KlondikePileStack::Tableau6(unsafe { core::mem::transmute(self.0 >> 4) })
|
||||
}
|
||||
KlondikePileId::Tableau7 => {
|
||||
KlondikePileStack::Tableau7(unsafe { core::mem::transmute(self.0 >> 4) })
|
||||
}
|
||||
KlondikePileId::Foundation1 => KlondikePileStack::Foundation1,
|
||||
KlondikePileId::Foundation2 => KlondikePileStack::Foundation2,
|
||||
KlondikePileId::Foundation3 => KlondikePileStack::Foundation3,
|
||||
KlondikePileId::Foundation4 => KlondikePileStack::Foundation4,
|
||||
KlondikePileId::Stock => KlondikePileStack::Stock,
|
||||
}
|
||||
}
|
||||
const fn next(self) -> Option<Self> {
|
||||
match self.into_spec().next() {
|
||||
Some(s) => Some(Self::new(s)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<InstructionSrc> for KlondikePileId {
|
||||
fn from(value: InstructionSrc) -> Self {
|
||||
match value.into_spec() {
|
||||
KlondikePileStack::Tableau1(_) => KlondikePileId::Tableau1,
|
||||
KlondikePileStack::Tableau2(_) => KlondikePileId::Tableau2,
|
||||
KlondikePileStack::Tableau3(_) => KlondikePileId::Tableau3,
|
||||
KlondikePileStack::Tableau4(_) => KlondikePileId::Tableau4,
|
||||
KlondikePileStack::Tableau5(_) => KlondikePileId::Tableau5,
|
||||
KlondikePileStack::Tableau6(_) => KlondikePileId::Tableau6,
|
||||
KlondikePileStack::Tableau7(_) => KlondikePileId::Tableau7,
|
||||
KlondikePileStack::Foundation1 => KlondikePileId::Foundation1,
|
||||
KlondikePileStack::Foundation2 => KlondikePileId::Foundation2,
|
||||
KlondikePileStack::Foundation3 => KlondikePileId::Foundation3,
|
||||
KlondikePileStack::Foundation4 => KlondikePileId::Foundation4,
|
||||
KlondikePileStack::Stock => KlondikePileId::Stock,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum SkipCards {
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
Nine,
|
||||
Ten,
|
||||
Eleven,
|
||||
Twelve,
|
||||
Thirteen,
|
||||
}
|
||||
impl SkipCards {
|
||||
const fn next(self) -> Option<Self> {
|
||||
use SkipCards::*;
|
||||
Some(match self {
|
||||
Zero => One,
|
||||
One => Two,
|
||||
Two => Three,
|
||||
Three => Four,
|
||||
Four => Five,
|
||||
Five => Six,
|
||||
Six => Seven,
|
||||
Seven => Eight,
|
||||
Eight => Nine,
|
||||
Nine => Ten,
|
||||
Ten => Eleven,
|
||||
Eleven => Twelve,
|
||||
Twelve => Thirteen,
|
||||
Thirteen => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum KlondikePileStack {
|
||||
Tableau1(SkipCards),
|
||||
Tableau2(SkipCards),
|
||||
Tableau3(SkipCards),
|
||||
Tableau4(SkipCards),
|
||||
Tableau5(SkipCards),
|
||||
Tableau6(SkipCards),
|
||||
Tableau7(SkipCards),
|
||||
Foundation1,
|
||||
Foundation2,
|
||||
Foundation3,
|
||||
Foundation4,
|
||||
Stock,
|
||||
}
|
||||
impl KlondikePileStack {
|
||||
const fn next(self) -> Option<Self> {
|
||||
use KlondikePileStack::*;
|
||||
Some(match self {
|
||||
Tableau1(skip) => match skip.next() {
|
||||
Some(next) => Tableau1(next),
|
||||
None => Tableau2(SkipCards::Zero),
|
||||
},
|
||||
Tableau2(skip) => match skip.next() {
|
||||
Some(next) => Tableau2(next),
|
||||
None => Tableau3(SkipCards::Zero),
|
||||
},
|
||||
Tableau3(skip) => match skip.next() {
|
||||
Some(next) => Tableau3(next),
|
||||
None => Tableau4(SkipCards::Zero),
|
||||
},
|
||||
Tableau4(skip) => match skip.next() {
|
||||
Some(next) => Tableau4(next),
|
||||
None => Tableau5(SkipCards::Zero),
|
||||
},
|
||||
Tableau5(skip) => match skip.next() {
|
||||
Some(next) => Tableau5(next),
|
||||
None => Tableau6(SkipCards::Zero),
|
||||
},
|
||||
Tableau6(skip) => match skip.next() {
|
||||
Some(next) => Tableau6(next),
|
||||
None => Tableau7(SkipCards::Zero),
|
||||
},
|
||||
Tableau7(skip) => match skip.next() {
|
||||
Some(next) => Tableau7(next),
|
||||
None => Foundation1,
|
||||
},
|
||||
Foundation1 => Foundation2,
|
||||
Foundation2 => Foundation3,
|
||||
Foundation3 => Foundation4,
|
||||
Foundation4 => Stock,
|
||||
Stock => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct KlondikeInstruction {
|
||||
pub src: KlondikePileId,
|
||||
pub src: InstructionSrc,
|
||||
pub dst: KlondikePileId,
|
||||
}
|
||||
impl KlondikeInstruction {
|
||||
pub fn stock() -> Self {
|
||||
pub const fn stock() -> Self {
|
||||
Self {
|
||||
src: KlondikePileId::Stock,
|
||||
src: InstructionSrc::STOCK,
|
||||
dst: KlondikePileId::Stock,
|
||||
}
|
||||
}
|
||||
fn next(self) -> Option<Self> {
|
||||
const fn next(self) -> Option<Self> {
|
||||
let KlondikeInstruction { src, dst } = self;
|
||||
if let Some(next_dst) = dst.next() {
|
||||
return Some(Self { src, dst: next_dst });
|
||||
@@ -71,8 +260,6 @@ impl KlondikeInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
const STOCKS: usize = 1;
|
||||
const FOUNDATIONS: usize = 4;
|
||||
const TABLEAUS: usize = 7;
|
||||
const fn sum(n: usize) -> usize {
|
||||
n * (n + 1) / 2
|
||||
@@ -80,25 +267,155 @@ const fn sum(n: usize) -> usize {
|
||||
const MAX_STACK: usize = 52 - sum(TABLEAUS);
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
struct KlondikeState {
|
||||
piles: [Pile<MAX_STACK>; TABLEAUS + FOUNDATIONS + STOCKS],
|
||||
pub struct KlondikeState {
|
||||
stock: Pile<MAX_STACK, MAX_STACK>,
|
||||
foundations: [Stack<13>; 4],
|
||||
tableau1: Pile<0, 13>,
|
||||
tableau2: Pile<1, 13>,
|
||||
tableau3: Pile<2, 13>,
|
||||
tableau4: Pile<3, 13>,
|
||||
tableau5: Pile<4, 13>,
|
||||
tableau6: Pile<5, 13>,
|
||||
tableau7: Pile<6, 13>,
|
||||
}
|
||||
impl KlondikeState {
|
||||
fn pile(&self, index: KlondikePileId) -> &Pile<MAX_STACK> {
|
||||
&self.piles[index as usize]
|
||||
pub const fn stock(&self) -> &Pile<MAX_STACK, MAX_STACK> {
|
||||
&self.stock
|
||||
}
|
||||
pub const fn foundation1(&self) -> &Stack<13> {
|
||||
&self.foundations[1 - 1]
|
||||
}
|
||||
pub const fn foundation2(&self) -> &Stack<13> {
|
||||
&self.foundations[2 - 1]
|
||||
}
|
||||
pub const fn foundation3(&self) -> &Stack<13> {
|
||||
&self.foundations[3 - 1]
|
||||
}
|
||||
pub const fn foundation4(&self) -> &Stack<13> {
|
||||
&self.foundations[4 - 1]
|
||||
}
|
||||
pub const fn tableau1(&self) -> &Pile<0, 13> {
|
||||
&self.tableau1
|
||||
}
|
||||
pub const fn tableau2(&self) -> &Pile<1, 13> {
|
||||
&self.tableau2
|
||||
}
|
||||
pub const fn tableau3(&self) -> &Pile<2, 13> {
|
||||
&self.tableau3
|
||||
}
|
||||
pub const fn tableau4(&self) -> &Pile<3, 13> {
|
||||
&self.tableau4
|
||||
}
|
||||
pub const fn tableau5(&self) -> &Pile<4, 13> {
|
||||
&self.tableau5
|
||||
}
|
||||
pub const fn tableau6(&self) -> &Pile<5, 13> {
|
||||
&self.tableau6
|
||||
}
|
||||
pub const fn tableau7(&self) -> &Pile<6, 13> {
|
||||
&self.tableau7
|
||||
}
|
||||
fn src_card(&self, src: InstructionSrc) -> Option<&Card> {
|
||||
match src.into_spec() {
|
||||
KlondikePileStack::Tableau1(skip_cards) => {
|
||||
self.tableau1.face_up().get(skip_cards as usize)
|
||||
}
|
||||
KlondikePileStack::Tableau2(skip_cards) => {
|
||||
self.tableau2.face_up().get(skip_cards as usize)
|
||||
}
|
||||
KlondikePileStack::Tableau3(skip_cards) => {
|
||||
self.tableau3.face_up().get(skip_cards as usize)
|
||||
}
|
||||
KlondikePileStack::Tableau4(skip_cards) => {
|
||||
self.tableau4.face_up().get(skip_cards as usize)
|
||||
}
|
||||
KlondikePileStack::Tableau5(skip_cards) => {
|
||||
self.tableau5.face_up().get(skip_cards as usize)
|
||||
}
|
||||
KlondikePileStack::Tableau6(skip_cards) => {
|
||||
self.tableau6.face_up().get(skip_cards as usize)
|
||||
}
|
||||
KlondikePileStack::Tableau7(skip_cards) => {
|
||||
self.tableau7.face_up().get(skip_cards as usize)
|
||||
}
|
||||
KlondikePileStack::Foundation1 => self.foundations[1 - 1].last(),
|
||||
KlondikePileStack::Foundation2 => self.foundations[2 - 1].last(),
|
||||
KlondikePileStack::Foundation3 => self.foundations[3 - 1].last(),
|
||||
KlondikePileStack::Foundation4 => self.foundations[4 - 1].last(),
|
||||
KlondikePileStack::Stock => self.stock.face_up().last(),
|
||||
}
|
||||
}
|
||||
fn take_src_cards(&mut self, src: InstructionSrc) -> Stack<13> {
|
||||
match src.into_spec() {
|
||||
KlondikePileStack::Tableau1(skip_cards) => {
|
||||
self.tableau1.take_range_flip_up(skip_cards as usize..)
|
||||
}
|
||||
KlondikePileStack::Tableau2(skip_cards) => {
|
||||
self.tableau2.take_range_flip_up(skip_cards as usize..)
|
||||
}
|
||||
KlondikePileStack::Tableau3(skip_cards) => {
|
||||
self.tableau3.take_range_flip_up(skip_cards as usize..)
|
||||
}
|
||||
KlondikePileStack::Tableau4(skip_cards) => {
|
||||
self.tableau4.take_range_flip_up(skip_cards as usize..)
|
||||
}
|
||||
KlondikePileStack::Tableau5(skip_cards) => {
|
||||
self.tableau5.take_range_flip_up(skip_cards as usize..)
|
||||
}
|
||||
KlondikePileStack::Tableau6(skip_cards) => {
|
||||
self.tableau6.take_range_flip_up(skip_cards as usize..)
|
||||
}
|
||||
KlondikePileStack::Tableau7(skip_cards) => {
|
||||
self.tableau7.take_range_flip_up(skip_cards as usize..)
|
||||
}
|
||||
KlondikePileStack::Foundation1 => Stack::from_iter(self.foundations[1 - 1].pop()),
|
||||
KlondikePileStack::Foundation2 => Stack::from_iter(self.foundations[2 - 1].pop()),
|
||||
KlondikePileStack::Foundation3 => Stack::from_iter(self.foundations[3 - 1].pop()),
|
||||
KlondikePileStack::Foundation4 => Stack::from_iter(self.foundations[4 - 1].pop()),
|
||||
KlondikePileStack::Stock => Stack::from_iter(self.stock.pop()),
|
||||
}
|
||||
}
|
||||
fn dst_card(&self, dst: KlondikePileId) -> Option<&Card> {
|
||||
match dst {
|
||||
KlondikePileId::Tableau1 => self.tableau1.face_up().last(),
|
||||
KlondikePileId::Tableau2 => self.tableau2.face_up().last(),
|
||||
KlondikePileId::Tableau3 => self.tableau3.face_up().last(),
|
||||
KlondikePileId::Tableau4 => self.tableau4.face_up().last(),
|
||||
KlondikePileId::Tableau5 => self.tableau5.face_up().last(),
|
||||
KlondikePileId::Tableau6 => self.tableau6.face_up().last(),
|
||||
KlondikePileId::Tableau7 => self.tableau7.face_up().last(),
|
||||
KlondikePileId::Foundation1 => self.foundations[1 - 1].last(),
|
||||
KlondikePileId::Foundation2 => self.foundations[2 - 1].last(),
|
||||
KlondikePileId::Foundation3 => self.foundations[3 - 1].last(),
|
||||
KlondikePileId::Foundation4 => self.foundations[4 - 1].last(),
|
||||
KlondikePileId::Stock => None,
|
||||
}
|
||||
}
|
||||
fn extend_dst_pile(&mut self, dst: KlondikePileId, cards: Stack<13>) {
|
||||
match dst {
|
||||
KlondikePileId::Tableau1 => self.tableau1.extend(cards),
|
||||
KlondikePileId::Tableau2 => self.tableau2.extend(cards),
|
||||
KlondikePileId::Tableau3 => self.tableau3.extend(cards),
|
||||
KlondikePileId::Tableau4 => self.tableau4.extend(cards),
|
||||
KlondikePileId::Tableau5 => self.tableau5.extend(cards),
|
||||
KlondikePileId::Tableau6 => self.tableau6.extend(cards),
|
||||
KlondikePileId::Tableau7 => self.tableau7.extend(cards),
|
||||
KlondikePileId::Foundation1 => self.foundations[1 - 1].extend(cards),
|
||||
KlondikePileId::Foundation2 => self.foundations[2 - 1].extend(cards),
|
||||
KlondikePileId::Foundation3 => self.foundations[3 - 1].extend(cards),
|
||||
KlondikePileId::Foundation4 => self.foundations[4 - 1].extend(cards),
|
||||
KlondikePileId::Stock => (),
|
||||
}
|
||||
fn pile_mut(&mut self, index: KlondikePileId) -> &mut Pile<MAX_STACK> {
|
||||
&mut self.piles[index as usize]
|
||||
}
|
||||
fn is_instruction_valid(&self, instruction: KlondikeInstruction) -> bool {
|
||||
match instruction {
|
||||
// Stock -> Stock draws a card or resets the stock
|
||||
KlondikeInstruction {
|
||||
src: KlondikePileId::Stock,
|
||||
src: InstructionSrc::STOCK,
|
||||
dst: KlondikePileId::Stock,
|
||||
} => {
|
||||
// cannot move stock when stock is empty
|
||||
!self.pile(KlondikePileId::Stock).is_empty()
|
||||
!self.stock.is_empty()
|
||||
}
|
||||
|
||||
// cannot move cards to stock
|
||||
@@ -118,8 +435,8 @@ impl KlondikeState {
|
||||
) =>
|
||||
{
|
||||
// get the top cards
|
||||
if let Some(src_card) = self.pile(src).face_up().last() {
|
||||
match self.pile(dst).face_up().last() {
|
||||
if let Some(src_card) = self.src_card(src) {
|
||||
match self.dst_card(dst) {
|
||||
// destination card exists
|
||||
Some(dst_card) => {
|
||||
// suit matches?
|
||||
@@ -137,8 +454,8 @@ impl KlondikeState {
|
||||
// other = move to tableau
|
||||
KlondikeInstruction { src, dst } => {
|
||||
// get the top cards
|
||||
if let Some(src_card) = self.pile(src).face_up().last() {
|
||||
match self.pile(dst).face_up().last() {
|
||||
if let Some(src_card) = self.src_card(src) {
|
||||
match self.dst_card(dst) {
|
||||
// destination card exists
|
||||
Some(dst_card) => {
|
||||
// red-ness is opposite?
|
||||
@@ -161,10 +478,10 @@ pub struct KlondikeIter {
|
||||
instruction: Option<KlondikeInstruction>,
|
||||
}
|
||||
impl KlondikeIter {
|
||||
fn new() -> Self {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
instruction: Some(KlondikeInstruction {
|
||||
src: KlondikePileId::Tableau1,
|
||||
src: InstructionSrc::new(KlondikePileStack::Tableau1(SkipCards::Zero)),
|
||||
dst: KlondikePileId::Tableau2,
|
||||
}),
|
||||
}
|
||||
@@ -196,41 +513,38 @@ impl Klondike {
|
||||
let mut deck = deck.into_iter();
|
||||
|
||||
// generate tableaus
|
||||
let [t0, t1, t2, t3, t4, t5, t6] = core::array::from_fn(|i| {
|
||||
let stack = arrayvec::ArrayVec::from_iter((&mut deck).take(i)).into();
|
||||
fn pile<const DN: usize>(deck: &mut arrayvec::IntoIter<Card, 52>) -> Pile<DN, 13> {
|
||||
let stack = arrayvec::ArrayVec::from_iter(deck.take(DN)).into();
|
||||
let mut pile = Pile::new_face_down(stack);
|
||||
pile.push(deck.next().unwrap());
|
||||
pile
|
||||
});
|
||||
}
|
||||
let tableau1 = pile(&mut deck);
|
||||
let tableau2 = pile(&mut deck);
|
||||
let tableau3 = pile(&mut deck);
|
||||
let tableau4 = pile(&mut deck);
|
||||
let tableau5 = pile(&mut deck);
|
||||
let tableau6 = pile(&mut deck);
|
||||
let tableau7 = pile(&mut deck);
|
||||
|
||||
// stock is remaining cards
|
||||
let stock = Pile::new_face_down(arrayvec::ArrayVec::from_iter(deck).into());
|
||||
|
||||
let state = KlondikeState {
|
||||
piles: [
|
||||
t0,
|
||||
t1,
|
||||
t2,
|
||||
t3,
|
||||
t4,
|
||||
t5,
|
||||
t6,
|
||||
Pile::new(),
|
||||
Pile::new(),
|
||||
Pile::new(),
|
||||
Pile::new(),
|
||||
stock,
|
||||
],
|
||||
foundations: core::array::from_fn(|_| Stack::new()),
|
||||
tableau1,
|
||||
tableau2,
|
||||
tableau3,
|
||||
tableau4,
|
||||
tableau5,
|
||||
tableau6,
|
||||
tableau7,
|
||||
};
|
||||
Self { config, state }
|
||||
}
|
||||
#[inline]
|
||||
pub fn pile(&self, index: KlondikePileId) -> &Pile<MAX_STACK> {
|
||||
self.state.pile(index)
|
||||
}
|
||||
#[inline]
|
||||
fn pile_mut(&mut self, index: KlondikePileId) -> &mut Pile<MAX_STACK> {
|
||||
self.state.pile_mut(index)
|
||||
pub const fn state(&self) -> &KlondikeState {
|
||||
&self.state
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,28 +561,30 @@ impl Game for Klondike {
|
||||
match instruction {
|
||||
// Reset the stock if it's empty
|
||||
KlondikeInstruction {
|
||||
src: KlondikePileId::Stock,
|
||||
src: InstructionSrc::STOCK,
|
||||
dst: KlondikePileId::Stock,
|
||||
} => {
|
||||
if self.pile(KlondikePileId::Stock).face_down().is_empty() {
|
||||
self.pile_mut(KlondikePileId::Stock)
|
||||
.flip_it_and_reverse_it();
|
||||
if self.state.stock.face_down().is_empty() {
|
||||
self.state.stock.flip_it_and_reverse_it();
|
||||
} else {
|
||||
self.pile_mut(KlondikePileId::Stock).flip_up();
|
||||
self.state.stock.flip_up();
|
||||
}
|
||||
}
|
||||
KlondikeInstruction { src, dst } => {
|
||||
if let Some(card) = self.pile_mut(src).pop_flip_up() {
|
||||
self.pile_mut(dst).push(card);
|
||||
} else {
|
||||
println!("Attempted to move from an empty src");
|
||||
dbg!(instruction);
|
||||
}
|
||||
let cards = self.state.take_src_cards(src);
|
||||
self.state.extend_dst_pile(dst, cards);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn is_win(&self) -> bool {
|
||||
// assuming only valid moves, tableau empty and stock empty means win
|
||||
self.state.piles[0..9].iter().all(|pile| pile.is_empty())
|
||||
// all face down cards empty means win
|
||||
self.state.stock.face_down().is_empty()
|
||||
&& self.state.tableau1.face_down().is_empty()
|
||||
&& self.state.tableau2.face_down().is_empty()
|
||||
&& self.state.tableau3.face_down().is_empty()
|
||||
&& self.state.tableau4.face_down().is_empty()
|
||||
&& self.state.tableau5.face_down().is_empty()
|
||||
&& self.state.tableau6.face_down().is_empty()
|
||||
&& self.state.tableau7.face_down().is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
+44
-22
@@ -3,8 +3,10 @@ mod klondike;
|
||||
|
||||
pub type Rng = rand::rngs::ThreadRng;
|
||||
|
||||
use card_game::{Card, Game, Session, Suit};
|
||||
use klondike::{Klondike, KlondikeInstruction, KlondikePileId};
|
||||
use card_game::{Card, Game, Pile, Session, Suit};
|
||||
use klondike::{
|
||||
InstructionSrc, Klondike, KlondikeInstruction, KlondikePileId, KlondikePileStack, SkipCards,
|
||||
};
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
@@ -39,38 +41,30 @@ impl Display for OptionalCard<'_> {
|
||||
impl Display for Klondike {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// Stock
|
||||
let stock_count = self.pile(KlondikePileId::Stock).face_down().len();
|
||||
let stock_count = self.state().stock().face_down().len();
|
||||
writeln!(f, "Stock: {stock_count}")?;
|
||||
|
||||
// Hand
|
||||
let hand = self.pile(KlondikePileId::Stock).face_up().last();
|
||||
let hand = self.state().stock().face_up().last();
|
||||
writeln!(f, "Hand: {}", OptionalCard(hand))?;
|
||||
|
||||
// Foundations
|
||||
write!(
|
||||
f,
|
||||
"Foundations: {} {} {} {}",
|
||||
OptionalCard(self.pile(KlondikePileId::Foundation1).face_up().last()),
|
||||
OptionalCard(self.pile(KlondikePileId::Foundation2).face_up().last()),
|
||||
OptionalCard(self.pile(KlondikePileId::Foundation3).face_up().last()),
|
||||
OptionalCard(self.pile(KlondikePileId::Foundation4).face_up().last()),
|
||||
OptionalCard(self.state().foundation1().last()),
|
||||
OptionalCard(self.state().foundation2().last()),
|
||||
OptionalCard(self.state().foundation3().last()),
|
||||
OptionalCard(self.state().foundation4().last()),
|
||||
)?;
|
||||
writeln!(f)?;
|
||||
|
||||
for (i, tableau) in [
|
||||
KlondikePileId::Tableau1,
|
||||
KlondikePileId::Tableau2,
|
||||
KlondikePileId::Tableau3,
|
||||
KlondikePileId::Tableau4,
|
||||
KlondikePileId::Tableau5,
|
||||
KlondikePileId::Tableau6,
|
||||
KlondikePileId::Tableau7,
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
write!(f, "T{} ", i + 1)?;
|
||||
let pile = self.pile(tableau);
|
||||
fn write_pile<const DN: usize, const UP: usize>(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
pile: &Pile<DN, UP>,
|
||||
pile_id: usize,
|
||||
) -> std::fmt::Result {
|
||||
write!(f, "T{} ", pile_id)?;
|
||||
for _ in pile.face_down() {
|
||||
write!(f, "]")?;
|
||||
}
|
||||
@@ -78,7 +72,15 @@ impl Display for Klondike {
|
||||
write!(f, "{card}")?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
Ok(())
|
||||
}
|
||||
write_pile(f, self.state().tableau1(), 1)?;
|
||||
write_pile(f, self.state().tableau2(), 2)?;
|
||||
write_pile(f, self.state().tableau3(), 3)?;
|
||||
write_pile(f, self.state().tableau4(), 4)?;
|
||||
write_pile(f, self.state().tableau5(), 5)?;
|
||||
write_pile(f, self.state().tableau6(), 6)?;
|
||||
write_pile(f, self.state().tableau7(), 7)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -95,6 +97,26 @@ impl core::str::FromStr for Parsed<KlondikeInstruction> {
|
||||
Ok(Parsed(KlondikeInstruction { src, dst }))
|
||||
}
|
||||
}
|
||||
impl core::str::FromStr for Parsed<InstructionSrc> {
|
||||
type Err = Invalid;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Parsed(match s {
|
||||
"ST" | "st" => InstructionSrc::new(KlondikePileStack::Stock),
|
||||
"T1" | "t1" => InstructionSrc::new(KlondikePileStack::Tableau1(SkipCards::Zero)),
|
||||
"T2" | "t2" => InstructionSrc::new(KlondikePileStack::Tableau2(SkipCards::Zero)),
|
||||
"T3" | "t3" => InstructionSrc::new(KlondikePileStack::Tableau3(SkipCards::Zero)),
|
||||
"T4" | "t4" => InstructionSrc::new(KlondikePileStack::Tableau4(SkipCards::Zero)),
|
||||
"T5" | "t5" => InstructionSrc::new(KlondikePileStack::Tableau5(SkipCards::Zero)),
|
||||
"T6" | "t6" => InstructionSrc::new(KlondikePileStack::Tableau6(SkipCards::Zero)),
|
||||
"T7" | "t7" => InstructionSrc::new(KlondikePileStack::Tableau7(SkipCards::Zero)),
|
||||
"F1" | "f1" => InstructionSrc::new(KlondikePileStack::Foundation1),
|
||||
"F2" | "f2" => InstructionSrc::new(KlondikePileStack::Foundation2),
|
||||
"F3" | "f3" => InstructionSrc::new(KlondikePileStack::Foundation3),
|
||||
"F4" | "f4" => InstructionSrc::new(KlondikePileStack::Foundation4),
|
||||
_ => return Err(Invalid),
|
||||
}))
|
||||
}
|
||||
}
|
||||
impl core::str::FromStr for Parsed<KlondikePileId> {
|
||||
type Err = Invalid;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
|
||||
Reference in New Issue
Block a user