extend instruction to include skip cards
This commit is contained in:
+201
-12
@@ -9,6 +9,7 @@ impl Default for KlondikeConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
pub enum KlondikePileId {
|
pub enum KlondikePileId {
|
||||||
Tableau1,
|
Tableau1,
|
||||||
@@ -25,7 +26,7 @@ pub enum KlondikePileId {
|
|||||||
Stock,
|
Stock,
|
||||||
}
|
}
|
||||||
impl KlondikePileId {
|
impl KlondikePileId {
|
||||||
fn next(self) -> Option<Self> {
|
const fn next(self) -> Option<Self> {
|
||||||
use KlondikePileId::*;
|
use KlondikePileId::*;
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Tableau1 => Tableau2,
|
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)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct KlondikeInstruction {
|
pub struct KlondikeInstruction {
|
||||||
pub src: KlondikePileId,
|
pub src: InstructionSrc,
|
||||||
pub dst: KlondikePileId,
|
pub dst: KlondikePileId,
|
||||||
}
|
}
|
||||||
impl KlondikeInstruction {
|
impl KlondikeInstruction {
|
||||||
pub fn stock() -> Self {
|
pub const fn stock() -> Self {
|
||||||
Self {
|
Self {
|
||||||
src: KlondikePileId::Stock,
|
src: InstructionSrc::STOCK,
|
||||||
dst: KlondikePileId::Stock,
|
dst: KlondikePileId::Stock,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn next(self) -> Option<Self> {
|
const fn next(self) -> Option<Self> {
|
||||||
let KlondikeInstruction { src, dst } = self;
|
let KlondikeInstruction { src, dst } = self;
|
||||||
if let Some(next_dst) = dst.next() {
|
if let Some(next_dst) = dst.next() {
|
||||||
return Some(Self { src, dst: next_dst });
|
return Some(Self { src, dst: next_dst });
|
||||||
@@ -94,7 +283,7 @@ impl KlondikeState {
|
|||||||
match instruction {
|
match instruction {
|
||||||
// Stock -> Stock draws a card or resets the stock
|
// Stock -> Stock draws a card or resets the stock
|
||||||
KlondikeInstruction {
|
KlondikeInstruction {
|
||||||
src: KlondikePileId::Stock,
|
src: InstructionSrc::STOCK,
|
||||||
dst: KlondikePileId::Stock,
|
dst: KlondikePileId::Stock,
|
||||||
} => {
|
} => {
|
||||||
// cannot move stock when stock is empty
|
// cannot move stock when stock is empty
|
||||||
@@ -118,7 +307,7 @@ impl KlondikeState {
|
|||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
// get the top cards
|
// get the top cards
|
||||||
if let Some(src_card) = self.pile(src).face_up().last() {
|
if let Some(src_card) = self.pile(src.into()).face_up().last() {
|
||||||
match self.pile(dst).face_up().last() {
|
match self.pile(dst).face_up().last() {
|
||||||
// destination card exists
|
// destination card exists
|
||||||
Some(dst_card) => {
|
Some(dst_card) => {
|
||||||
@@ -137,7 +326,7 @@ impl KlondikeState {
|
|||||||
// other = move to tableau
|
// other = move to tableau
|
||||||
KlondikeInstruction { src, dst } => {
|
KlondikeInstruction { src, dst } => {
|
||||||
// get the top cards
|
// get the top cards
|
||||||
if let Some(src_card) = self.pile(src).face_up().last() {
|
if let Some(src_card) = self.pile(src.into()).face_up().last() {
|
||||||
match self.pile(dst).face_up().last() {
|
match self.pile(dst).face_up().last() {
|
||||||
// destination card exists
|
// destination card exists
|
||||||
Some(dst_card) => {
|
Some(dst_card) => {
|
||||||
@@ -161,10 +350,10 @@ pub struct KlondikeIter {
|
|||||||
instruction: Option<KlondikeInstruction>,
|
instruction: Option<KlondikeInstruction>,
|
||||||
}
|
}
|
||||||
impl KlondikeIter {
|
impl KlondikeIter {
|
||||||
fn new() -> Self {
|
const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
instruction: Some(KlondikeInstruction {
|
instruction: Some(KlondikeInstruction {
|
||||||
src: KlondikePileId::Tableau1,
|
src: InstructionSrc::new(KlondikePileStack::Tableau1(SkipCards::Zero)),
|
||||||
dst: KlondikePileId::Tableau2,
|
dst: KlondikePileId::Tableau2,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@@ -247,7 +436,7 @@ impl Game for Klondike {
|
|||||||
match instruction {
|
match instruction {
|
||||||
// Reset the stock if it's empty
|
// Reset the stock if it's empty
|
||||||
KlondikeInstruction {
|
KlondikeInstruction {
|
||||||
src: KlondikePileId::Stock,
|
src: InstructionSrc::STOCK,
|
||||||
dst: KlondikePileId::Stock,
|
dst: KlondikePileId::Stock,
|
||||||
} => {
|
} => {
|
||||||
if self.pile(KlondikePileId::Stock).face_down().is_empty() {
|
if self.pile(KlondikePileId::Stock).face_down().is_empty() {
|
||||||
@@ -258,7 +447,7 @@ impl Game for Klondike {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
KlondikeInstruction { src, dst } => {
|
KlondikeInstruction { src, dst } => {
|
||||||
if let Some(card) = self.pile_mut(src).pop_flip_up() {
|
if let Some(card) = self.pile_mut(src.into()).pop_flip_up() {
|
||||||
self.pile_mut(dst).push(card);
|
self.pile_mut(dst).push(card);
|
||||||
} else {
|
} else {
|
||||||
println!("Attempted to move from an empty src");
|
println!("Attempted to move from an empty src");
|
||||||
|
|||||||
+23
-1
@@ -4,7 +4,9 @@ mod klondike;
|
|||||||
pub type Rng = rand::rngs::ThreadRng;
|
pub type Rng = rand::rngs::ThreadRng;
|
||||||
|
|
||||||
use card_game::{Card, Game, Session, Suit};
|
use card_game::{Card, Game, Session, Suit};
|
||||||
use klondike::{Klondike, KlondikeInstruction, KlondikePileId};
|
use klondike::{
|
||||||
|
InstructionSrc, Klondike, KlondikeInstruction, KlondikePileId, KlondikePileStack, SkipCards,
|
||||||
|
};
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
@@ -95,6 +97,26 @@ impl core::str::FromStr for Parsed<KlondikeInstruction> {
|
|||||||
Ok(Parsed(KlondikeInstruction { src, dst }))
|
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> {
|
impl core::str::FromStr for Parsed<KlondikePileId> {
|
||||||
type Err = Invalid;
|
type Err = Invalid;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
|||||||
Reference in New Issue
Block a user