commit c4d86b81bac818f6fd02fd73eae2f1273c423ca5 Author: mii Date: Fri Oct 1 15:36:41 2021 +0900 rustris_base diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c9bbdba --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,273 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossterm" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486d44227f71a1ef39554c0dc47e44b9f4139927c75043312690c3f476d1d788" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507" +dependencies = [ + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "instant" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "libc" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mio" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustris" +version = "0.1.0" +dependencies = [ + "crossterm", + "rand", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "signal-hook" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fb4c86b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rustris" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +crossterm = "0.21.0" +rand = "0.8.4" diff --git a/src/block.rs b/src/block.rs new file mode 100644 index 0000000..296ef90 --- /dev/null +++ b/src/block.rs @@ -0,0 +1,16 @@ +#[derive(Debug, Clone, Copy)] +pub enum Block { + Air, + Block, + Ghost +} + +impl Block { + pub fn show(self) -> String { + match self { + Block::Air => String::from("・"), + Block::Block => String::from("■"), + Block::Ghost => String::from("□"), + } + } +} diff --git a/src/game_data.rs b/src/game_data.rs new file mode 100644 index 0000000..2491edd --- /dev/null +++ b/src/game_data.rs @@ -0,0 +1,93 @@ +use crate::{ + mino::*, + block::*, + mino_rotation::* +}; + +#[derive(Debug)] +pub struct GameData { + pub minos: Vec, + pub field: Vec>, + pub control_mino: Option, + pub mino_pos: (i32, i32), + pub next_minos: Vec, + pub mino_rotation: MinoRotation, + pub field_size: (usize, usize), +} + +impl GameData { + pub fn new(field_size: (usize, usize)) -> GameData { + let minos: Vec = vec![ + Mino { + id: String::from("I"), + shape: vec![ + vec![false, false, true ,false], + vec![false, false, true ,false], + vec![false, false, true ,false], + vec![false, false, true ,false], + ] + }, + Mino { + id: String::from("J"), + shape: vec![ + vec![false, true, false], + vec![false, true, false], + vec![true, true, false], + ] + }, + Mino { + id: String::from("L"), + shape: vec![ + vec![false ,true ,false], + vec![false ,true ,false], + vec![false ,true ,true], + ] + }, + Mino { + id: String::from("S"), + shape: vec![ + vec![false ,true ,true], + vec![true ,true ,false], + vec![false ,false ,false], + ] + }, + Mino { + id: String::from("Z"), + shape: vec![ + vec![true ,true ,false], + vec![false ,true ,true], + vec![false ,false ,false], + ] + }, + Mino { + id: String::from("T"), + shape: vec![ + vec![false ,true ,false], + vec![true ,true ,true], + vec![false ,false ,false], + ] + }, + Mino { + id: String::from("O"), + shape: vec![ + vec![true ,true], + vec![true ,true], + ] + } + ]; + + let field: Vec> = vec![vec![Block::Air; field_size.0]; field_size.1]; + + let next_minos = minos.clone(); + + GameData { + minos, + field, + control_mino: None, + mino_pos: (0, 0) , + next_minos, + mino_rotation: MinoRotation::Up, + field_size + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e90db2d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,177 @@ +mod game_data; +mod mino; +mod block; +mod rustris; +mod mino_rotation; +use std::{io::stdout, sync::{Arc, Mutex}, thread, time::Duration}; + +use crossterm::{cursor, event::{Event, KeyCode, KeyEvent, KeyModifiers, read}, execute, style::{Print}, terminal::{self, Clear, ClearType, enable_raw_mode}}; + +use crate::{block::Block, rustris::Rustris}; + +fn main() { + let stdout = Arc::new(Mutex::new(stdout())); + enable_raw_mode().unwrap(); + + { + let mut stdout = stdout.lock().unwrap(); + execute!( + stdout, + cursor::MoveTo(0, 0), + cursor::Hide, + Clear(ClearType::All) + ).unwrap(); + } + + let console_size = terminal::size().expect("Error: Cannot get console size."); + for _ in 0..console_size.1 { + for _ in 0..console_size.0 { + print!(" "); + } + println!(); + } + + let rustris = Arc::new(Mutex::new(Rustris::new((10, 21)))); + + { + let mut rustris = rustris.lock().unwrap(); + rustris.game_data.control_mino = Some(rustris.get_next_mino()); + } + + let rustris_rc = Arc::clone(&rustris); + let stdout_rc = Arc::clone(&stdout); + thread::spawn(move || { + loop { + thread::sleep(Duration::from_secs(1)); + { + let mut rustris_rc = rustris_rc.lock().unwrap(); + if !rustris_rc.move_mino(1, 0) { + rustris_rc.place_control_mino(Block::Block); + rustris_rc.game_data.control_mino = Some(rustris_rc.get_next_mino()); + rustris_rc.game_data.mino_pos = (0, 5); + } + } + { + let mut stdout = stdout_rc.lock().unwrap(); + let buf = rustris_rc.lock().unwrap().show(); + execute!(stdout, Print(buf)).unwrap() + } + } + }); + + loop { + { + let mut rustris = rustris.lock().unwrap(); + let mut stdout = stdout.lock().unwrap(); + let buf = rustris.show(); + execute!(stdout, Print(buf)).unwrap(); + } + + match read().unwrap() { + Event::Key(KeyEvent { + code: KeyCode::Right, + modifiers: KeyModifiers::NONE + }) => { + let mut rustris = rustris.lock().unwrap(); + rustris.move_mino(0, 1); + } + + Event::Key(KeyEvent { + code: KeyCode::Left, + modifiers: KeyModifiers::NONE + }) => { + let mut rustris = rustris.lock().unwrap(); + rustris.move_mino(0, -1); + } + + Event::Key(KeyEvent { + code: KeyCode::Down, + modifiers: KeyModifiers::NONE + }) => { + let mut rustris = rustris.lock().unwrap(); + rustris.move_mino(1, 0); + } + + Event::Key(KeyEvent { + code: KeyCode::Up, + modifiers: KeyModifiers::NONE + }) => { + let mut rustris = rustris.lock().unwrap(); + while rustris.move_mino(1, 0) { } + + rustris.place_control_mino(Block::Block); + rustris.game_data.control_mino = Some(rustris.get_next_mino()); + rustris.game_data.mino_pos = (0, 5); + } + + Event::Key(KeyEvent { + code: KeyCode::Char('x'), + modifiers: KeyModifiers::NONE + }) => { + let mut rustris = rustris.lock().unwrap(); + rustris.rotate_mino_to_right(); + } + + Event::Key(KeyEvent { + code: KeyCode::Char('z'), + modifiers: KeyModifiers::NONE + }) => { + let mut rustris = rustris.lock().unwrap(); + rustris.rotate_mino_to_left(); + } + + + Event::Key(KeyEvent { + code: KeyCode::Char('q'), + modifiers: KeyModifiers::NONE + }) => { + break; + } + + _ => () + } + + + { + let mut rustris = rustris.lock().unwrap(); + for x in 0..(rustris.game_data.field_size.1) { + let mut air = false; + for y in 0..(rustris.game_data.field_size.0) { + if let Block::Air = rustris.game_data.field[x][y] { + air = true; + } + } + + if !air { + for y in 0..(rustris.game_data.field_size.0) { + rustris.game_data.field[x][y] = Block::Air; + } + + for x2 in (0..(rustris.game_data.field_size.1)).rev() { + if x2 < x && x2 < 20 { + for y2 in 0..(rustris.game_data.field_size.0) { + rustris.game_data.field[x2 + 1][y2] = rustris.game_data.field[x2][y2]; + rustris.game_data.field[x2][y2] = Block::Air; + } + } + } + } + } + } + } + + { + let mut stdout = stdout.lock().unwrap(); + execute!( + stdout, + cursor::MoveTo(0, 0), + cursor::Show, + Clear(ClearType::All) + ).unwrap(); + } +} + +#[cfg(test)] +mod tests { + +} diff --git a/src/mino.rs b/src/mino.rs new file mode 100644 index 0000000..2079bf9 --- /dev/null +++ b/src/mino.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone)] +pub struct Mino { + pub id: String, + pub shape: Vec> +} diff --git a/src/mino_rotation.rs b/src/mino_rotation.rs new file mode 100644 index 0000000..98faf83 --- /dev/null +++ b/src/mino_rotation.rs @@ -0,0 +1,69 @@ +#[derive(Debug, Clone, PartialEq)] +pub enum MinoRotation { + Up, + Right, + Down, + Left +} + +impl MinoRotation { + pub fn to_count(&self) -> i32 { + match self { + MinoRotation::Up => 0, + MinoRotation::Right => 1, + MinoRotation::Down => 2, + MinoRotation::Left => 3 + } + } + + pub fn get(rotation: i32) -> MinoRotation { + let mut rotation = rotation; + if rotation < 0 { + rotation += 4; + } + rotation %= 4; + + match rotation { + 0 => MinoRotation::Up, + 1 => MinoRotation::Right, + 2 => MinoRotation::Down, + 3 => MinoRotation::Left, + _ => MinoRotation::Up + } + } +} + +#[cfg(test)] +mod mino_rotation_test { + use super::*; + + #[test] + fn convert_rotate_test() { + assert_eq!(MinoRotation::get(-1), MinoRotation::Left); + } + + #[test] + fn convert_rotate_test1() { + assert_eq!(MinoRotation::get(0), MinoRotation::Up); + } + + #[test] + fn convert_rotate_test2() { + assert_eq!(MinoRotation::get(1), MinoRotation::Right); + } + + #[test] + fn convert_rotate_test3() { + assert_eq!(MinoRotation::get(2), MinoRotation::Down); + } + + #[test] + fn convert_rotate_test4() { + assert_eq!(MinoRotation::get(3), MinoRotation::Left); + } + + #[test] + fn convert_rotate_test5() { + assert_eq!(MinoRotation::get(4), MinoRotation::Up); + } +} \ No newline at end of file diff --git a/src/rustris.rs b/src/rustris.rs new file mode 100644 index 0000000..126e633 --- /dev/null +++ b/src/rustris.rs @@ -0,0 +1,229 @@ +use crossterm::terminal; +use rand::prelude::*; + +use crate::{block::Block, game_data::*, mino::Mino, mino_rotation::MinoRotation}; + +#[derive(Debug)] +pub struct Rustris { + pub game_data: GameData +} + +impl Rustris { + pub fn new(field_size: (usize, usize)) -> Rustris { + let game_data = GameData::new(field_size); + Rustris { game_data } + } + + pub fn get_next_mino(&mut self) -> Mino { + if self.game_data.next_minos.is_empty() { + self.game_data.next_minos = self.game_data.minos.clone(); + } + + let next_mino_index = rand::thread_rng().gen_range(0..self.game_data.next_minos.len()); + let next_mino = self.game_data.next_minos[next_mino_index].clone(); + self.game_data.next_minos.remove(next_mino_index); + next_mino + } + + pub fn check_field(&mut self) -> bool { + match self.try_place_control_mino(Block::Block) { + Ok(_) => true, + Err(()) => false + } + } + + fn rotate_mino(&mut self, rotation: MinoRotation) -> bool { + let original_rotation = self.game_data.mino_rotation.clone(); + self.game_data.mino_rotation = rotation; + + if self.check_field() { + true + } else { + self.game_data.mino_rotation = original_rotation; + false + } + } + + pub fn rotate_mino_to_right(&mut self) -> bool { + self.rotate_mino(MinoRotation::get(self.game_data.mino_rotation.to_count() + 1)) + } + + pub fn rotate_mino_to_left(&mut self) -> bool { + self.rotate_mino(MinoRotation::get(self.game_data.mino_rotation.to_count() - 1)) + } + + pub fn move_mino(&mut self, x: i32, y: i32) -> bool { + self.game_data.mino_pos.0 += x; + self.game_data.mino_pos.1 += y; + + if self.check_field() { + true + } else { + self.game_data.mino_pos.0 -= x; + self.game_data.mino_pos.1 -= y; + false + } + } + + fn rotate_to_right(vec: Vec>) -> Vec> { + let x_max = vec.len(); + let y_max = vec[0].len(); + + let mut buffer: Vec> = vec![vec![]; y_max]; + for y in (0..y_max).rev() { + for x in (0..x_max).rev() { + buffer[x].push(vec[y][x].clone()); + } + } + + buffer + } + + fn try_place_control_mino(&mut self, block_type: Block) -> Result>, ()> { + if let Some(control_mino) = &self.game_data.control_mino { + let mut shape = control_mino.shape.clone(); + let offset = self.game_data.mino_pos; + + let rotate_count = self.game_data.mino_rotation.to_count(); + + for _ in 0..rotate_count { + shape = Rustris::rotate_to_right(shape); + } + + let mut field = self.game_data.field.clone(); + + for x in 0..shape.len() { + for y in 0..shape[x].len() { + + if shape[x][y] { + if (x as i32 + offset.0) < 0 || + (x as i32 + offset.0) > (self.game_data.field_size.1 - 1) as i32 || + (y as i32 + offset.1) < 0 || + (y as i32 + offset.1) > (self.game_data.field_size.0 - 1) as i32 { + return Err(()); + } + match field[(x as i32 + offset.0) as usize][(y as i32 + offset.1) as usize] { + Block::Block => { + return Err(()); + } + + Block::Air => { + field[(x as i32 + offset.0) as usize][(y as i32 + offset.1) as usize] = block_type; + } + + Block::Ghost => { + field[(x as i32 + offset.0) as usize][(y as i32 + offset.1) as usize] = block_type; + } + } + } + } + } + + return Ok(field); + } + + Err(()) + } + + pub fn place_control_mino(&mut self, block_type: Block) -> bool { + match self.try_place_control_mino(block_type) { + Ok(field) => { + self.game_data.field = field; + true + } + Err(()) => { + false + } + } + } + + pub fn show(&mut self) -> String { + let original_field = self.game_data.field.clone(); + let original_mino_pos = self.game_data.mino_pos; + + while self.move_mino(1, 0) { } + self.place_control_mino(Block::Ghost); + self.game_data.mino_pos = original_mino_pos; + self.place_control_mino(Block::Block); + + let console_size = terminal::size().expect("Error: Cannot get console size."); + + let mut print_buffer = String::default(); + + for blocks in &self.game_data.field { + print_buffer += &" ".repeat((console_size.0 / 5).into()); + + for block in blocks { + print_buffer += &block.show(); + } + print_buffer += "\n"; + } + + for _ in 0..(console_size.1 / 6) { + for _ in 0..console_size.0 { + print_buffer += " "; + } + print_buffer += "\n"; + } + + self.game_data.field = original_field; + + print_buffer + } +} + +#[cfg(test)] +mod rustris_tests { + use super::*; + + #[test] + fn rotate_test() { + let rotate_before = + vec![ + vec![0, 0, 1], + vec![1, 1, 0], + vec![1, 0, 0] + ]; + let rotate_after = + vec![ + vec![1, 1, 0], + vec![0, 1, 0], + vec![0, 0, 1] + ]; + assert_eq!(Rustris::rotate_to_right(rotate_before), rotate_after); + } + + #[test] + fn rotate_test2() { + let rotate_before = + vec![ + vec![1, 0, 0], + vec![1, 1, 1], + vec![1, 0, 0] + ]; + let rotate_after = + vec![ + vec![1, 1, 1], + vec![0, 1, 0], + vec![0, 1, 0] + ]; + assert_eq!(Rustris::rotate_to_right(rotate_before), rotate_after); + } + + #[test] + fn rotate_test3() { + let rotate_before = + vec![ + vec![1, 0, 0], + vec![1, 1, 1], + vec![0, 1, 0] + ]; + let rotate_after = + vec![ + vec![0, 1, 1], + vec![1, 1, 0], + vec![0, 1, 0] + ]; + assert_eq!(Rustris::rotate_to_right(rotate_before), rotate_after); + } +}