(hopefully) fixed documentation

fixed the docs, added relevant stuff to gitignore, and accidently
rustfmt'd main.rs so i formatted the rest lol
This commit is contained in:
Ezra Fein 2020-04-20 03:59:17 -04:00
parent b7d5621dd6
commit d03795f247
5 changed files with 161 additions and 117 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target
rusty-tags.vi
Cargo.lock

View File

@ -12,4 +12,7 @@ readme = "README.md"
midir = "0.5.0"
array2d = "0.2.1"
multiinput = "0.0.15"
rand = "0.6.0"
rand = "0.6.0"
[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"

View File

@ -15,12 +15,15 @@ pub enum ControlEvent {
MoveDown,
DropBlock,
SpeedChange(u8),
ExitGame
ExitGame,
}
/// Represents a pad on the Launchpad
/// Provides methods to convert to and from a note byte
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Pad {pub x: u8, pub y: u8}
pub struct Pad {
pub x: u8,
pub y: u8,
}
// Pad impl
impl Pad {
@ -63,10 +66,10 @@ impl Pad {
/// ```
pub fn from_note(note: u8) -> Pad {
let ones = note % 10;
let tens = (note.saturating_sub(ones))/10;
let tens = (note.saturating_sub(ones)) / 10;
let x = ones.saturating_sub(1);
let y = tens.saturating_sub(1);
Pad {x, y}
Pad { x, y }
}
}
// Core defs
@ -84,24 +87,24 @@ impl Launchpad {
}
}
let out_port = out_port.expect("Couldn't find launchpad!");
let conn_out = midi_out.connect(out_port, "").expect("Failed to open connection");
let conn_out = midi_out
.connect(out_port, "")
.expect("Failed to open connection");
// let (c_tx, c_rx) = mpsc::channel();
let (events_tx, events_rx) = mpsc::channel();
let mut manager = RawInputManager::new().unwrap();
manager.register_devices(DeviceType::Keyboards);
thread::spawn(move || {
loop {
if let Some(event) = manager.get_event() {
if let Some(msg) = input_map(event) {
events_tx.send(msg).ok();
}
thread::spawn(move || loop {
if let Some(event) = manager.get_event() {
if let Some(msg) = input_map(event) {
events_tx.send(msg).ok();
}
}
});
Launchpad {
conn_out,
events_rx
events_rx,
}
}
/// Closes the underlying midi connection to the launchpad
@ -134,7 +137,7 @@ impl Launchpad {
let mut msg = Vec::new();
for y in 0..8 {
for x in 0..8 {
let pad = Pad {x, y};
let pad = Pad { x, y };
msg.push(pad.note());
msg.push(matrix[(y as usize, x as usize)]);
}
@ -161,7 +164,7 @@ pub fn input_map(event: RawEvent) -> Option<ControlEvent> {
RawEvent::KeyboardEvent(_, KeyId::Left, State::Pressed) => Some(ControlEvent::MoveLeft),
RawEvent::KeyboardEvent(_, KeyId::Right, State::Pressed) => Some(ControlEvent::MoveRight),
RawEvent::KeyboardEvent(_, KeyId::Up, State::Pressed) => Some(ControlEvent::MoveUp),
RawEvent::KeyboardEvent(_, KeyId::Down, State::Pressed) => Some(ControlEvent::MoveDown) ,
RawEvent::KeyboardEvent(_, KeyId::Down, State::Pressed) => Some(ControlEvent::MoveDown),
RawEvent::KeyboardEvent(_, KeyId::Space, State::Pressed) => Some(ControlEvent::DropBlock),
// RawEvent::KeyboardEvent(_, KeyId::One, State::Pressed) => Some(ControlEvent::SpeedChange(0)),
// RawEvent::KeyboardEvent(_, KeyId::Two, State::Pressed) => Some(ControlEvent::SpeedChange(1)),
@ -172,7 +175,9 @@ pub fn input_map(event: RawEvent) -> Option<ControlEvent> {
// RawEvent::KeyboardEvent(_, KeyId::Seven, State::Pressed) => Some(ControlEvent::SpeedChange(6)),
// RawEvent::KeyboardEvent(_, KeyId::Eight, State::Pressed) => Some(ControlEvent::SpeedChange(7)),
// RawEvent::KeyboardEvent(_, KeyId::Nine, State::Pressed) => Some(ControlEvent::SpeedChange(8)),
RawEvent::KeyboardEvent(_, KeyId::Backspace, State::Pressed) => Some(ControlEvent::ExitGame),
_ => None
RawEvent::KeyboardEvent(_, KeyId::Backspace, State::Pressed) => {
Some(ControlEvent::ExitGame)
}
_ => None,
}
}
}

View File

@ -1,10 +1,10 @@
use array2d::Array2D;
use lp_tetris::{ControlEvent, Launchpad};
use std::thread::sleep;
use std::time::Duration;
use array2d::Array2D;
use lp_tetris::{Launchpad, ControlEvent};
mod tetris;
use rand::Rng;
use tetris::CollisionResult;
use rand::{Rng};
#[allow(unused)]
fn run_color(lp: &mut Launchpad, c: u8) {
@ -35,13 +35,16 @@ fn main() {
let mut lp = Launchpad::new();
println!("Connection open!!");
lp.clear();
'gameloop: loop{
'gameloop: loop {
sleep(Duration::from_millis(4));
tick += 1;
lp.send_matrix(board.shadow(&current_piece, pos_x, pos_y));
let tickrate = match 255u8.checked_sub(speed) {
Some(tr) => tr,
None => panic!("255u8 - {} is, out of bounds? this shouldn't happen, speed is a u8", speed),
None => panic!(
"255u8 - {} is, out of bounds? this shouldn't happen, speed is a u8",
speed
),
};
if tick % tickrate as u32 == 0 || drop_down {
if pos_y == 0 {
@ -57,7 +60,7 @@ fn main() {
// },
CollisionResult::Unobstructed => {
pos_y = pos_y.saturating_sub(1);
},
}
_ => {
board.place(&current_piece, pos_x, pos_y);
current_piece = tetris::Piece::new(rng.gen());
@ -78,14 +81,14 @@ fn main() {
match board.collides(&current_piece, pos_x.saturating_sub(1), pos_y) {
CollisionResult::Unobstructed => pos_x = pos_x.saturating_sub(1),
// CollisionResult::AboveRoof => pos_x = pos_x.saturating_sub(1),
_ => ()
_ => (),
}
}
ControlEvent::MoveRight => {
match board.collides(&current_piece, pos_x.saturating_add(1), pos_y) {
CollisionResult::Unobstructed => pos_x = pos_x.saturating_add(1),
// CollisionResult::AboveRoof => pos_x = pos_x.saturating_add(1),
_ => ()
_ => (),
}
}
ControlEvent::RotateLeft => {
@ -96,7 +99,7 @@ fn main() {
} else {
current_piece.rotate_right();
}
},
}
ControlEvent::RotateRight => {
current_piece.rotate_right();
if let Some((new_x, new_y)) = board.try_rotation(&current_piece, pos_x, pos_y) {
@ -105,13 +108,15 @@ fn main() {
} else {
current_piece.rotate_left();
}
},
ControlEvent::DropBlock => {drop_down = true;},
}
ControlEvent::DropBlock => {
drop_down = true;
}
ControlEvent::SpeedChange(s) => speed = s,
ControlEvent::ExitGame => break 'gameloop,
// ControlEvent::MoveUp => pos_y = pos_y.saturating_add(1),
// ControlEvent::MoveDown => pos_y = pos_y.saturating_sub(1)
_ => ()
_ => (),
}
}
}
@ -132,4 +137,4 @@ fn main() {
// // }
// // }
// lp.close();
}
}

View File

@ -1,14 +1,17 @@
use array2d::Array2D;
use rand::{
distributions::{Distribution, Standard},
Rng,
};
use std::cmp;
use std::convert::TryInto;
use rand::{distributions::{Distribution, Standard}, Rng};
#[derive(Debug)]
pub enum Rotation {
Zero,
HalfPi,
Pi,
OneHalfPi
OneHalfPi,
}
#[derive(Debug, PartialEq)]
pub enum CollisionResult {
@ -18,7 +21,15 @@ pub enum CollisionResult {
// AboveRoof
}
#[derive(Debug)]
pub enum Tetromino {S, J, L, I, T, Z, O}
pub enum Tetromino {
S,
J,
L,
I,
T,
Z,
O,
}
impl Distribution<Tetromino> for Standard {
/// Implements random selection of tetrominos
/// This could be replaced, i.e. if there's a Correct(TM) random distribution, implement it here.
@ -30,7 +41,7 @@ impl Distribution<Tetromino> for Standard {
3 => Tetromino::I,
4 => Tetromino::T,
5 => Tetromino::Z,
_ => Tetromino::O
_ => Tetromino::O,
}
}
}
@ -38,7 +49,7 @@ impl Distribution<Tetromino> for Standard {
pub struct Piece {
layout: Array2D<bool>,
pub color: u8,
rotation: Rotation
rotation: Rotation,
}
#[allow(unused)]
@ -47,65 +58,48 @@ impl Piece {
pub fn new(id: Tetromino) -> Piece {
match id {
Tetromino::S => Piece {
layout: Array2D::from_rows(&vec![
vec![false, true, true],
vec![true, true, false]
]),
layout: Array2D::from_rows(&vec![vec![false, true, true], vec![true, true, false]]),
color: 5,
rotation: Rotation::Zero
rotation: Rotation::Zero,
},
Tetromino::J => Piece {
layout: Array2D::from_rows(&vec![
vec![false, true],
vec![false, true],
vec![true, true]
vec![true, true],
]),
color: 13,
rotation: Rotation::Zero
rotation: Rotation::Zero,
},
Tetromino::L => Piece {
layout: Array2D::from_rows(&vec![
vec![true, false],
vec![true, false],
vec![true, true]
vec![true, true],
]),
color: 21,
rotation: Rotation::Zero
rotation: Rotation::Zero,
},
Tetromino::I => Piece {
layout: Array2D::from_rows(&vec![
vec![true],
vec![true],
vec![true],
vec![true]
]),
layout: Array2D::from_rows(&vec![vec![true], vec![true], vec![true], vec![true]]),
color: 3,
rotation: Rotation::Zero
rotation: Rotation::Zero,
},
Tetromino::T => Piece {
layout: Array2D::from_rows(&vec![
vec![true, true, true],
vec![false, true, false]
]),
layout: Array2D::from_rows(&vec![vec![true, true, true], vec![false, true, false]]),
color: 37,
rotation: Rotation::Zero
rotation: Rotation::Zero,
},
Tetromino::Z => Piece {
layout: Array2D::from_rows(&vec![
vec![true, true, false],
vec![false, true, true]
]),
layout: Array2D::from_rows(&vec![vec![true, true, false], vec![false, true, true]]),
color: 45,
rotation: Rotation::Zero
rotation: Rotation::Zero,
},
Tetromino::O => Piece {
layout: Array2D::from_rows(&vec![
vec![true, true],
vec![true, true]
]),
layout: Array2D::from_rows(&vec![vec![true, true], vec![true, true]]),
color: 53,
rotation: Rotation::Zero
}
rotation: Rotation::Zero,
},
}
}
/// Rotates a piece to the left
@ -114,7 +108,7 @@ impl Piece {
Rotation::Zero => Rotation::HalfPi,
Rotation::HalfPi => Rotation::Pi,
Rotation::Pi => Rotation::OneHalfPi,
Rotation::OneHalfPi => Rotation::Zero
Rotation::OneHalfPi => Rotation::Zero,
}
}
/// Rotates a piece to the right
@ -123,7 +117,7 @@ impl Piece {
Rotation::Zero => Rotation::OneHalfPi,
Rotation::HalfPi => Rotation::Zero,
Rotation::Pi => Rotation::HalfPi,
Rotation::OneHalfPi => Rotation::Pi
Rotation::OneHalfPi => Rotation::Pi,
}
}
/// Returns a left-rotated version of the piece
@ -144,7 +138,7 @@ impl Piece {
let mut columns = self.layout.as_columns();
columns.reverse();
Array2D::from_rows(&columns)
},
}
Rotation::Pi => {
let mut layout = self.layout.as_row_major();
layout.reverse();
@ -154,21 +148,21 @@ impl Piece {
let mut rows = self.layout.as_rows();
rows.reverse();
Array2D::from_columns(&rows)
},
}
}
}
}
#[derive(Debug)]
pub struct Board {
matrix: Array2D<u8>
matrix: Array2D<u8>,
}
impl Board {
/// Returns a new, empty board
pub fn new() -> Board {
Board {
matrix: Array2D::filled_with(0, 8, 8)
matrix: Array2D::filled_with(0, 8, 8),
}
}
fn place_impl(&self, piece: &Piece, x: usize, y: usize) -> Array2D<u8> {
@ -177,8 +171,10 @@ impl Board {
for iy in (0..render.num_rows()).rev() {
for ix in 0..render.num_columns() {
match render.get(render.num_rows().saturating_sub(1) - iy, ix) {
Some(true) => {new_matrix.set(y + iy, x + ix, piece.color).ok();},
_ => ()
Some(true) => {
new_matrix.set(y + iy, x + ix, piece.color).ok();
}
_ => (),
};
}
}
@ -214,7 +210,7 @@ impl Board {
match self.matrix.get(iy, x) {
Some(0) => (),
None => (),
Some(_) => return (iy + 1).try_into().unwrap()
Some(_) => return (iy + 1).try_into().unwrap(),
}
}
0
@ -223,7 +219,7 @@ impl Board {
pub fn finished(&self) -> bool {
for ix in 0..8 {
if self.column_height(ix) == 8 {
return true
return true;
}
}
false
@ -239,10 +235,18 @@ impl Board {
for ix in 0..render.num_columns() {
if y + iy >= 8 || (x + ix) >= 8 {
continue;
} else if *render.get(render.num_rows().saturating_sub(iy + 1), ix).unwrap() && *self.matrix.get(y + iy, x + ix).unwrap() > 0 {
println!("Collision at ({}, {}), ({}, {})", y+iy, x+ix, iy, ix);
println!("Block Value: {:?}. Matrix Value: {}.", render.as_rows(), *self.matrix.get(y + iy, x + ix).unwrap());
return CollisionResult::Collides
} else if *render
.get(render.num_rows().saturating_sub(iy + 1), ix)
.unwrap()
&& *self.matrix.get(y + iy, x + ix).unwrap() > 0
{
println!("Collision at ({}, {}), ({}, {})", y + iy, x + ix, iy, ix);
println!(
"Block Value: {:?}. Matrix Value: {}.",
render.as_rows(),
*self.matrix.get(y + iy, x + ix).unwrap()
);
return CollisionResult::Collides;
}
}
}
@ -259,19 +263,19 @@ impl Board {
for i in 0..piece.render().num_columns() {
match self.collides(&piece, x.saturating_sub(i) as usize, y as usize) {
CollisionResult::Unobstructed => return Some((x.saturating_sub(i), y)),
_ => ()
_ => (),
};
}
None
},
}
CollisionResult::Collides => {
match self.collides(&piece, x.saturating_add(1) as usize, y as usize) {
CollisionResult::Unobstructed => return Some((x.saturating_sub(1), y)),
_ => ()
_ => (),
};
match self.collides(&piece, x.saturating_sub(1) as usize, y as usize) {
CollisionResult::Unobstructed => return Some((x.saturating_sub(1), y)),
_ => ()
_ => (),
};
None
}
@ -282,30 +286,16 @@ impl Board {
#[cfg(test)]
mod tests {
fn zero() -> Vec<Vec<bool>> {
vec![
vec![false, true],
vec![false, true],
vec![true, true]
]
vec![vec![false, true], vec![false, true], vec![true, true]]
}
fn half_pi() -> Vec<Vec<bool>> {
vec![
vec![true, true, true],
vec![false, false, true]
]
vec![vec![true, true, true], vec![false, false, true]]
}
fn pi() -> Vec<Vec<bool>> {
vec![
vec![true, true],
vec![true, false],
vec![true, false]
]
vec![vec![true, true], vec![true, false], vec![true, false]]
}
fn one_half_pi() -> Vec<Vec<bool>> {
vec![
vec![true, false, false],
vec![true, true, true]
]
vec![vec![true, false, false], vec![true, true, true]]
}
#[test]
fn rotations() {
@ -397,8 +387,20 @@ mod tests {
#[test]
fn clear_floating_row() {
let mut board = super::Board::new();
board.place(&super::Piece::new(super::Tetromino::L).rotated_left().rotated_left(), 6, 0);
board.place(&super::Piece::new(super::Tetromino::J).rotated_left().rotated_left(), 0, 0);
board.place(
&super::Piece::new(super::Tetromino::L)
.rotated_left()
.rotated_left(),
6,
0,
);
board.place(
&super::Piece::new(super::Tetromino::J)
.rotated_left()
.rotated_left(),
0,
0,
);
board.place(&super::Piece::new(super::Tetromino::I).rotated_left(), 2, 2);
assert_eq!(board.column_height(0), 3);
assert_eq!(board.column_height(1), 3);
@ -426,20 +428,47 @@ mod tests {
fn collide_hbound() {
let board = super::Board::new();
let piece = super::Piece::new(super::Tetromino::I).rotated_left();
assert_eq!(board.collides(&piece, 4, 4), super::CollisionResult::Unobstructed);
assert_eq!(board.collides(&piece, 5, 4), super::CollisionResult::CollidesHBound);
assert_eq!(
board.collides(&piece, 4, 4),
super::CollisionResult::Unobstructed
);
assert_eq!(
board.collides(&piece, 5, 4),
super::CollisionResult::CollidesHBound
);
}
#[test]
fn collide_roof() {
let board = super::Board::new();
let mut piece = super::Piece::new(super::Tetromino::I).rotated_left();
assert_eq!(board.collides(&piece, 2, 7), super::CollisionResult::Unobstructed);
assert_eq!(board.collides(&piece, 2, 8), super::CollisionResult::Unobstructed);
assert_eq!(
board.collides(&piece, 2, 7),
super::CollisionResult::Unobstructed
);
assert_eq!(
board.collides(&piece, 2, 8),
super::CollisionResult::Unobstructed
);
piece.rotate_left();
assert_eq!(board.collides(&piece, 2, 7), super::CollisionResult::Unobstructed);
assert_eq!(board.collides(&piece, 2, 8), super::CollisionResult::Unobstructed);
assert_eq!(board.collides(&piece, 2, 9), super::CollisionResult::Unobstructed);
assert_eq!(board.collides(&piece, 2, 10), super::CollisionResult::Unobstructed);
assert_eq!(board.collides(&piece, 2, 11), super::CollisionResult::Unobstructed);
assert_eq!(
board.collides(&piece, 2, 7),
super::CollisionResult::Unobstructed
);
assert_eq!(
board.collides(&piece, 2, 8),
super::CollisionResult::Unobstructed
);
assert_eq!(
board.collides(&piece, 2, 9),
super::CollisionResult::Unobstructed
);
assert_eq!(
board.collides(&piece, 2, 10),
super::CollisionResult::Unobstructed
);
assert_eq!(
board.collides(&piece, 2, 11),
super::CollisionResult::Unobstructed
);
}
}
}