This commit is contained in:
mii
2021-10-02 00:00:15 +09:00
parent c4d86b81ba
commit 5b92af32c6
8 changed files with 872 additions and 53 deletions

45
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,45 @@
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'rustris'",
"cargo": {
"args": [
"build",
"--bin=rustris",
"--package=rustris"
],
"filter": {
"name": "rustris",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'rustris'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=rustris",
"--package=rustris"
],
"filter": {
"name": "rustris",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

View File

@ -2,7 +2,8 @@
pub enum Block {
Air,
Block,
Ghost
Ghost,
Control
}
impl Block {
@ -11,6 +12,7 @@ impl Block {
Block::Air => String::from(""),
Block::Block => String::from(""),
Block::Ghost => String::from(""),
Block::Control => String::from(""),
}
}
}

View File

@ -13,34 +13,36 @@ pub struct GameData {
pub next_minos: Vec<Mino>,
pub mino_rotation: MinoRotation,
pub field_size: (usize, usize),
pub show_ghost: bool,
pub hold_mino: Option<Mino>
}
impl GameData {
pub fn new(field_size: (usize, usize)) -> GameData {
pub fn new(field_size: (usize, usize), show_ghost: bool) -> 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],
vec![false, false, false ,false],
vec![true, true, true ,true],
vec![false, false, false ,false],
vec![false, false, false ,false],
]
},
Mino {
id: String::from("J"),
shape: vec![
vec![false, true, false],
vec![false, true, false],
vec![true, true, false],
vec![true, false, false],
vec![true, true, true],
vec![false, false, false],
]
},
Mino {
id: String::from("L"),
shape: vec![
vec![false ,true ,false],
vec![false ,true ,false],
vec![false ,true ,true],
vec![false ,false ,true],
vec![true ,true ,true],
vec![false ,false ,false],
]
},
Mino {
@ -87,7 +89,9 @@ impl GameData {
mino_pos: (0, 0),
next_minos,
mino_rotation: MinoRotation::Up,
field_size
field_size,
show_ghost,
hold_mino: None
}
}
}

5
src/game_status.rs Normal file
View File

@ -0,0 +1,5 @@
#[derive(Debug)]
pub enum GameStatus {
Playing,
Gameover
}

View File

