Compare commits
4 Commits
7e4cfc638b
...
367ad78198
| Author | SHA1 | Date | |
|---|---|---|---|
| 367ad78198 | |||
| 8ce03a4b1c | |||
| 408978dd4f | |||
| 804ff3f8b1 |
@@ -128,10 +128,12 @@ pub enum StackingError {
|
|||||||
SameColour,
|
SameColour,
|
||||||
#[error("{0} is not \"next\" to {1}")]
|
#[error("{0} is not \"next\" to {1}")]
|
||||||
NotAdjacent(String, String),
|
NotAdjacent(String, String),
|
||||||
|
#[error("Foundations need to be the same suit")]
|
||||||
|
WrongSuit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Card {
|
impl Card {
|
||||||
pub fn can_be_placed_on_top(&self, top: &Card) -> Result<(), StackingError> {
|
pub fn can_be_placed_on_pile(&self, top: &Card) -> Result<(), StackingError> {
|
||||||
// Can't be the same Colour
|
// Can't be the same Colour
|
||||||
if self.suit.colour() == top.suit.colour() {
|
if self.suit.colour() == top.suit.colour() {
|
||||||
return Err(StackingError::SameColour);
|
return Err(StackingError::SameColour);
|
||||||
@@ -144,6 +146,31 @@ impl Card {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn can_be_placed_on_foundation(&self, top: &Option<Card>) -> Result<(), StackingError> {
|
||||||
|
// TODO check suit is correct
|
||||||
|
println!("hello1");
|
||||||
|
match top {
|
||||||
|
None => {
|
||||||
|
if self.value == Value::Ace {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
return Err(StackingError::NotAdjacent(self.to_string(), "an empty foundation".to_string()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(c) => {
|
||||||
|
if self.suit != c.suit {
|
||||||
|
println!("hellosuitsame");
|
||||||
|
return Err(StackingError::WrongSuit);
|
||||||
|
}
|
||||||
|
if self.value.indexed_values() + 1 == c.value.indexed_values() {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
return Err(StackingError::NotAdjacent(self.to_string(), c.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -188,6 +215,20 @@ impl Deck {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CardAndPosition {
|
||||||
|
card: Option<Card>,
|
||||||
|
pos: CardPosition,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum CardPosition {
|
||||||
|
Deck, // I don't think this will need to be used
|
||||||
|
TopWaste, // I don't think we'd ever interact with anything other than the top of the Waste
|
||||||
|
Pile(usize, usize), // (PileNumber, Index)
|
||||||
|
Foundation(usize)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum NumPassesThroughDeck {
|
pub enum NumPassesThroughDeck {
|
||||||
Unlimited,
|
Unlimited,
|
||||||
@@ -234,7 +275,96 @@ impl Klondike {
|
|||||||
self.current_num_passes_through_deck += 1;
|
self.current_num_passes_through_deck += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//pub fn move_one_stack_to_another(self, )
|
|
||||||
|
pub fn move_card(self, source_card: &CardAndPosition, dest_card: &CardAndPosition) -> bool {
|
||||||
|
// TODO raise errors properly
|
||||||
|
assert!(source_card.card.is_some());
|
||||||
|
assert_eq!(source_card.card.unwrap().visible, true);
|
||||||
|
source_card.pos.is_valid_source();
|
||||||
|
dest_card.pos.is_valid_dest();
|
||||||
|
|
||||||
|
// Maybe TODO - check the .cards is the actual card in that position
|
||||||
|
|
||||||
|
match dest_card.pos {
|
||||||
|
CardPosition::Pile(_, _) => self.move_card_to_pile(&source_card, &dest_card),
|
||||||
|
CardPosition::Foundation(_f) => self.move_card_to_foundation(source_card, dest_card),
|
||||||
|
CardPosition::Deck | CardPosition::TopWaste => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_card_to_foundation(mut self, source_card: &CardAndPosition, dest_card: &CardAndPosition) -> bool {
|
||||||
|
// TODO Check whether the card is the top of a pile / waste - it needs to be
|
||||||
|
// TODO actually learn Rust properly so I can figure out why I need to clone the whole struct to check a value
|
||||||
|
if source_card.pos != CardPosition::TopWaste && !self.clone().is_card_top_of_pile(&source_card.pos) {
|
||||||
|
// TODO as above - make all these proper errors
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if source_card.card.unwrap().can_be_placed_on_foundation(&dest_card.card).is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO actually move the cards - it should be possible from here
|
||||||
|
// There really must be a better way to extract an enum "value" than this...
|
||||||
|
if let CardPosition::Foundation(foundation_index) = dest_card.pos {
|
||||||
|
match source_card.pos {
|
||||||
|
CardPosition::TopWaste => {
|
||||||
|
let card = self.waste.pop().unwrap();
|
||||||
|
self.foundation[foundation_index].push(card);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
CardPosition::Pile(pile_index, _) => {
|
||||||
|
let card = self.piles[pile_index].pop().unwrap();
|
||||||
|
self.foundation[foundation_index].push(card);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
CardPosition::Deck | CardPosition::Foundation(_) => {
|
||||||
|
unreachable!()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_card_to_pile(self, source_card: &CardAndPosition, dest_card: &CardAndPosition) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_card_top_of_pile(self, pos: &CardPosition) -> bool {
|
||||||
|
// TODO consider, at which point the Pos::Pile() ranges etc are correct
|
||||||
|
match pos {
|
||||||
|
CardPosition::Pile(pile_index, card_index) => {
|
||||||
|
match self.piles.get(*pile_index) {
|
||||||
|
Some(pile_index) => {
|
||||||
|
// TODO - this is the correct palce to put a - 1
|
||||||
|
if *card_index == (pile_index.len() - 1) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CardPosition::Deck | CardPosition::TopWaste | CardPosition::Foundation(_) => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CardPosition {
|
||||||
|
// Unsure this is "correct" to just panic - but it really shouldn't happen
|
||||||
|
fn is_valid_dest(&self) {
|
||||||
|
match self {
|
||||||
|
CardPosition::Deck => panic!("You can't move cards to deck"),
|
||||||
|
CardPosition::TopWaste => panic!("You can't move cards to waste"),
|
||||||
|
CardPosition::Pile(_, _) | CardPosition::Foundation(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_source(&self) {
|
||||||
|
match self {
|
||||||
|
CardPosition::Deck => panic!("You can't move cards from deck"),
|
||||||
|
CardPosition::TopWaste | CardPosition::Pile(_, _) | CardPosition::Foundation(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Klondike {
|
impl Default for Klondike {
|
||||||
@@ -282,45 +412,222 @@ mod tests {
|
|||||||
value: Value::Six,
|
value: Value::Six,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert_eq!(testing_card.can_be_placed_on_top(&bad_same_suit), Err(StackingError::SameColour));
|
assert_eq!(testing_card.can_be_placed_on_pile(&bad_same_suit), Err(StackingError::SameColour));
|
||||||
let bad_same_colour = Card {
|
let bad_same_colour = Card {
|
||||||
suit: Suit::Diamond,
|
suit: Suit::Diamond,
|
||||||
value: Value::Six,
|
value: Value::Six,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert_eq!(testing_card.can_be_placed_on_top(&bad_same_colour), Err(StackingError::SameColour));
|
assert_eq!(testing_card.can_be_placed_on_pile(&bad_same_colour), Err(StackingError::SameColour));
|
||||||
let should_stack_card = Card {
|
let should_stack_card = Card {
|
||||||
suit: Suit::Club,
|
suit: Suit::Club,
|
||||||
value: Value::Six,
|
value: Value::Six,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert_eq!(testing_card.can_be_placed_on_top(&should_stack_card), Ok(()));
|
assert_eq!(testing_card.can_be_placed_on_pile(&should_stack_card), Ok(()));
|
||||||
let value_too_high = Card {
|
let value_too_high = Card {
|
||||||
suit: Suit::Club,
|
suit: Suit::Club,
|
||||||
value: Value::Seven,
|
value: Value::Seven,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let not_adj_error = testing_card.can_be_placed_on_top(&value_too_high);
|
let not_adj_error = testing_card.can_be_placed_on_pile(&value_too_high);
|
||||||
if let Err(e) = not_adj_error {
|
if let Err(e) = not_adj_error {
|
||||||
match e {
|
match e {
|
||||||
StackingError::NotAdjacent(_, _) => assert!(true),
|
StackingError::NotAdjacent(_, _) => assert!(true),
|
||||||
StackingError::SameColour => assert!(false, "Colour is different - incorrect error"),
|
StackingError::SameColour => assert!(false, "Colour is different - incorrect error"),
|
||||||
|
StackingError::WrongSuit => unreachable!(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert!(false, "Cards are not adjacent - should be an error")
|
assert!(false, "Cards are not adjacent - should be an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_foundation_stacking() {
|
||||||
|
let testing_card = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Ace,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(testing_card.can_be_placed_on_foundation(&None), Ok(()));
|
||||||
|
let testing_card = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Two,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert!(testing_card.can_be_placed_on_foundation(&None).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn moving_to_foundation() {
|
||||||
|
// GOOD: Ace from Pile to Empty Foundation
|
||||||
|
let mut klon = Klondike::default();
|
||||||
|
let ace = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Ace,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
klon.piles[0].push(ace.clone());
|
||||||
|
let source_card = CardAndPosition {
|
||||||
|
card: Some(ace.clone()),
|
||||||
|
pos: CardPosition::Pile(0, 1)
|
||||||
|
};
|
||||||
|
let dest_card = CardAndPosition {
|
||||||
|
card: None,
|
||||||
|
pos: CardPosition::Foundation(0),
|
||||||
|
};
|
||||||
|
assert!(klon.move_card_to_foundation(&source_card, &dest_card));
|
||||||
|
|
||||||
|
// BAD: Non-Ace from Pile to Empty Foundaction
|
||||||
|
let mut klon = Klondike::default();
|
||||||
|
let two = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Two,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
klon.piles[0].push(two.clone());
|
||||||
|
let source_card = CardAndPosition {
|
||||||
|
card: Some(two.clone()),
|
||||||
|
pos: CardPosition::Pile(0, 1)
|
||||||
|
};
|
||||||
|
let dest_card = CardAndPosition {
|
||||||
|
card: None,
|
||||||
|
pos: CardPosition::Foundation(0),
|
||||||
|
};
|
||||||
|
assert!(!klon.move_card_to_foundation(&source_card, &dest_card));
|
||||||
|
|
||||||
|
// GOOD: Ace from TopWaste to Empty Foundaction
|
||||||
|
let mut klon = Klondike::default();
|
||||||
|
let ace = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Ace,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
klon.waste.push(ace.clone());
|
||||||
|
let source_card = CardAndPosition {
|
||||||
|
card: Some(ace.clone()),
|
||||||
|
pos: CardPosition::TopWaste,
|
||||||
|
};
|
||||||
|
let dest_card = CardAndPosition {
|
||||||
|
card: None,
|
||||||
|
pos: CardPosition::Foundation(0),
|
||||||
|
};
|
||||||
|
assert!(klon.move_card_to_foundation(&source_card, &dest_card));
|
||||||
|
|
||||||
|
// BAD: Non-Ace from TopWaste to Empty Foundaction
|
||||||
|
let mut klon = Klondike::default();
|
||||||
|
let two = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Two,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
klon.waste.push(two.clone());
|
||||||
|
let source_card = CardAndPosition {
|
||||||
|
card: Some(two.clone()),
|
||||||
|
pos: CardPosition::TopWaste,
|
||||||
|
};
|
||||||
|
let dest_card = CardAndPosition {
|
||||||
|
card: None,
|
||||||
|
pos: CardPosition::Foundation(0),
|
||||||
|
};
|
||||||
|
assert!(!klon.move_card_to_foundation(&source_card, &dest_card));
|
||||||
|
|
||||||
|
// GOOD: Two from TopWaste to Foundaction with Ace there
|
||||||
|
let mut klon = Klondike::default();
|
||||||
|
let ace = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Ace,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
let two = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Two,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
klon.waste.push(two.clone());
|
||||||
|
klon.piles[0].push(ace.clone());
|
||||||
|
let source_card = CardAndPosition {
|
||||||
|
card: Some(ace.clone()),
|
||||||
|
pos: CardPosition::TopWaste,
|
||||||
|
};
|
||||||
|
let dest_card = CardAndPosition {
|
||||||
|
card: None,
|
||||||
|
pos: CardPosition::Foundation(0),
|
||||||
|
};
|
||||||
|
assert!(klon.move_card_to_foundation(&source_card, &dest_card));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO the following cases:
|
||||||
|
// - moving a card from pile to foundation when something is already there
|
||||||
|
// - moving Ace from waste to top of pile
|
||||||
|
// - moving a card frmo waste to foundation when something is already there
|
||||||
|
// => for cases where it'll both work and not work - when cards are / aren't present
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_to_foundation_with_wrong_suit() {
|
||||||
|
let mut klon = Klondike::default();
|
||||||
|
let ace = Card {
|
||||||
|
suit: Suit::Heart,
|
||||||
|
value: Value::Ace,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
let two = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Two,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
klon.waste.push(two.clone());
|
||||||
|
klon.piles[0].push(ace.clone());
|
||||||
|
let source_card = CardAndPosition {
|
||||||
|
card: Some(ace.clone()),
|
||||||
|
pos: CardPosition::TopWaste,
|
||||||
|
};
|
||||||
|
let dest_card = CardAndPosition {
|
||||||
|
card: Some(two.clone()),
|
||||||
|
pos: CardPosition::Foundation(0),
|
||||||
|
};
|
||||||
|
assert!(!klon.move_card_to_foundation(&source_card, &dest_card));
|
||||||
|
// TODO this is passing - but I don't think it's passing for the right reason
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_ace_to_foundation_with_card_present() {
|
||||||
|
let mut klon = Klondike::default();
|
||||||
|
let ace_heart = Card {
|
||||||
|
suit: Suit::Heart,
|
||||||
|
value: Value::Ace,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
let ace_spade = Card {
|
||||||
|
suit: Suit::Spade,
|
||||||
|
value: Value::Ace,
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
klon.waste.push(ace_heart.clone());
|
||||||
|
klon.piles[0].push(ace_spade.clone());
|
||||||
|
let source_card = CardAndPosition {
|
||||||
|
card: Some(ace_heart.clone()),
|
||||||
|
pos: CardPosition::TopWaste,
|
||||||
|
};
|
||||||
|
let dest_card = CardAndPosition {
|
||||||
|
card: Some(ace_spade.clone()),
|
||||||
|
pos: CardPosition::Foundation(0),
|
||||||
|
};
|
||||||
|
assert!(!klon.move_card_to_foundation(&source_card, &dest_card));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_a_whole_deck() {
|
fn get_a_whole_deck() {
|
||||||
let d = Deck::default();
|
let d = Deck::default();
|
||||||
assert_eq!(d.cards.len(), 52); // Probably should test whether all cards are in... eh
|
assert_eq!(d.cards.len(), 52); // Probably should test whether all cards are in... eh
|
||||||
println!("{:#?}", d); // A "manual" review looks alright
|
//println!("{:#?}", d); // A "manual" review looks alright
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn klondike() {
|
fn klondike() {
|
||||||
let k = Klondike::default();
|
let k = Klondike::default();
|
||||||
println!("{:#?}", k);
|
//println!("{:#?}", k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user