diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..0e74f03 --- /dev/null +++ b/src/game.rs @@ -0,0 +1,52 @@ +// A game is a self-contained struct that holds everything that an instance of +// Tetris needs to run, except for something to tick the time forward. + +use crate::playfield::PlayField; +use crate::srs::RotationSystem; +use crate::srs::SRS; + +pub struct Game { + playfield: PlayField, + rotation_system: RS, + level: u8, + points: u64, +} + +trait PlayerControllable { + fn move_left(&mut self); + fn move_up(&mut self); + fn move_right(&mut self); + fn move_down(&mut self); + fn rotate_left(&mut self); + fn rotate_right(&mut self); + fn hard_drop(&mut self); + fn hold(&mut self); +} + +impl PlayerControllable for Game { + fn move_left(&mut self) {} + fn move_up(&mut self) {} + fn move_right(&mut self) {} + fn move_down(&mut self) {} + fn rotate_left(&mut self) {} + fn rotate_right(&mut self) {} + fn hard_drop(&mut self) {} + + fn hold(&mut self) { + // if self.can_swap_hold { + // match self.hold_piece { + // None => { + // self.hold_piece = Some(self.active_piece); + // self.get_new_piece(); + // } + // Some(piece) => { + // self.hold_piece = Some(self.active_piece); + // self.active_piece = piece; + // self.reset_position(); + // } + // } + + // self.can_swap_hold = false; + // } + } +} diff --git a/src/main.rs b/src/main.rs index ba5f5ca..af380c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ +mod game; +mod playfield; mod random; +mod srs; #[derive(Debug, Copy, Clone)] -pub enum Tetrino { +pub enum Tetromino { I, O, T, diff --git a/src/playfield.rs b/src/playfield.rs new file mode 100644 index 0000000..a12b26e --- /dev/null +++ b/src/playfield.rs @@ -0,0 +1,76 @@ +use crate::random::RandomSystem; +use crate::Tetromino; +use std::collections::VecDeque; + +#[derive(Copy, Clone)] +enum MinoColor { + Cyan, + Yellow, + Purple, + Green, + Red, + Blue, + Orange, + Gray, +} + +enum Movement { + Rotate, + Gravity, + Translation, +} + +pub struct Position { + x: u8, + y: u8, +} + +pub struct PlayField { + can_swap_hold: bool, + hold_piece: Option, + field: [[Option; 10]; 40], // access via y, x + active_piece: Tetromino, + active_pos: Position, + bag: RandomSystem, + next_pieces: VecDeque, + last_movement: Movement, +} + +impl PlayField { + pub fn new() -> Self { + let mut bag = RandomSystem::new(); + let active_piece = bag.get_tetrino(); + let mut next_pieces = VecDeque::with_capacity(3); + for _ in 0..next_pieces.len() { + next_pieces.push_back(bag.get_tetrino()); + } + + PlayField { + can_swap_hold: true, + hold_piece: None, + field: [[None; 10]; 40], + active_piece, + active_pos: Position { x: 0, y: 0 }, + bag, + next_pieces, + last_movement: Movement::Gravity, + } + } + + fn get_new_piece(&mut self) { + self.active_piece = self + .next_pieces + .pop_front() + .expect("visible queue to be populated"); + self.next_pieces.push_back(self.bag.get_tetrino()); + self.reset_position(); + self.can_swap_hold = true; + } + + /// Place the current Tetromino at its default spawn position and + /// orientation. Used for hold-swapping or spawning in a new piece. + fn reset_position(&mut self) { + self.last_movement = Movement::Gravity; + unimplemented!(); + } +} diff --git a/src/random.rs b/src/random.rs index 5f09e6e..d105fa4 100644 --- a/src/random.rs +++ b/src/random.rs @@ -1,9 +1,9 @@ -use crate::Tetrino; +use crate::Tetromino; use rand::{rngs::ThreadRng, seq::SliceRandom, thread_rng}; pub struct RandomSystem { rng: ThreadRng, - bag: [Tetrino; 7], + bag: [Tetromino; 7], cur_pos: usize, } @@ -12,12 +12,12 @@ impl RandomSystem { let rng = thread_rng(); RandomSystem { rng: rng, - bag: [Tetrino::I; 7], + bag: [Tetromino::I; 7], // Default value, should get init on first get cur_pos: 0, } } - pub fn get_tetrino(&mut self) -> Tetrino { + pub fn get_tetrino(&mut self) -> Tetromino { if self.cur_pos == 0 { self.refresh_bag(); } @@ -28,13 +28,13 @@ impl RandomSystem { fn refresh_bag(&mut self) { self.bag = [ - Tetrino::I, - Tetrino::O, - Tetrino::T, - Tetrino::S, - Tetrino::Z, - Tetrino::J, - Tetrino::L, + Tetromino::I, + Tetromino::O, + Tetromino::T, + Tetromino::S, + Tetromino::Z, + Tetromino::J, + Tetromino::L, ]; self.bag.shuffle(&mut self.rng); self.cur_pos = 0; diff --git a/src/srs.rs b/src/srs.rs new file mode 100644 index 0000000..cde06c7 --- /dev/null +++ b/src/srs.rs @@ -0,0 +1,111 @@ +use crate::playfield::{PlayField, Position}; +use crate::Tetromino; + +#[derive(Copy)] +pub struct Offset { + x: i8, + y: i8, +} + +pub enum RotationDirection { + Clockwise, + AntiClockwise, +} + +pub trait RotationSystem { + fn get_rotation_offset( + piece: &Tetromino, + center: &Position, + direction: RotationDirection, + playfield: &PlayField, + ) -> Option; +} + +pub struct SRS {} + +impl RotationSystem for SRS { + fn get_rotation_offset( + piece: &Tetromino, + center: &Position, + direction: RotationDirection, + playfield: &PlayField, + ) -> Option { + None + } +} + +#[derive(PartialEq, Eq, Hash)] +enum Rotation { + O, // Spawn state + R, // Right rotation: clockwise rotation from spawn state + U, // Upside-down rotation: rotation after 2 left or right rotations from spawn state + L, // Left rotation: counterclockwise rotation from spawn state +} + +struct OffsetData { + O: Vec, + R: Vec, + U: Vec, + L: Vec, +} + +impl OffsetData { + pub fn apply_right_rotation() {} +} + +const JLSTZOffsetData: OffsetData = OffsetData { + O: vec![Offset { x: 0, y: 0 }], + R: vec![ + Offset { x: 0, y: 0 }, + Offset { x: 1, y: 0 }, + Offset { x: 1, y: -1 }, + Offset { x: 0, y: 2 }, + Offset { x: 1, y: 2 }, + ], + U: vec![Offset { x: 0, y: 0 }], + L: vec![ + Offset { x: 0, y: 0 }, + Offset { x: -1, y: 0 }, + Offset { x: -1, y: -1 }, + Offset { x: 0, y: 2 }, + Offset { x: -1, y: 2 }, + ], +}; + +const IOffsetData: OffsetData = OffsetData { + O: vec![ + Offset { x: 0, y: 0 }, + Offset { x: -1, y: 0 }, + Offset { x: 2, y: 0 }, + Offset { x: -1, y: 0 }, + Offset { x: 2, y: 0 }, + ], + R: vec![ + Offset { x: -1, y: 0 }, + Offset { x: 0, y: 0 }, + Offset { x: 0, y: 0 }, + Offset { x: 0, y: 1 }, + Offset { x: 0, y: -2 }, + ], + U: vec![ + Offset { x: -1, y: 1 }, + Offset { x: 1, y: 1 }, + Offset { x: -2, y: 1 }, + Offset { x: 1, y: 0 }, + Offset { x: -2, y: 0 }, + ], + L: vec![ + Offset { x: 0, y: 1 }, + Offset { x: 0, y: 1 }, + Offset { x: 0, y: 1 }, + Offset { x: 0, y: -1 }, + Offset { x: 0, y: 2 }, + ], +}; + +const OOffsetData: OffsetData = OffsetData { + O: vec![Offset { x: 0, y: 0 }], + R: vec![Offset { x: 0, y: -1 }], + U: vec![Offset { x: -1, y: -1 }], + L: vec![Offset { x: -1, y: 0 }], +};