@ -3,9 +3,14 @@ mod mino;
mod block;
mod rustris;
mod mino_rotation;
use std::{io::stdout, sync::{Arc, Mutex}, thread, time::Duration};
mod game_status;
mod super_rotation;
use std::{io::stdout, process::exit, 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 crossterm::{cursor, event::{Event, KeyCode, KeyEvent, KeyModifiers, read}, execute, style::{Color, Print, ResetColor, SetBackgroundColor, SetForegroundColor}, terminal::{self, Clear, ClearType, enable_raw_mode}};
use game_data::GameData;
use game_status::GameStatus;
use mino_rotation::MinoRotation;
use crate::{block::Block, rustris::Rustris};
@ -31,7 +36,98 @@ fn main() {
println!();
}
let rustris = Arc::new(Mutex::new(Rustris::new((10, 21))));
let rustris = Arc::new(Mutex::new(
Rustris::new(
GameData::new((10, 21), true)
)
));
{
let mut select = 0;
let mut rustris = rustris.lock().unwrap();
loop {
let mut stdout = stdout.lock().unwrap();
execute!(
stdout,
cursor::MoveTo(0, 0),
cursor::Hide,
Clear(ClearType::All)
).unwrap();
if select == 0 {
print!("> ");
}
println!("Play");
if select == 1 {
print!("> ");
}
println!("Ghost {}",
if rustris.game_data.show_ghost {
"ON"
} else {
"OFF"
}
);
if select == 2 {
print!("> ");
}
println!("Quit");
match read().unwrap() {
Event::Key(KeyEvent {
code: KeyCode::Up,
modifiers: KeyModifiers::NONE
}) => {
if select > 0 {
select -= 1;
}
}
Event::Key(KeyEvent {
code: KeyCode::Down,
modifiers: KeyModifiers::NONE
}) => {
if select < 2 {
select += 1;
}
}
Event::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE
}) => {
match select {
0 => {
break;
}
1 => {
rustris.game_data.show_ghost = !rustris.game_data.show_ghost;
}
2 => {
std::process::exit(0);
}
_ => ()
}
}
_ => ()
}
}
}
{
let mut stdout = stdout.lock().unwrap();
execute!(
stdout,
cursor::MoveTo(0, 0),
cursor::Hide,
Clear(ClearType::All)
).unwrap();
}
{
let mut rustris = rustris.lock().unwrap();
@ -40,15 +136,36 @@ fn main() {
let rustris_rc = Arc::clone(&rustris);
let stdout_rc = Arc::clone(&stdout);
thread::spawn(move || {
let exit_flag = Arc::new(Mutex::new(false));
let exit_flag_rc = Arc::clone(&exit_flag);
let control_count = Arc::new(Mutex::new(0));
let control_count_rc = Arc::clone(&control_count);
let frame_thread = thread::spawn(move || {
let mut ground_flag = false;
let mut before_control_count = 0;
loop {
thread::sleep(Duration::from_secs(1));
if *exit_flag_rc.lock().unwrap() {
break;
}
thread::sleep(Duration::from_secs_f32(0.5));
{
let mut rustris_rc = rustris_rc.lock().unwrap();
if !rustris_rc.move_mino(1, 0) {
if ground_flag {
let control_count = *control_count_rc.lock().unwrap();
if before_control_count < control_count && control_count < 15 {
before_control_count = control_count.clone();
ground_flag = false;
} else {
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);
rustris_rc.next_mino();
ground_flag = false;
*control_count_rc.lock().unwrap() = 0;
before_control_count = 0;
}
} else {
ground_flag = true;
}
}
}
{
@ -74,6 +191,7 @@ fn main() {
}) => {
let mut rustris = rustris.lock().unwrap();
rustris.move_mino(0, 1);
*control_count.lock().unwrap() += 1;
}
Event::Key(KeyEvent {
@ -82,6 +200,7 @@ fn main() {
}) => {
let mut rustris = rustris.lock().unwrap();
rustris.move_mino(0, -1);
*control_count.lock().unwrap() += 1;
}
Event::Key(KeyEvent {
@ -90,6 +209,7 @@ fn main() {
}) => {
let mut rustris = rustris.lock().unwrap();
rustris.move_mino(1, 0);
*control_count.lock().unwrap() += 1;
}
Event::Key(KeyEvent {
@ -100,16 +220,8 @@ fn main() {
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();
rustris.next_mino();
*control_count.lock().unwrap() += 1;
}
Event::Key(KeyEvent {
@ -117,9 +229,33 @@ fn main() {
modifiers: KeyModifiers::NONE
}) => {
let mut rustris = rustris.lock().unwrap();
rustris.rotate_mino_to_left();
if let Some(holding) = rustris.game_data.hold_mino.clone() {
let control_tmp = rustris.game_data.control_mino.clone();
rustris.game_data.control_mino = Some(holding);
rustris.game_data.hold_mino = control_tmp;
rustris.game_data.mino_pos = (0, 3);
rustris.game_data.mino_rotation = MinoRotation::Up;
} else {
rustris.game_data.hold_mino = rustris.game_data.control_mino.clone();
rustris.next_mino();
}
}
Event::Key(KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::NONE
}) => {
let mut rustris = rustris.lock().unwrap();
rustris.rotate_mino_to_right();
}
Event::Key(KeyEvent {
code: KeyCode::Char('x'),
modifiers: KeyModifiers::NONE
}) => {
let mut rustris = rustris.lock().unwrap();
rustris.rotate_mino_to_left();
}
Event::Key(KeyEvent {
code: KeyCode::Char('q'),
@ -158,6 +294,39 @@ fn main() {
}
}
}
{
let mut rustris = rustris.lock().unwrap();
if let GameStatus::Gameover = rustris.game_status {
let mut stdout = stdout.lock().unwrap();
execute!(
stdout,
cursor::MoveTo(0, 0),
cursor::Show,
Clear(ClearType::All),
SetBackgroundColor(Color::Red),
).unwrap();
let console_size = terminal::size().expect("Error: Cannot get console size.");
execute!(
stdout,
cursor::MoveTo(console_size.0 / 2, console_size.1 / 2),
Print("Game Over")
).unwrap();
thread::sleep(Duration::from_secs(3));
execute!(
stdout,
cursor::MoveTo(0, 0),
cursor::Show,
Clear(ClearType::All),
ResetColor
).unwrap();
break;
}
}
}
{
@ -169,6 +338,10 @@ fn main() {
Clear(ClearType::All)
).unwrap();
}
*exit_flag.lock().unwrap() = true;
frame_thread.join();
main();
}
#[cfg(test)]

View File

@ -1,4 +1,4 @@
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MinoRotation {
Up,
Right,

View File

@ -1,17 +1,17 @@
use crossterm::terminal;
use rand::prelude::*;
use crate::{block::Block, game_data::*, mino::Mino, mino_rotation::MinoRotation};
use crate::{block::Block, game_data::*, game_status::GameStatus, mino::Mino, mino_rotation::MinoRotation, super_rotation::SuperRotation};
#[derive(Debug)]
pub struct Rustris {
pub game_data: GameData
pub game_data: GameData,
pub game_status: GameStatus
}
impl Rustris {
pub fn new(field_size: (usize, usize)) -> Rustris {
let game_data = GameData::new(field_size);
Rustris { game_data }
pub fn new(game_data: GameData) -> Rustris {
Rustris { game_data, game_status: GameStatus::Playing }
}
pub fn get_next_mino(&mut self) -> Mino {
@ -33,16 +33,31 @@ impl Rustris {
}
fn rotate_mino(&mut self, rotation: MinoRotation) -> bool {
let original_rotation = self.game_data.mino_rotation.clone();
self.game_data.mino_rotation = rotation;
if let Some(mino) = self.game_data.control_mino.clone() {
let mut super_rotation = SuperRotation::new(
self.game_data.field.clone(),
mino.clone(),
self.game_data.mino_pos.clone(),
self.game_data.field_size.clone(),
self.game_data.mino_rotation.clone(),
rotation.clone()
);
if super_rotation.rotate() {
self.game_data.mino_pos = super_rotation.mino_position.clone();
self.game_data.mino_rotation = super_rotation.now_rotation.clone();
if self.check_field() {
true
return true;
} else {
self.game_data.mino_rotation = original_rotation;
false
self.game_data.mino_pos = super_rotation.origin_position;
self.game_data.mino_rotation = super_rotation.origin_rotation;
return false;
}
}
}
false
}
pub fn rotate_mino_to_right(&mut self) -> bool {
self.rotate_mino(MinoRotation::get(self.game_data.mino_rotation.to_count() + 1))
@ -65,6 +80,18 @@ impl Rustris {
}
}
pub fn next_mino(&mut self) -> bool {
self.game_data.control_mino = Some(self.get_next_mino());
self.game_data.mino_pos = (0, 3);
self.game_data.mino_rotation = MinoRotation::Up;
if self.check_field() {
true
} else {
self.game_status = GameStatus::Gameover;
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();
@ -114,6 +141,8 @@ impl Rustris {
Block::Ghost => {
field[(x as i32 + offset.0) as usize][(y as i32 + offset.1) as usize] = block_type;
}
_ => ()
}
}
}
@ -141,10 +170,13 @@ impl Rustris {
let original_field = self.game_data.field.clone();
let original_mino_pos = self.game_data.mino_pos;
if self.game_data.show_ghost {
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);
self.place_control_mino(Block::Control);
let console_size = terminal::size().expect("Error: Cannot get console size.");
@ -159,10 +191,23 @@ impl Rustris {
print_buffer += "\n";
}
for _ in 0..(console_size.1 / 6) {
for _ in 0..console_size.0 {
print_buffer += " ";
print_buffer += "     Hold\n";
if let Some(hold) = self.game_data.hold_mino.clone() {
for shape_row in hold.shape {
print_buffer += "     ";
for x in shape_row {
print_buffer += if x {
""
} else {
" "
}
}
print_buffer += "\n";
}
}
print_buffer += "     Z ホールド, X 左回転, C 右回転\n";
for _ in 0..(if (console_size.1 - print_buffer.lines().count() as u16) > 0 { console_size.1 - print_buffer.lines().count() as u16 - 1 } else { console_size.1 - print_buffer.lines().count() as u16 }) {
print_buffer += "\n";
}

545
src/super_rotation.rs Normal file
View File

@ -0,0 +1,545 @@
use crate::{block::Block, mino::{self, Mino}, mino_rotation::MinoRotation};
#[derive(PartialEq, Debug, Clone, Copy)]
enum RotateDirection {
Right,
Left,
}
pub struct SuperRotation {
pub field: Vec<Vec<Block>>,
pub target: Mino,
pub mino_position: (i32, i32),
pub origin_position: (i32, i32),
pub field_size: (usize, usize),
pub now_rotation: MinoRotation,
pub origin_rotation: MinoRotation,
pub next_rotation: MinoRotation,
rotate_direction: RotateDirection,
}
impl SuperRotation {
pub fn new(field: Vec<Vec<Block>>, target: Mino, mino_position: (i32, i32), field_size: (usize, usize), now_rotation: MinoRotation, next_rotation: MinoRotation) -> SuperRotation {
SuperRotation {
field,
target,
mino_position,
origin_position: mino_position.clone(),
field_size,
now_rotation,
origin_rotation: now_rotation.clone(),
next_rotation,
rotate_direction: SuperRotation::get_rotate_direction(now_rotation.clone(), now_rotation.clone())
}
}
pub fn rotate(&mut self) -> bool {
if self.target.id == "I" {
if self.step0() {
return true;
}
if self.i_step1() {
return true;
}
if self.i_step2() {
return true;
}
if self.i_step3() {
return true;
}
if self.i_step4() {
return true;
}
} else {
if self.step0() {
return true;
}
if self.step1() {
return true;
}
if self.step2() {
return true;
}
if self.step3() {
return true;
}
if self.step4() {
return true;
}
}
false
}
fn i_step1(&mut self) -> bool {
match self.origin_rotation {
MinoRotation::Up => {
match self.now_rotation {
MinoRotation::Left => {
self.move_to_left();
}
MinoRotation::Right => {
self.move_to_left();
self.move_to_left();
}
_ => ()
}
}
MinoRotation::Right => {
match self.now_rotation {
MinoRotation::Up => {
self.move_to_right();
self.move_to_right();
}
MinoRotation::Down => {
self.move_to_left();
}
_ => ()
}
}
MinoRotation::Down => {
match self.now_rotation {
MinoRotation::Left => {
self.move_to_right();
self.move_to_right();
}
MinoRotation::Right => {
self.move_to_right();
}
_ => ()
}
}
MinoRotation::Left => {
match self.now_rotation {
MinoRotation::Up => {
self.move_to_left();
self.move_to_left();
}
MinoRotation::Down => {
self.move_to_right();
}
_ => ()
}
}
}
if self.check_duplicate() {
return true;
}
false
}
fn i_step2(&mut self) -> bool {
self.step0();
match self.origin_rotation {
MinoRotation::Up => {
match self.now_rotation {
MinoRotation::Left => {
self.move_to_right();
self.move_to_right();
}
MinoRotation::Right => {
self.move_to_right();
}
_ => ()
}
}
MinoRotation::Right => {
match self.now_rotation {
MinoRotation::Up => {
self.move_to_left();
}
MinoRotation::Down => {
self.move_to_right();
self.move_to_right();
}
_ => ()
}
}
MinoRotation::Down => {
match self.now_rotation {
MinoRotation::Left => {
self.move_to_left();
}
MinoRotation::Right => {
self.move_to_left();
self.move_to_left();
}
_ => ()
}
}
MinoRotation::Left => {
match self.now_rotation {
MinoRotation::Up => {
self.move_to_right();
}
MinoRotation::Down => {
self.move_to_left();
self.move_to_left();
}
_ => ()
}
}
}
if self.check_duplicate() {
return true;
}
false
}
fn i_step3(&mut self) -> bool {
self.step0();
match self.origin_rotation {
MinoRotation::Up => {
match self.now_rotation {
MinoRotation::Left => {
self.move_to_right();
self.move_to_up();
self.move_to_up();
}
MinoRotation::Right => {
self.move_to_down();
self.move_to_left();
self.move_to_left();
}
_ => ()
}
}
MinoRotation::Right => {
match self.now_rotation {
MinoRotation::Up => {
self.move_to_right();
self.move_to_right();
self.move_to_up();
}
MinoRotation::Down => {
self.move_to_up();
self.move_to_up();
self.move_to_left();
}
_ => ()
}
}
MinoRotation::Down => {
match self.now_rotation {
MinoRotation::Left => {
self.move_to_up();
self.move_to_right();
self.move_to_right();
}
MinoRotation::Right => {
self.move_to_right();
self.move_to_down();
self.move_to_down();
}
_ => ()
}
}
MinoRotation::Left => {
match self.now_rotation {
MinoRotation::Up => {
self.move_to_down();
self.move_to_down();
self.move_to_right();
}
MinoRotation::Down => {
self.move_to_down();
self.move_to_left();
self.move_to_left();
}
_ => ()
}
}
}
if self.check_duplicate() {
return true;
}
false
}
fn i_step4(&mut self) -> bool {
self.step0();
match self.origin_rotation {
MinoRotation::Up => {
match self.now_rotation {
MinoRotation::Left => {
self.move_to_down();
self.move_to_left();
self.move_to_left();
}
MinoRotation::Right => {
self.move_to_right();
self.move_to_up();
self.move_to_up();
}
_ => ()
}
}
MinoRotation::Right => {
match self.now_rotation {
MinoRotation::Up => {
self.move_to_down();
self.move_to_down();
self.move_to_left();
}
MinoRotation::Down => {
self.move_to_right();
self.move_to_right();
self.move_to_down();
}
_ => ()
}
}
MinoRotation::Down => {
match self.now_rotation {
MinoRotation::Left => {
self.move_to_left();
self.move_to_down();
self.move_to_down();
}
MinoRotation::Right => {
self.move_to_up();
self.move_to_left();
self.move_to_left();
}
_ => ()
}
}
MinoRotation::Left => {
match self.now_rotation {
MinoRotation::Up => {
self.move_to_left();
self.move_to_left();
self.move_to_up();
}
MinoRotation::Down => {
self.move_to_right();
self.move_to_up();
self.move_to_up();
}
_ => ()
}
}
}
if self.check_duplicate() {
return true;
}
false
}
fn step0(&mut self) -> bool {
self.now_rotation = self.next_rotation.clone();
self.mino_position = self.origin_position.clone();
if self.check_duplicate() {
return true;
}
false
}
fn step1(&mut self) -> bool {
let rotate_direction = self.rotate_direction;
match self.now_rotation {
MinoRotation::Right => {
self.move_to_left();
}
MinoRotation::Left => {
self.move_to_right();
}
_ => {
if self.origin_rotation == MinoRotation::Left {
self.move_to_left();
} else {
self.move_to_right();
}
}
}
if self.check_duplicate() {
return true;
}
false
}
fn step2(&mut self) -> bool {
if self.now_rotation == MinoRotation::Right || self.now_rotation == MinoRotation::Left {
self.move_to_up();
} else {
self.move_to_down();
}
if self.check_duplicate() {
return true;
}
false
}
fn step3(&mut self) -> bool {
self.now_rotation = self.next_rotation.clone();
self.mino_position = self.origin_position.clone();
if self.now_rotation == MinoRotation::Right || self.now_rotation == MinoRotation::Left {
self.move_to_down();
self.move_to_down();
} else {
self.move_to_up();
self.move_to_up();
}
if self.check_duplicate() {
return true;
}
false
}
fn step4(&mut self) -> bool {
let rotate_direction = self.rotate_direction;
match self.now_rotation {
MinoRotation::Right => {
self.move_to_left();
}
MinoRotation::Left => {
self.move_to_right();
}
_ => {
if rotate_direction == RotateDirection::Right {
self.move_to_left();
} else {
self.move_to_right();
}
}
}
if self.check_duplicate() {
return true;
}
false
}
fn move_to_right(&mut self) {
self.mino_position.1 += 1;
}
fn move_to_left(&mut self) {
self.mino_position.1 -= 1;
}
fn move_to_up(&mut self) {
self.mino_position.0 -= 1;
}
fn move_to_down(&mut self) {
self.mino_position.0 += 1;
}
fn check_duplicate(&mut self) -> bool {
let shape = self.target.shape.clone();
let pos = self.mino_position;
let field = self.field.clone();
let field_size = self.field_size;
for x in 0..shape.len() {
for y in 0..shape[x].len() {
if shape[x][y] {
if (x as i32 + pos.0) < 0 ||
(x as i32 + pos.0) > (field_size.1 - 1) as i32 ||
(y as i32 + pos.1) < 0 ||
(y as i32 + pos.1) > (field_size.0 - 1) as i32 {
return false;
}
match field[(x as i32 + pos.0) as usize][(y as i32 + pos.1) as usize] {
Block::Block => {
return false;
}
_ => ()
}
}
}
}
true
}
fn get_rotate_direction(before: MinoRotation, after: MinoRotation) -> RotateDirection {
match before {
MinoRotation::Up => {
if after == MinoRotation::Right {
RotateDirection::Right
} else {
RotateDirection::Left
}
}
MinoRotation::Right => {
if after == MinoRotation::Down {
RotateDirection::Right
} else {
RotateDirection::Left
}
}
MinoRotation::Down => {
if after == MinoRotation::Left {
RotateDirection::Right
} else {
RotateDirection::Left
}
}
MinoRotation::Left => {
if after == MinoRotation::Up {
RotateDirection::Right
} else {
RotateDirection::Left
}
}
}
}
}
#[cfg(test)]
mod super_rotation_test {
use super::*;
#[test]
fn get_rotate_direction_test1() {
assert_eq!(SuperRotation::get_rotate_direction(MinoRotation::Up, MinoRotation::Right), RotateDirection::Right);
}
#[test]
fn get_rotate_direction_test2() {
assert_eq!(SuperRotation::get_rotate_direction(MinoRotation::Down, MinoRotation::Right), RotateDirection::Left);
}
#[test]
fn get_rotate_direction_test3() {
assert_eq!(SuperRotation::get_rotate_direction(MinoRotation::Left, MinoRotation::Up), RotateDirection::Right);
}
#[test]
fn get_rotate_direction_test4() {
assert_eq!(SuperRotation::get_rotate_direction(MinoRotation::Right, MinoRotation::Up), RotateDirection::Left);
}
}