From b918f31663a0c74099fbbcc19310df12b8c744cd Mon Sep 17 00:00:00 2001 From: Oxore Date: Sun, 30 Jul 2023 13:59:21 +0300 Subject: Initial commit --- src/main.rs | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 src/main.rs (limited to 'src') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..245c2b1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,277 @@ +extern crate termios; + +use rand::Rng; +use std::io::Read; + +const FIELD_COLS: usize = 10; +const FIELD_ROWS_VISIBLE: usize = 20; +const FIELD_ROWS: usize = FIELD_ROWS_VISIBLE + 20; +const FIELD_SIZE: usize = FIELD_COLS * FIELD_ROWS; +const FIGURE_SIZE_COLS: usize = 4; +const FIGURE_SIZE_ROWS: usize = 4; +const FIGURE_SIZE: usize = FIGURE_SIZE_COLS*FIGURE_SIZE_ROWS; +const CELLS_I: [i8; FIGURE_SIZE] = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0]; +const CELLS_L2:[i8; FIGURE_SIZE] = [0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0]; +const CELLS_L: [i8; FIGURE_SIZE] = [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0]; +const CELLS_B: [i8; FIGURE_SIZE] = [0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0]; +const CELLS_S: [i8; FIGURE_SIZE] = [0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0]; +const CELLS_T: [i8; FIGURE_SIZE] = [0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0]; +const CELLS_Z: [i8; FIGURE_SIZE] = [0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0]; + +#[derive(Clone)] +#[derive(Debug)] +enum FigureKind { + I, + L2, + L, + T, + B, + S, + Z, +} + +#[derive(Clone)] +struct Figure { + kind: FigureKind, + cells: [i8; FIGURE_SIZE], +} + +impl Figure { + fn kind_random() -> Self { + return match rand::thread_rng().gen_range(0..7) { + 0 => Self::kind_i(), + 1 => Self::kind_l2(), + 2 => Self::kind_l(), + 3 => Self::kind_b(), + 4 => Self::kind_s(), + 5 => Self::kind_t(), + 6 => Self::kind_z(), + _ => unreachable!(), + }; + } + const fn kind_i() -> Self { Self{ cells: CELLS_I, kind: FigureKind::I } } + const fn kind_l2() -> Self { Self{ cells: CELLS_L2, kind: FigureKind::L2 } } + const fn kind_l() -> Self { Self{ cells: CELLS_L, kind: FigureKind::L } } + const fn kind_b() -> Self { Self{ cells: CELLS_B, kind: FigureKind::B } } + const fn kind_s() -> Self { Self{ cells: CELLS_S, kind: FigureKind::S } } + const fn kind_t() -> Self { Self{ cells: CELLS_T, kind: FigureKind::T } } + const fn kind_z() -> Self { Self{ cells: CELLS_Z, kind: FigureKind::Z } } + fn rotate_cw_4x4(&mut self) { + let mut tmp: [i8; 4 * 4] = Default::default(); + for y in 0..4 { + for x in 0..4 { + tmp[y + (3 - x) * 4] = self.cells[x + y * 4]; + } + } + self.cells = tmp; + } + fn rotate_cw_3x3(&mut self) { + let mut tmp: [i8; 3 * 3] = Default::default(); + for y in 0..3 { + for x in 0..3 { + tmp[y + (2 - x) * 3] = self.cells[x + y * 4]; + } + } + for y in 0..3 { + for x in 0..3 { + self.cells[x + y * 4] = tmp[x + y * 3]; + } + } + } + fn rotate(&mut self) { + match self.kind { + FigureKind::I => self.rotate_cw_4x4(), + FigureKind::L2 => self.rotate_cw_3x3(), + FigureKind::L => self.rotate_cw_3x3(), + FigureKind::B => {}, + FigureKind::S => self.rotate_cw_3x3(), + FigureKind::T => self.rotate_cw_3x3(), + FigureKind::Z => self.rotate_cw_3x3(), + } + } +} + +#[derive(Clone)] +struct Field { + cells: [i8; FIELD_SIZE], +} + +impl Default for Field { + #[inline] + fn default() -> Self { + Self { cells: [0; FIELD_SIZE] } + } +} + +impl Field { + fn render(&mut self, figure: &Figure, x: isize, y: isize) { + for i in 0..FIGURE_SIZE { + let f_x = (i % FIGURE_SIZE_COLS) as isize + x; + let f_y = (i / FIGURE_SIZE_ROWS) as isize + y; + if f_x < 0 { + continue; + } else if f_x >= FIELD_COLS as isize { + continue; + } else if f_y < 0 { + continue; + } else if f_y >= FIELD_ROWS as isize { + continue; + } + if figure.cells[i] != 0 { + self.cells[f_x as usize + f_y as usize * FIELD_COLS] = figure.cells[i]; + } + } + } + fn has_collision(&self, figure: &Figure, x: isize, y: isize) -> bool { + for i in 0..FIGURE_SIZE { + let f_x = (i % FIGURE_SIZE_COLS) as isize + x; + let f_y = (i / FIGURE_SIZE_ROWS) as isize + y; + if figure.cells[i] != 0 { + if f_x < 0 { + return true; + } else if f_x >= FIELD_COLS as isize { + return true; + } else if f_y < 0 { + return true; + } else if f_y >= FIELD_ROWS as isize { + return true; + } else if self.cells[f_x as usize + f_y as usize * FIELD_COLS] != 0 { + return true; + } + } + } + false + } +} + +struct Game { + field: Field, + figure: Figure, + x: isize, + y: isize, +} + +impl Default for Game { + #[inline] + fn default() -> Self { + Self{ field: Field::default(), figure: Figure::kind_random(), x: 0, y: 0 } + } +} + +impl Game { + fn place(&mut self) { + self.field.render(&self.figure, self.x, self.y); + self.figure = Figure::kind_random(); + self.x = 0; + self.y = 0; + } + fn step(&mut self, fd: &mut std::io::Stdin) -> Option<()> { + let mut r: [u8;1]=[0]; + fd.read(&mut r[..]).unwrap(); + match r[0] as char { + 'q' => return None, + ' ' => { + if !self.field.has_collision(&self.figure, self.x, self.y) { + self.place() + } + }, + 'h' => { + if self.field.has_collision(&self.figure, self.x, self.y) { + self.x -= 1 + } else if !self.field.has_collision(&self.figure, self.x - 1, self.y) { + self.x -= 1 + } + } + 'j' => { + if self.field.has_collision(&self.figure, self.x, self.y) { + self.y -= 1 + } else if !self.field.has_collision(&self.figure, self.x, self.y - 1) { + self.y -= 1 + } + } + 'k' => { + if self.field.has_collision(&self.figure, self.x, self.y) { + self.y += 1 + } else if !self.field.has_collision(&self.figure, self.x, self.y + 1) { + self.y += 1 + } + } + 'l' => { + if self.field.has_collision(&self.figure, self.x, self.y) { + self.x += 1 + } else if !self.field.has_collision(&self.figure, self.x + 1, self.y) { + self.x += 1 + } + } + ';' => { + let mut figure = self.figure.clone(); + figure.rotate(); + if !self.field.has_collision(&figure, self.x, self.y) { + self.figure.rotate() + } + } + _ => {}, + } + Some(()) + } +} + +fn print_field(field: &Field) { + cursor_to_home(); + clear_line(); + for y in 0..FIELD_ROWS_VISIBLE { + for x in 0..FIELD_COLS { + let c = field.cells[(FIELD_ROWS_VISIBLE - 1 - y) * FIELD_COLS + x]; + if c == 0 { + print!("██"); + } else { + print!(" "); + } + } + print!("\r\x1b[1B"); + } +} + +fn display_game(game: &Game) { + cursor_to_home(); + let mut field = game.field.clone(); + field.render(&game.figure, game.x, game.y); + print_field(&field); + if game.field.has_collision(&game.figure, game.x, game.y) { + println!("Collision!"); + } else { + clear_line(); + } +} + +fn clear_screen() { + print!("\x1B[2J"); +} + +fn cursor_to_home() { + print!("\x1B[H"); +} + +fn clear_line() { + println!("\x1B[2K"); +} + +fn main() { + let termios_state_initial = termios::Termios::from_fd(0).unwrap(); + let mut termios_state = termios_state_initial.clone(); + let c_lflag = termios_state.c_lflag; + termios_state.c_lflag &= !(termios::ICANON | termios::ECHO); + termios::tcsetattr(0, termios::TCSADRAIN, &termios_state).unwrap(); + termios_state.c_lflag = c_lflag; + clear_screen(); + let mut game = Game::default(); + display_game(&game); + let mut stdin = std::io::stdin(); + loop { + if let None = game.step(&mut stdin) { + break; + } + display_game(&game); + } + termios::tcsetattr(0, termios::TCSADRAIN, &termios_state_initial).unwrap(); +} -- cgit v1.2.3