rustris_base

This commit is contained in:
mii
2021-10-01 15:36:41 +09:00
commit c4d86b81ba
9 changed files with 873 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

273
Cargo.lock generated Normal file
View File

@ -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"

10
Cargo.toml Normal file
View File

@ -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"

16
src/block.rs Normal file
View File

@ -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(""),
}
}
}

93
src/game_data.rs Normal file
View File

@ -0,0 +1,93 @@
use crate::{
mino::*,
block::*,
mino_rotation::*
};
#[derive(Debug)]
pub struct GameData {
pub minos: Vec<Mino>,
pub field: Vec<Vec<Block>>,
pub control_mino: Option<Mino>,
pub mino_pos: (i32, i32),
pub next_minos: Vec<Mino>,
pub mino_rotation: MinoRotation,
pub field_size: (usize, usize),
}
impl GameData {
pub fn new(field_size: (usize, usize)) -> GameData {
let minos: Vec<Mino> = 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<Block>> = 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
}
}
}

177
src/main.rs Normal file
View File

@ -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 {
}

5
src/mino.rs Normal file
View File

@ -0,0 +1,5 @@
#[derive(Debug, Clone)]
pub struct Mino {
pub id: String,
pub shape: Vec<Vec<bool>>
}

69
src/mino_rotation.rs Normal file
View File

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

229
src/rustris.rs Normal file
View File

@ -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<T: Clone>(vec: Vec<Vec<T>>) -> Vec<Vec<T>> {
let x_max = vec.len();
let y_max = vec[0].len();
let mut buffer: Vec<Vec<T>> = 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<Vec<Vec<Block>>, ()> {
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);
}
}