mirror of
https://github.com/mii443/rustris.git
synced 2025-08-22 16:25:42 +00:00
add SRS
This commit is contained in:
45
.vscode/launch.json
vendored
Normal file
45
.vscode/launch.json
vendored
Normal 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}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -2,7 +2,8 @@
|
|||||||
pub enum Block {
|
pub enum Block {
|
||||||
Air,
|
Air,
|
||||||
Block,
|
Block,
|
||||||
Ghost
|
Ghost,
|
||||||
|
Control
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
impl Block {
|
||||||
@ -11,6 +12,7 @@ impl Block {
|
|||||||
Block::Air => String::from("・"),
|
Block::Air => String::from("・"),
|
||||||
Block::Block => String::from("■"),
|
Block::Block => String::from("■"),
|
||||||
Block::Ghost => String::from("□"),
|
Block::Ghost => String::from("□"),
|
||||||
|
Block::Control => String::from("■"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,34 +13,36 @@ pub struct GameData {
|
|||||||
pub next_minos: Vec<Mino>,
|
pub next_minos: Vec<Mino>,
|
||||||
pub mino_rotation: MinoRotation,
|
pub mino_rotation: MinoRotation,
|
||||||
pub field_size: (usize, usize),
|
pub field_size: (usize, usize),
|
||||||
|
pub show_ghost: bool,
|
||||||
|
pub hold_mino: Option<Mino>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameData {
|
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![
|
let minos: Vec<Mino> = vec![
|
||||||
Mino {
|
Mino {
|
||||||
id: String::from("I"),
|
id: String::from("I"),
|
||||||
shape: vec![
|
shape: vec![
|
||||||
vec![false, false, true ,false],
|
vec![false, false, false ,false],
|
||||||
vec![false, false, true ,false],
|
vec![true, true, true ,true],
|
||||||
vec![false, false, true ,false],
|
vec![false, false, false ,false],
|
||||||
vec![false, false, true ,false],
|
vec![false, false, false ,false],
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
Mino {
|
Mino {
|
||||||
id: String::from("J"),
|
id: String::from("J"),
|
||||||
shape: vec![
|
shape: vec![
|
||||||
vec![false, true, false],
|
vec![true, false, false],
|
||||||
vec![false, true, false],
|
vec![true, true, true],
|
||||||
vec![true, true, false],
|
vec![false, false, false],
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
Mino {
|
Mino {
|
||||||
id: String::from("L"),
|
id: String::from("L"),
|
||||||
shape: vec![
|
shape: vec![
|
||||||
vec![false ,true ,false],
|
vec![false ,false ,true],
|
||||||
vec![false ,true ,false],
|
vec![true ,true ,true],
|
||||||
vec![false ,true ,true],
|
vec![false ,false ,false],
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
Mino {
|
Mino {
|
||||||
@ -84,10 +86,12 @@ impl GameData {
|
|||||||
minos,
|
minos,
|
||||||
field,
|
field,
|
||||||
control_mino: None,
|
control_mino: None,
|
||||||
mino_pos: (0, 0) ,
|
mino_pos: (0, 0),
|
||||||
next_minos,
|
next_minos,
|
||||||
mino_rotation: MinoRotation::Up,
|
mino_rotation: MinoRotation::Up,
|
||||||
field_size
|
field_size,
|
||||||
|
show_ghost,
|
||||||
|
hold_mino: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
src/game_status.rs
Normal file
5
src/game_status.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GameStatus {
|
||||||
|
Playing,
|
||||||
|
Gameover
|
||||||
|
}
|
211
src/main.rs
211
src/main.rs
@ -3,9 +3,14 @@ mod mino;
|
|||||||
mod block;
|
mod block;
|
||||||
mod rustris;
|
mod rustris;
|
||||||
mod mino_rotation;
|
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};
|
use crate::{block::Block, rustris::Rustris};
|
||||||
|
|
||||||
@ -31,7 +36,98 @@ fn main() {
|
|||||||
println!();
|
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();
|
let mut rustris = rustris.lock().unwrap();
|
||||||
@ -40,15 +136,36 @@ fn main() {
|
|||||||
|
|
||||||
let rustris_rc = Arc::clone(&rustris);
|
let rustris_rc = Arc::clone(&rustris);
|
||||||
let stdout_rc = Arc::clone(&stdout);
|
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 {
|
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();
|
let mut rustris_rc = rustris_rc.lock().unwrap();
|
||||||
if !rustris_rc.move_mino(1, 0) {
|
if !rustris_rc.move_mino(1, 0) {
|
||||||
rustris_rc.place_control_mino(Block::Block);
|
if ground_flag {
|
||||||
rustris_rc.game_data.control_mino = Some(rustris_rc.get_next_mino());
|
let control_count = *control_count_rc.lock().unwrap();
|
||||||
rustris_rc.game_data.mino_pos = (0, 5);
|
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.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();
|
let mut rustris = rustris.lock().unwrap();
|
||||||
rustris.move_mino(0, 1);
|
rustris.move_mino(0, 1);
|
||||||
|
*control_count.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
@ -82,6 +200,7 @@ fn main() {
|
|||||||
}) => {
|
}) => {
|
||||||
let mut rustris = rustris.lock().unwrap();
|
let mut rustris = rustris.lock().unwrap();
|
||||||
rustris.move_mino(0, -1);
|
rustris.move_mino(0, -1);
|
||||||
|
*control_count.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
@ -90,6 +209,7 @@ fn main() {
|
|||||||
}) => {
|
}) => {
|
||||||
let mut rustris = rustris.lock().unwrap();
|
let mut rustris = rustris.lock().unwrap();
|
||||||
rustris.move_mino(1, 0);
|
rustris.move_mino(1, 0);
|
||||||
|
*control_count.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
@ -100,16 +220,8 @@ fn main() {
|
|||||||
while rustris.move_mino(1, 0) { }
|
while rustris.move_mino(1, 0) { }
|
||||||
|
|
||||||
rustris.place_control_mino(Block::Block);
|
rustris.place_control_mino(Block::Block);
|
||||||
rustris.game_data.control_mino = Some(rustris.get_next_mino());
|
rustris.next_mino();
|
||||||
rustris.game_data.mino_pos = (0, 5);
|
*control_count.lock().unwrap() += 1;
|
||||||
}
|
|
||||||
|
|
||||||
Event::Key(KeyEvent {
|
|
||||||
code: KeyCode::Char('x'),
|
|
||||||
modifiers: KeyModifiers::NONE
|
|
||||||
}) => {
|
|
||||||
let mut rustris = rustris.lock().unwrap();
|
|
||||||
rustris.rotate_mino_to_right();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
@ -117,9 +229,33 @@ fn main() {
|
|||||||
modifiers: KeyModifiers::NONE
|
modifiers: KeyModifiers::NONE
|
||||||
}) => {
|
}) => {
|
||||||
let mut rustris = rustris.lock().unwrap();
|
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 {
|
Event::Key(KeyEvent {
|
||||||
code: KeyCode::Char('q'),
|
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)
|
Clear(ClearType::All)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*exit_flag.lock().unwrap() = true;
|
||||||
|
frame_thread.join();
|
||||||
|
main();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum MinoRotation {
|
pub enum MinoRotation {
|
||||||
Up,
|
Up,
|
||||||
Right,
|
Right,
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use crossterm::terminal;
|
use crossterm::terminal;
|
||||||
use rand::prelude::*;
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Rustris {
|
pub struct Rustris {
|
||||||
pub game_data: GameData
|
pub game_data: GameData,
|
||||||
|
pub game_status: GameStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rustris {
|
impl Rustris {
|
||||||
pub fn new(field_size: (usize, usize)) -> Rustris {
|
pub fn new(game_data: GameData) -> Rustris {
|
||||||
let game_data = GameData::new(field_size);
|
Rustris { game_data, game_status: GameStatus::Playing }
|
||||||
Rustris { game_data }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_next_mino(&mut self) -> Mino {
|
pub fn get_next_mino(&mut self) -> Mino {
|
||||||
@ -33,15 +33,30 @@ impl Rustris {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_mino(&mut self, rotation: MinoRotation) -> bool {
|
fn rotate_mino(&mut self, rotation: MinoRotation) -> bool {
|
||||||
let original_rotation = self.game_data.mino_rotation.clone();
|
if let Some(mino) = self.game_data.control_mino.clone() {
|
||||||
self.game_data.mino_rotation = rotation;
|
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 self.check_field() {
|
if super_rotation.rotate() {
|
||||||
true
|
self.game_data.mino_pos = super_rotation.mino_position.clone();
|
||||||
} else {
|
self.game_data.mino_rotation = super_rotation.now_rotation.clone();
|
||||||
self.game_data.mino_rotation = original_rotation;
|
if self.check_field() {
|
||||||
false
|
return true;
|
||||||
|
} else {
|
||||||
|
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 {
|
pub fn rotate_mino_to_right(&mut self) -> bool {
|
||||||
@ -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>> {
|
fn rotate_to_right<T: Clone>(vec: Vec<Vec<T>>) -> Vec<Vec<T>> {
|
||||||
let x_max = vec.len();
|
let x_max = vec.len();
|
||||||
let y_max = vec[0].len();
|
let y_max = vec[0].len();
|
||||||
@ -114,6 +141,8 @@ impl Rustris {
|
|||||||
Block::Ghost => {
|
Block::Ghost => {
|
||||||
field[(x as i32 + offset.0) as usize][(y as i32 + offset.1) as usize] = block_type;
|
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_field = self.game_data.field.clone();
|
||||||
let original_mino_pos = self.game_data.mino_pos;
|
let original_mino_pos = self.game_data.mino_pos;
|
||||||
|
|
||||||
while self.move_mino(1, 0) { }
|
if self.game_data.show_ghost {
|
||||||
self.place_control_mino(Block::Ghost);
|
while self.move_mino(1, 0) { }
|
||||||
|
self.place_control_mino(Block::Ghost);
|
||||||
|
}
|
||||||
|
|
||||||
self.game_data.mino_pos = original_mino_pos;
|
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.");
|
let console_size = terminal::size().expect("Error: Cannot get console size.");
|
||||||
|
|
||||||
@ -159,10 +191,23 @@ impl Rustris {
|
|||||||
print_buffer += "\n";
|
print_buffer += "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..(console_size.1 / 6) {
|
print_buffer += " Hold\n";
|
||||||
for _ in 0..console_size.0 {
|
if let Some(hold) = self.game_data.hold_mino.clone() {
|
||||||
print_buffer += " ";
|
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";
|
print_buffer += "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
545
src/super_rotation.rs
Normal file
545
src/super_rotation.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user