130 lines
2.9 KiB
Rust
130 lines
2.9 KiB
Rust
use crate::Rng;
|
|
|
|
// TODO: pub struct ValidInstruction<I>(I);
|
|
pub trait Game {
|
|
type Instruction;
|
|
fn enumerate_instructions(&self) -> impl Iterator<Item = Self::Instruction>;
|
|
fn validate_instruction(&self, instruction: Self::Instruction) -> bool;
|
|
fn process_instruction(&mut self, instruction: Self::Instruction);
|
|
}
|
|
|
|
/// An identifier which specifies the deck id, suit, and card value.
|
|
/// 2 bits for deck ID
|
|
/// 2 bits for suit ID
|
|
/// 4 bits for card Value
|
|
/// TODO: better encoding for slightly more decks
|
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
|
pub struct Card(u8);
|
|
pub struct CardValue(u8);
|
|
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
|
pub enum Suit {
|
|
Spades,
|
|
Hearts,
|
|
Clubs,
|
|
Diamonds,
|
|
}
|
|
impl Card {
|
|
pub fn value(&self) -> CardValue {
|
|
let masked = self.0 & 0b1111;
|
|
CardValue(masked)
|
|
}
|
|
pub fn suit(&self) -> Suit {
|
|
let red = self.is_red();
|
|
let kiki = self.is_kiki();
|
|
match (kiki, red) {
|
|
(false, false) => Suit::Spades,
|
|
(false, true) => Suit::Hearts,
|
|
(true, false) => Suit::Clubs,
|
|
(true, true) => Suit::Diamonds,
|
|
}
|
|
}
|
|
/// Is the suit red.
|
|
pub fn is_red(&self) -> bool {
|
|
self.0 & 0b010000 != 0
|
|
}
|
|
/// Is the suit shape spikey. (Bouba/kiki)
|
|
pub fn is_kiki(&self) -> bool {
|
|
self.0 & 0b100000 != 0
|
|
}
|
|
pub fn deck(&self) -> u8 {
|
|
self.0 >> 6
|
|
}
|
|
}
|
|
|
|
pub struct Stack(Vec<Card>);
|
|
impl Stack {
|
|
/// Generate a full deck of cards with the specified deck id.
|
|
pub fn full_deck(deck_id: u8) -> Stack {
|
|
let mut stack = Vec::with_capacity(52);
|
|
for suit in 0..4 {
|
|
for value in 1..=13 {
|
|
stack.push(Card(deck_id << 6 | suit << 4 | value));
|
|
}
|
|
}
|
|
Stack(stack)
|
|
}
|
|
pub fn shuffle<R: rand::Rng>(&mut self, rng: &mut R) {
|
|
use rand::seq::SliceRandom;
|
|
self.0.shuffle(rng);
|
|
}
|
|
pub fn push(&mut self, card: Card) {
|
|
self.0.push(card);
|
|
}
|
|
pub fn pop(&mut self) -> Option<Card> {
|
|
self.0.pop()
|
|
}
|
|
pub fn is_empty(&mut self) -> bool {
|
|
self.0.is_empty()
|
|
}
|
|
}
|
|
|
|
pub struct Pile {
|
|
face_down: Stack,
|
|
face_up: Stack,
|
|
}
|
|
impl Pile {
|
|
pub fn pop(&mut self) -> Option<Card> {
|
|
let card = self.face_up.pop()?;
|
|
if self.face_up.is_empty() {
|
|
if let Some(card) = self.face_down.pop() {
|
|
self.face_up.push(card);
|
|
}
|
|
}
|
|
Some(card)
|
|
}
|
|
pub fn push(&mut self, card: Card) {
|
|
self.face_up.push(card);
|
|
}
|
|
}
|
|
|
|
pub struct Session<G: Game> {
|
|
seed: Rng,
|
|
state: G,
|
|
history: Vec<G::Instruction>,
|
|
}
|
|
impl<G: Game> Session<G> {
|
|
pub fn new(seed: Rng, state: G) -> Self {
|
|
Self {
|
|
seed,
|
|
state,
|
|
history: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
impl<G: Game> Game for Session<G>
|
|
where
|
|
G::Instruction: Clone,
|
|
{
|
|
type Instruction = G::Instruction;
|
|
fn enumerate_instructions(&self) -> impl Iterator<Item = Self::Instruction> {
|
|
self.state.enumerate_instructions()
|
|
}
|
|
fn validate_instruction(&self, instruction: Self::Instruction) -> bool {
|
|
self.state.validate_instruction(instruction)
|
|
}
|
|
fn process_instruction(&mut self, instruction: Self::Instruction) {
|
|
self.history.push(instruction.clone());
|
|
self.state.process_instruction(instruction);
|
|
}
|
|
}
|