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(); }