summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs277
1 files changed, 277 insertions, 0 deletions
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();
+}