extern crate termios;
use nonblock::NonBlockingReader;
use rand::seq::SliceRandom;
const FIELD_COLS: usize = 10;
const FIELD_ROWS_VISIBLE: usize = 20;
const FIELD_ROWS: usize = FIELD_ROWS_VISIBLE + 20;
const FIELD_SIZE: usize = FIELD_COLS * FIELD_ROWS;
const FIGURE_SIZE_COLS: usize = 4;
const FIGURE_SIZE_ROWS: usize = 4;
const FIGURE_SIZE: usize = FIGURE_SIZE_COLS * FIGURE_SIZE_ROWS;
const CELLS_I: [u8; FIGURE_SIZE] = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0];
const CELLS_L2: [u8; FIGURE_SIZE] = [0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0];
const CELLS_L: [u8; FIGURE_SIZE] = [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0];
const CELLS_B: [u8; FIGURE_SIZE] = [0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
const CELLS_S: [u8; FIGURE_SIZE] = [0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0];
const CELLS_T: [u8; FIGURE_SIZE] = [0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0];
const CELLS_Z: [u8; FIGURE_SIZE] = [0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0];
const NEXT_COUNT: usize = 4;
const DISPLAY_NEXT_OFFSET_DOWN: usize = 4;
const DISPLAY_HOLD_OFFSET_DOWN: usize = 4;
const GHOST_MASK: u8 = 8;
enum StepResult {
StateChanged,
Quit,
}
#[derive(Clone, Debug)]
enum FigureKind {
I,
J,
L,
O,
S,
T,
Z,
}
struct Timeout {
beginning: nix::sys::time::TimeSpec,
duration: std::time::Duration,
}
impl Default for Timeout {
#[inline]
fn default() -> Self {
Self {
beginning: nix::sys::time::TimeSpec::new(0, 0),
duration: std::time::Duration::default(),
}
}
}
impl Timeout {
fn from_duration(d: std::time::Duration) -> Self {
Timeout {
beginning: nix::time::clock_gettime(nix::time::ClockId::CLOCK_MONOTONIC).unwrap(),
duration: d,
}
}
fn is_elapsed(&self) -> bool {
std::time::Duration::from(self.beginning) + self.duration
<= std::time::Duration::from(
nix::time::clock_gettime(nix::time::ClockId::CLOCK_MONOTONIC).unwrap(),
)
}
}
#[derive(Clone)]
struct Figure {
kind: FigureKind,
cells: [u8; FIGURE_SIZE],
}
impl Figure {
fn from_kind(kind: FigureKind) -> Self {
match kind {
FigureKind::I => Self {
cells: CELLS_I,
kind,
},
FigureKind::J => Self {
cells: CELLS_L2,
kind,
},
FigureKind::L => Self {
cells: CELLS_L,
kind,
},
FigureKind::O => Self {
cells: CELLS_B,
kind,
},
FigureKind::S => Self {
cells: CELLS_S,
kind,
},
FigureKind::T => Self {
cells: CELLS_T,
kind,
},
FigureKind::Z => Self {
cells: CELLS_Z,
kind,
},
}
}
fn rotate_cw_4x4(&mut self) {
let mut tmp: [u8; 4 * 4] = Default::default();
for y in 0..4 {
for x in 0..4 {
tmp[y + (3 - x) * 4] = self.cells[x + y * 4];
}
}
self.cells = tmp;
}
fn rotate_cw_3x3(&mut self) {
let mut tmp: [u8; 3 * 3] = Default::default();
for y in 0..3 {
for x in 0..3 {
tmp[y + (2 - x) * 3] = self.cells[x + y * 4];
}
}
for y in 0..3 {
for x in 0..3 {
self.cells[x + y * 4] = tmp[x + y * 3];
}
}
}
fn rotate(&mut self) {
match self.kind {
FigureKind::I => self.rotate_cw_4x4(),
FigureKind::J => self.rotate_cw_3x3(),
FigureKind::L => self.rotate_cw_3x3(),
FigureKind::O => {}
FigureKind::S => self.rotate_cw_3x3(),
FigureKind::T => self.rotate_cw_3x3(),
FigureKind::Z => self.rotate_cw_3x3(),
}
}
}
trait Cell {
fn is_ghost(&self) -> bool;
fn set_ghost(&mut self, ghost: bool);
fn color(&self) -> Self;
}
impl Cell for u8 {
fn is_ghost(&self) -> bool {
*self & GHOST_MASK != 0
}
fn set_ghost(&mut self, ghost: bool) {
if ghost {
*self |= GHOST_MASK;
} else {
*self &= !GHOST_MASK;
}
}
fn color(&self) -> Self {
self & !GHOST_MASK
}
}
#[derive(Clone)]
struct Field {
cells: [u8; FIELD_SIZE],
}
impl Default for Field {
#[inline]
fn default() -> Self {
Self {
cells: [0; FIELD_SIZE],
}
}
}
impl Field {
fn render(&mut self, figure: &Figure, x: isize, y: isize) {
for i in 0..FIGURE_SIZE {
let f_x = (i % FIGURE_SIZE_COLS) as isize + x;
let f_y = (i / FIGURE_SIZE_ROWS) as isize + y;
if f_x < 0 || f_x >= FIELD_COLS as isize || f_y < 0 || f_y >= FIELD_ROWS as isize {
continue;
}
let color = figure.cells[i].color();
let current = f_x as usize + f_y as usize * FIELD_COLS;
if color != 0 && (self.cells[current].is_ghost() || self.cells[current] == 0) {
self.cells[current] = figure.cells[i];
}
}
}
fn has_collision(&self, figure: &Figure, x: isize, y: isize) -> bool {
for i in 0..FIGURE_SIZE {
let f_x = (i % FIGURE_SIZE_COLS) as isize + x;
let f_y = (i / FIGURE_SIZE_ROWS) as isize + y;
let color = figure.cells[i].color();
if color != 0 {
if f_x < 0 || f_x >= FIELD_COLS as isize || f_y < 0 || f_y >= FIELD_ROWS as isize {
return true;
}
let fcolor = self.cells[f_x as usize + f_y as usize * FIELD_COLS].color();
if fcolor != 0 {
return true;
}
}
}
false
}
fn is_line_complete(&self, line: usize) -> bool {
assert!(line < FIELD_ROWS);
self.cells[FIELD_COLS * line..][..FIELD_COLS]
.iter()
.fold(1, |a, e| a & e)
!= 0
}
fn kill_line(&mut self, line: usize) {
for row in line..FIELD_ROWS - 1 {
for col in 0..FIELD_COLS {
self.cells[col + row * FIELD_COLS] = self.cells[col + (row + 1) * FIELD_COLS];
}
}
for col in 0..FIELD_COLS {
self.cells[col + (FIELD_ROWS - 1) * FIELD_COLS] = 0;
}
}
}
struct Bag {
index: u8,
pieces: [u8; 7],
}
impl Bag {
fn next(&mut self) -> FigureKind {
if self.index >= 7 {
self.index = 0;
self.pieces.shuffle(&mut rand::thread_rng());
}
let i = self.index;
self.index += 1;
match self.pieces[i as usize] {
1 => FigureKind::I,
2 => FigureKind::J,
3 => FigureKind::L,
5 => FigureKind::O,
6 => FigureKind::S,
4 => FigureKind::T,
7 => FigureKind::Z,
_ => unreachable!(),
}
}
}
impl Default for Bag {
#[inline]
fn default() -> Self {
let mut pieces: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
pieces.shuffle(&mut rand::thread_rng());
Self { index: 0, pieces }
}
}
enum Parameter {
Bool(bool),
Int(i32),
}
enum MenuAction {
Run,
Quit,
}
enum MenuContent {
Menu(Vec