fix bag logic
This commit is contained in:
parent
7caef1ce4a
commit
e11653d8dc
159
src/game.rs
159
src/game.rs
@ -2,6 +2,13 @@ use crate::actions::{Action, ActionsHeld};
|
|||||||
use crate::board::Board;
|
use crate::board::Board;
|
||||||
use crate::tetromino::{Direction, DirectionDiff, Tetromino};
|
use crate::tetromino::{Direction, DirectionDiff, Tetromino};
|
||||||
|
|
||||||
|
pub enum SoundEffect {
|
||||||
|
HardDrop,
|
||||||
|
LineClear(usize),
|
||||||
|
Move,
|
||||||
|
Rotation,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CurrentTetromino {
|
pub struct CurrentTetromino {
|
||||||
pub tetromino: Tetromino,
|
pub tetromino: Tetromino,
|
||||||
@ -42,6 +49,7 @@ pub struct Game {
|
|||||||
pub game_over: bool,
|
pub game_over: bool,
|
||||||
pub board: Board,
|
pub board: Board,
|
||||||
pub next_tetrominos: [Tetromino; 3],
|
pub next_tetrominos: [Tetromino; 3],
|
||||||
|
bag: Bag,
|
||||||
pub current_tetromino: CurrentTetromino,
|
pub current_tetromino: CurrentTetromino,
|
||||||
pub held_tetromino: Option<Tetromino>,
|
pub held_tetromino: Option<Tetromino>,
|
||||||
has_swapped_held: bool,
|
has_swapped_held: bool,
|
||||||
@ -49,74 +57,68 @@ pub struct Game {
|
|||||||
pub ticks: usize,
|
pub ticks: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SoundEffect {
|
struct Bag {
|
||||||
HardDrop,
|
inner: [Tetromino; 7],
|
||||||
LineClear(usize),
|
idx: usize,
|
||||||
Move,
|
|
||||||
Rotation,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Score {
|
impl Bag {
|
||||||
pub level: usize,
|
fn new() -> Self {
|
||||||
pub points: usize,
|
|
||||||
pub lines: usize,
|
|
||||||
pub combo: usize,
|
|
||||||
back_to_back: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Score {
|
|
||||||
const fn new() -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
level: 0,
|
inner: Self::random_tetrominos(),
|
||||||
points: 0,
|
idx: 0,
|
||||||
lines: 0,
|
|
||||||
combo: 0,
|
|
||||||
back_to_back: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn random_tetrominos() -> [Tetromino; 7] {
|
||||||
|
use rand::seq::IndexedRandom;
|
||||||
|
let sample = [
|
||||||
|
Tetromino::I,
|
||||||
|
Tetromino::J,
|
||||||
|
Tetromino::L,
|
||||||
|
Tetromino::O,
|
||||||
|
Tetromino::S,
|
||||||
|
Tetromino::T,
|
||||||
|
Tetromino::Z,
|
||||||
|
];
|
||||||
|
|
||||||
fn level_up(&mut self, lines_cleared: usize) {
|
debug_assert_eq!(sample.len(), 7, "each piece should only appear once");
|
||||||
self.lines += lines_cleared;
|
|
||||||
if self.lines > self.level * 5 {
|
sample
|
||||||
self.level += 1;
|
.choose_multiple_array(&mut rand::rng())
|
||||||
self.lines = 0;
|
.expect("both arrays should have a length of 7")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
fn take_next(&mut self) -> Tetromino {
|
||||||
fn point_multiplier_from_lines_cleared(lines_cleared: usize) -> f32 {
|
if self.idx >= self.inner.len() {
|
||||||
match lines_cleared {
|
self.idx = 0;
|
||||||
0 => 0.0,
|
self.inner = Self::random_tetrominos();
|
||||||
1 => 100.0,
|
|
||||||
2 => 300.0,
|
|
||||||
3 => 500.0,
|
|
||||||
4 => 800.0,
|
|
||||||
_ => unreachable!("we cannot clear more than 4 lines"),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn combos(&self, lines_cleared: usize) -> usize {
|
let uninitialized_tetromino = Tetromino::I;
|
||||||
if lines_cleared > 0 {
|
let current = std::mem::replace(&mut self.inner[self.idx], uninitialized_tetromino);
|
||||||
self.combo * 50 * self.level
|
self.idx += 1;
|
||||||
} else {
|
current
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
let mut bag = Bag::new();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
game_over: false,
|
game_over: false,
|
||||||
board: Board::new(),
|
board: Board::new(),
|
||||||
next_tetrominos: std::array::from_fn(|_| Tetromino::random()),
|
next_tetrominos: std::array::from_fn(|_| bag.take_next()),
|
||||||
current_tetromino: CurrentTetromino::new(Tetromino::random()),
|
current_tetromino: CurrentTetromino::new(bag.take_next()),
|
||||||
held_tetromino: None,
|
held_tetromino: None,
|
||||||
|
bag,
|
||||||
has_swapped_held: false,
|
has_swapped_held: false,
|
||||||
score: Score::new(),
|
score: Score::new(),
|
||||||
ticks: 0,
|
ticks: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn take_next_in_bag(&mut self, mut last: Tetromino) -> Tetromino {
|
|
||||||
|
fn take_next_up(&mut self) -> Tetromino {
|
||||||
|
let mut last = self.bag.take_next();
|
||||||
for value in self.next_tetrominos.iter_mut().rev() {
|
for value in self.next_tetrominos.iter_mut().rev() {
|
||||||
std::mem::swap(value, &mut last)
|
std::mem::swap(value, &mut last)
|
||||||
}
|
}
|
||||||
@ -265,7 +267,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn place_current_tetromino(&mut self) {
|
fn place_current_tetromino(&mut self) {
|
||||||
let next = CurrentTetromino::new(self.take_next_in_bag(Tetromino::random()));
|
let next = CurrentTetromino::new(self.take_next_up());
|
||||||
let current = std::mem::replace(&mut self.current_tetromino, next);
|
let current = std::mem::replace(&mut self.current_tetromino, next);
|
||||||
let pattern = current.tetromino.pattern(¤t.direction);
|
let pattern = current.tetromino.pattern(¤t.direction);
|
||||||
|
|
||||||
@ -301,7 +303,7 @@ impl Game {
|
|||||||
let held_or_first_in_bag_tetromino = self
|
let held_or_first_in_bag_tetromino = self
|
||||||
.held_tetromino
|
.held_tetromino
|
||||||
.take()
|
.take()
|
||||||
.unwrap_or_else(|| self.take_next_in_bag(Tetromino::random()));
|
.unwrap_or_else(|| self.take_next_up());
|
||||||
let current_tetromino = CurrentTetromino::new(held_or_first_in_bag_tetromino);
|
let current_tetromino = CurrentTetromino::new(held_or_first_in_bag_tetromino);
|
||||||
let old_tetromino = std::mem::replace(&mut self.current_tetromino, current_tetromino);
|
let old_tetromino = std::mem::replace(&mut self.current_tetromino, current_tetromino);
|
||||||
self.held_tetromino.replace(old_tetromino.tetromino);
|
self.held_tetromino.replace(old_tetromino.tetromino);
|
||||||
@ -309,26 +311,49 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
pub struct Score {
|
||||||
mod test {
|
pub level: usize,
|
||||||
use super::{Board, CurrentTetromino, Game, Score, Tetromino};
|
pub points: usize,
|
||||||
|
pub lines: usize,
|
||||||
|
pub combo: usize,
|
||||||
|
back_to_back: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
impl Score {
|
||||||
fn advance_bag() {
|
const fn new() -> Self {
|
||||||
let mut game = Game {
|
Self {
|
||||||
game_over: false,
|
level: 0,
|
||||||
board: Board::new(),
|
points: 0,
|
||||||
score: Score::new(),
|
lines: 0,
|
||||||
next_tetrominos: [Tetromino::I, Tetromino::J, Tetromino::O],
|
combo: 0,
|
||||||
current_tetromino: CurrentTetromino::new(Tetromino::J),
|
back_to_back: false,
|
||||||
held_tetromino: None,
|
}
|
||||||
has_swapped_held: false,
|
}
|
||||||
ticks: 0,
|
|
||||||
};
|
fn level_up(&mut self, lines_cleared: usize) {
|
||||||
assert_eq!(game.take_next_in_bag(Tetromino::S), Tetromino::I);
|
self.lines += lines_cleared;
|
||||||
assert_eq!(
|
if self.lines > self.level * 5 {
|
||||||
game.next_tetrominos,
|
self.level += 1;
|
||||||
[Tetromino::J, Tetromino::O, Tetromino::S]
|
self.lines = 0;
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn point_multiplier_from_lines_cleared(lines_cleared: usize) -> f32 {
|
||||||
|
match lines_cleared {
|
||||||
|
0 => 0.0,
|
||||||
|
1 => 100.0,
|
||||||
|
2 => 300.0,
|
||||||
|
3 => 500.0,
|
||||||
|
4 => 800.0,
|
||||||
|
_ => unreachable!("we cannot clear more than 4 lines"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn combos(&self, lines_cleared: usize) -> usize {
|
||||||
|
if lines_cleared > 0 {
|
||||||
|
self.combo * 50 * self.level
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,20 +38,6 @@ pub enum DirectionDiff {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Tetromino {
|
impl Tetromino {
|
||||||
pub fn random() -> Self {
|
|
||||||
let v: u8 = rand::random();
|
|
||||||
match v % 7 {
|
|
||||||
0 => Self::I,
|
|
||||||
1 => Self::J,
|
|
||||||
2 => Self::L,
|
|
||||||
3 => Self::O,
|
|
||||||
4 => Self::S,
|
|
||||||
5 => Self::T,
|
|
||||||
6 => Self::Z,
|
|
||||||
_ => unreachable!("v%7 is always in range 0..=6"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pattern(&self, direction: &Direction) -> Vec<(usize, usize)> {
|
pub fn pattern(&self, direction: &Direction) -> Vec<(usize, usize)> {
|
||||||
self.raw_pattern(direction)
|
self.raw_pattern(direction)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
Loading…
Reference in New Issue
Block a user