use bevy::{prelude::*, utils::HashMap}; /// Deck and Cards pub struct DeckPlugin; impl Plugin for DeckPlugin { fn build(&self, _app: &mut App) { // Nothing yet! } } /// We expect 81 cards total #[derive(Resource)] pub(crate) struct Deck { pub cards: HashMap, } impl Deck { pub(crate) fn cards() -> Vec { let mut v = Vec::new(); [ItemColor::Red, ItemColor::Green, ItemColor::Purple] .iter() .for_each(|&color| { [ItemShape::Oval, ItemShape::Diamond, ItemShape::Squiggle] .iter() .for_each(|&shape| { [ItemPattern::Open, ItemPattern::Solid, ItemPattern::Striped] .iter() .for_each(|&pattern| { [ItemNumber::One, ItemNumber::Two, ItemNumber::Three] .iter() .for_each(|&number| { v.push(Card { color, shape, pattern, number, }) }); }); }); }); v } pub(crate) fn iter_cards() -> impl Iterator { Self::cards().into_iter() } pub(crate) fn shuffled() -> Vec { let mut base = Self::cards(); { use rand::seq::SliceRandom; use rand::thread_rng; let mut rng = thread_rng(); base.as_mut_slice().shuffle(&mut rng); } base } pub(crate) fn iter_shuffled() -> impl Iterator { Deck::shuffled().into_iter() } } /// A card describes abstractly the contents of a card /// including color of items, number of items, pattern, and shape #[derive(PartialEq, Eq, Hash, Clone, Copy, Component, Debug)] pub(crate) struct Card { pub color: ItemColor, pub number: ItemNumber, pub pattern: ItemPattern, pub shape: ItemShape, } impl Card { pub(crate) fn sprite(&self) -> (String, TextureAtlasLayout, u8) { // Spritesheet contains all cards ordered like so: // | squiggle | diamond | oval | // | 1 | 2 | 3 | 1 | 2 | 3 | 1 | 2 | 3 | // | open green // | open blue // | open red // | shaded green // | shaded blue // | shaded red // | filled green // | filled blue // | filled red // with all sprites being 20x32 // we have 12 rows and 9 columns total // so the col + row for 1+open+red+squiggle is col 0 + row 3 // and the col + row for 3 filled blue oval is col 8 + row 11 let num = { let row = { let pattern_row = match self.pattern { ItemPattern::Open => 0, ItemPattern::Striped => 1, ItemPattern::Solid => 2, }; let color_row = match self.color { ItemColor::Green => 0, ItemColor::Purple => 1, ItemColor::Red => 2, }; (3 * pattern_row) + color_row + 3 }; let col = { let shape_col = match self.shape { ItemShape::Squiggle => 0, ItemShape::Diamond => 1, ItemShape::Oval => 2, }; let number_col = match self.number { ItemNumber::One => 0, ItemNumber::Two => 1, ItemNumber::Three => 2, }; (3 * shape_col) + number_col }; assert!(row < 12, "Sprite row should be less than 12"); assert!(row > 2, "sprite row should be greater than 2"); assert!(col < 9, "Sprite column should be less than 9"); // Figure out the sprite number based on row + col (9 * row) + col }; assert!(num < 108, "Sprite index should be less than 108"); let size = UVec2 { x: 20, y: 32 }; let layout = TextureAtlasLayout::from_grid(size, 9, 12, None, None); ("cards.png".into(), layout, num) } } impl std::fmt::Display for Card { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{} {} {} {}", self.number, self.pattern, self.color, self.shape ) } } /// Item colors for the card may be red, green or purple /// All items must be the same color #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub(crate) enum ItemColor { Red, Green, Purple, } impl std::fmt::Display for ItemColor { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let color = match self { ItemColor::Red => "red", ItemColor::Green => "green", ItemColor::Purple => "purple", }; write!(f, "{}", color) } } /// A card may have 1 to 3 items #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub(crate) enum ItemNumber { One, Two, Three, } impl std::fmt::Display for ItemNumber { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let num = match self { ItemNumber::One => 1, ItemNumber::Two => 2, ItemNumber::Three => 3, }; write!(f, "{}", num) } } /// Each item can be solid, striped, or open /// a card has all of it's item as one pattern #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub(crate) enum ItemPattern { Solid, Striped, Open, } impl std::fmt::Display for ItemPattern { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let fill = match self { ItemPattern::Open => "empty", ItemPattern::Striped => "shaded", ItemPattern::Solid => "solid", }; write!(f, "{}", fill) } } /// Each item can be an oval, diamond, or squiggle /// All items on a card are one shape #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub(crate) enum ItemShape { Oval, Diamond, Squiggle, } impl std::fmt::Display for ItemShape { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let shape = match self { ItemShape::Oval => "oval", ItemShape::Diamond => "diamond", ItemShape::Squiggle => "squiggle", }; write!(f, "{}", shape) } }