diff --git a/src/editor.rs b/src/editor.rs index 90f2b29..0cb0d7c 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -13,7 +13,7 @@ use crate::{ level::Level, marble_engine::{ board::Board, - pos::{Pos, PosInt}, + pos::Pos, tile::{Direction, GateType, MathOp, MirrorType, PTile, Tile, WireType}, Machine, }, @@ -28,10 +28,10 @@ const HEADER_HEIGHT: i32 = 40; const FOOTER_HEIGHT: i32 = 95; const SIDEBAR_WIDTH: i32 = 200 + 32 * 2 + 5 * 4; const MAX_ZOOM_IN: i32 = 3; -const BOARD_MARGIN: PosInt = 3; +const BOARD_MARGIN: isize = 3; const MAX_SPEED_POWER: u8 = 16; const SPEED_DIGITS: u8 = 5; -const MAX_FRAME_TIME_MICROS: u128 = 1_000_000 / 30; +const MAX_FRAME_TIME_MICROS: u128 = 1000_000 / 30; #[derive(Debug)] pub struct Editor { @@ -63,7 +63,6 @@ pub struct Editor { selected_blueprint: usize, blueprint_scroll: usize, step_time: u128, - max_step_time:u128, } #[derive(Debug, PartialEq)] @@ -140,7 +139,6 @@ impl Editor { selected_blueprint: usize::MAX, blueprint_scroll: 0, step_time: 0, - max_step_time: 0, } } @@ -165,7 +163,6 @@ impl Editor { } fn start_sim(&mut self) { - self.max_step_time = 0; self.machine.reset(); self.machine.set_board(self.source_board.clone()); } @@ -183,7 +180,9 @@ impl Editor { } fn step(&mut self) { + let start_time = Instant::now(); self.machine.step(); + self.step_time = start_time.elapsed().as_micros(); if self.complete_popup == Popup::Visible { self.complete_popup = Popup::Dismissed; @@ -335,10 +334,9 @@ impl Editor { if self.sim_state == SimState::Running { self.time_since_step += rl.get_frame_time(); let step_size = 1. / (1 << self.sim_speed) as f32; - let mut steps_taken = 0; - let start_time = Instant::now(); if self.time_since_step > step_size { let step_count = (self.time_since_step / step_size) as u32; + let start_time = Instant::now(); for _ in 0..step_count { if self.sim_state != SimState::Running { // pause on level completion @@ -348,17 +346,9 @@ impl Editor { break; } self.step(); - steps_taken += 1; } self.time_since_step -= step_count as f32 * step_size; } - let avg_step_time = start_time - .elapsed() - .as_micros() - .checked_div(steps_taken) - .unwrap_or_default(); - self.step_time = avg_step_time; - self.max_step_time = avg_step_time.max(self.max_step_time); } if rl.is_key_pressed(KeyboardKey::KEY_SPACE) { self.step_pressed() @@ -601,7 +591,6 @@ impl Editor { draw_usize(d, textures, self.machine.step_count(), 420, 4, 9, 2); draw_usize(d, textures, self.step_time as usize, 540, 42, 9, 1); - draw_usize(d, textures, self.max_step_time as usize, 540, 58, 9, 1); d.draw_text("input:", 603, 8, 10, Color::WHITE); if simple_button(d, 600, 20, 35, 15) { diff --git a/src/marble_engine.rs b/src/marble_engine.rs index e4c997c..6b521b9 100644 --- a/src/marble_engine.rs +++ b/src/marble_engine.rs @@ -16,7 +16,6 @@ pub struct Machine { board: Board, marbles: Vec, powered: Vec, - events: Vec, input: Vec, input_index: usize, @@ -24,27 +23,12 @@ pub struct Machine { steps: usize, } -#[derive(Debug, Clone, Copy)] -enum Event { - Stay, - /// (new_pos, new_dir) - MoveTo(Pos, Direction), - /// (new_pos, new_dir, trigger_pos) - Trigger(Pos, Direction, Pos), - /// (other, new_dir) - /// other marble should be set to reverse of new_dir - /// and should be cancelled if it had a movement event planned - Bounce(usize, Direction), - Remove, -} - impl Machine { pub fn new_empty(input: Vec, width: usize) -> Self { Self { board: Board::new_empty(width, width), marbles: Vec::new(), powered: Vec::new(), - events: Vec::new(), input, input_index: 0, output: Vec::new(), @@ -129,102 +113,116 @@ impl Machine { return; } - self.events.clear(); - for &pos in &self.marbles { - let marble = self.board.get(pos).unwrap(); - let Tile::Marble { value, dir } = marble else { - panic!("broken marble"); - }; - let front_pos = dir.step(pos); - let Some(front_tile) = self.board.get(front_pos) else { - self.events.push(Event::Stay); - continue; - }; - - if let Tile::Powerable(PTile::Bag, _) = front_tile { - self.events.push(Event::Remove); - continue; - } - if let Tile::Powerable(PTile::IO, _) = front_tile { - self.output.push(value as u8); - self.events.push(Event::Remove); - continue; - } - - let can_move_to = |tile| matches!(tile, Some(Tile::Blank | Tile::Digit(_))); - - let can_move_over = |tile| match tile { - Tile::Mirror(mirror) => { - let new_dir = mirror.new_dir(dir); - let target_pos = new_dir.step(front_pos); - let target = self.board.get(target_pos); - if can_move_to(target) { - Some((target_pos, new_dir)) - } else { - None - } - } - Tile::Arrow(new_dir) => { - let target_pos = new_dir.step(front_pos); - let target = self.board.get(target_pos); - if target_pos == pos || can_move_to(target) { - Some((target_pos, new_dir)) - } else { - None - } - } - _ => None, - }; - let e = if can_move_to(Some(front_tile)) { - Event::MoveTo(front_pos, dir) - } else if let Tile::Powerable(PTile::Trigger, _) = front_tile { - let target_pos = dir.step(front_pos); - let target = self.board.get(target_pos); - if can_move_to(target) { - Event::Trigger(target_pos, dir, front_pos) - } else { - Event::Stay - } - } else if let Some((new_pos, new_dir)) = can_move_over(front_tile) { - Event::MoveTo(new_pos, new_dir) - } else if let Tile::Marble { - value: _, - dir: other_dir, - } = front_tile - { - if other_dir != dir { - Event::Bounce( - self.marbles.iter().position(|m| m == &front_pos).unwrap(), - dir.opposite(), - ) - } else { - Event::Stay - } - } else { - Event::Stay - }; - self.events.push(e); + #[derive(Debug, Clone, Copy)] + enum Event { + Stay, + /// (new_pos, new_dir) + MoveTo(Pos, Direction), + /// (new_pos, new_dir, trigger_pos) + Trigger(Pos, Direction, Pos), + /// (other, new_dir) + /// other marble should be set to reverse of new_dir + /// and should be cancelled if it had a movement event planned + Bounce(usize, Direction), + Remove, } + let mut marble_events: Vec = self + .marbles + .iter() + .map(|&pos| { + let marble = self.board.get(pos).unwrap(); + let Tile::Marble { value, dir } = marble else { + panic!("broken marble"); + }; + let front_pos = dir.step(pos); + let Some(front_tile) = self.board.get(front_pos) else { + return Event::Stay; + }; + + if let Tile::Powerable(PTile::Bag, _) = front_tile { + return Event::Remove; + } + if let Tile::Powerable(PTile::IO, _) = front_tile { + self.output.push(value as u8); + return Event::Remove; + } + + let can_move_to = |tile| matches!(tile, Some(Tile::Blank | Tile::Digit(_))); + + let can_move_over = |tile| match tile { + Tile::Mirror(mirror) => { + let new_dir = mirror.new_dir(dir); + let target_pos = new_dir.step(front_pos); + let target = self.board.get(target_pos); + if can_move_to(target) { + Some((target_pos, new_dir)) + } else { + None + } + } + Tile::Arrow(new_dir) => { + let target_pos = new_dir.step(front_pos); + let target = self.board.get(target_pos); + if target_pos == pos || can_move_to(target) { + Some((target_pos, new_dir)) + } else { + None + } + } + _ => None, + }; + + if can_move_to(Some(front_tile)) { + Event::MoveTo(front_pos, dir) + } else if let Tile::Powerable(PTile::Trigger, _) = front_tile { + let target_pos = dir.step(front_pos); + let target = self.board.get(target_pos); + if can_move_to(target) { + Event::Trigger(target_pos, dir, front_pos) + } else { + Event::Stay + } + } else if let Some((new_pos, new_dir)) = can_move_over(front_tile) { + Event::MoveTo(new_pos, new_dir) + } else if let Tile::Marble { + value: _, + dir: other_dir, + } = front_tile + { + if other_dir != dir { + Event::Bounce( + self.marbles.iter().position(|m| m == &front_pos).unwrap(), + dir.opposite(), + ) + } else { + Event::Stay + } + } else { + Event::Stay + } + }) + .collect(); + // resolve bounces - for i in 0..self.events.len() { - let event = self.events[i]; + for i in 0..marble_events.len() { + let event = marble_events[i]; if let Event::Bounce(other_index, dir) = event { - match self.events[other_index] { + match marble_events[other_index] { // cancel bounces on marble that are about to disappear Event::Remove => { - self.events[i] = Event::MoveTo(self.marbles[other_index], dir.opposite()) + marble_events[i] = Event::MoveTo(self.marbles[other_index], dir.opposite()) } // let already bouncing marbles continue Event::Bounce(_, _) => (), // interrupt any other movement/staying to bounce - _ => self.events[other_index] = Event::Bounce(i, dir.opposite()), + _ => marble_events[other_index] = Event::Bounce(i, dir.opposite()), } } } // resolve deletions of tiles - for (i, event) in self.events.iter().enumerate() { + for (i, event) in marble_events.iter().enumerate() { if let Event::Remove = event { self.board.set(self.marbles[i], Tile::Blank); } @@ -232,7 +230,7 @@ impl Machine { // resolve triggers let mut triggers_activated = Vec::new(); - for event in &mut self.events { + for event in &mut marble_events { if let Event::Trigger(new_pos, dir, trigger_pos) = event { triggers_activated.push(*trigger_pos); *event = Event::MoveTo(*new_pos, *dir); @@ -240,10 +238,10 @@ impl Machine { } // resolve collisions (multiple marbles entering the same space) - for i in 0..(self.events.len() - 1) { - let event = self.events[i]; + for i in 0..(marble_events.len() - 1) { + let event = marble_events[i]; if let Event::MoveTo(new_pos, _dir) = event { - for other_event in &mut self.events[(i + 1)..] { + for other_event in &mut marble_events[(i + 1)..] { if let Event::MoveTo(other_pos, _other_dir) = other_event { // todo: maybe sort by direction so the sucessful direction is consistent if other_pos == &new_pos { @@ -255,7 +253,7 @@ impl Machine { } // resolve movement - for (i, &event) in self.events.iter().enumerate() { + for (i, &event) in marble_events.iter().enumerate() { if let Event::Remove = event { continue; } @@ -283,7 +281,7 @@ impl Machine { } // resolve deletions of marbles - for (i, event) in self.events.iter().enumerate().rev() { + for (i, event) in marble_events.iter().enumerate().rev() { if let Event::Remove = event { self.marbles.remove(i); } diff --git a/src/marble_engine/board.rs b/src/marble_engine/board.rs index 7a401d3..9f69ceb 100644 --- a/src/marble_engine/board.rs +++ b/src/marble_engine/board.rs @@ -2,7 +2,6 @@ use crate::{draw_scaled_texture, Textures}; use super::tile::*; use super::Pos; -use super::PosInt; use raylib::prelude::*; #[derive(Debug, Clone)] @@ -74,7 +73,7 @@ impl Board { } fn in_bounds(&self, p: Pos) -> bool { - p.x >= 0 && p.y >= 0 && p.x < self.width as PosInt && p.y < self.height as PosInt + p.x >= 0 && p.y >= 0 && p.x < self.width as isize && p.y < self.height as isize } pub fn get(&self, p: Pos) -> Option { @@ -117,11 +116,11 @@ impl Board { } } - pub fn grow_to_include(&mut self, p: Pos) -> (PosInt,PosInt) { + pub fn grow_to_include(&mut self, p: Pos) -> (isize, isize) { let mut offset_x = 0; let mut offset_y = 0; if p.x < 0 { - let len = p.x.unsigned_abs() as usize; + let len = p.x.unsigned_abs(); for row in &mut self.rows { let mut new_row = vec![Tile::Blank; len]; new_row.append(row); @@ -138,7 +137,7 @@ impl Board { } if p.y < 0 { - let len = p.y.unsigned_abs() as usize; + let len = p.y.unsigned_abs(); let mut new_rows = vec![vec![Tile::Blank; self.width]; len]; new_rows.append(&mut self.rows); self.rows = new_rows; @@ -149,7 +148,7 @@ impl Board { self.rows.resize(new_height, vec![Tile::Blank; self.width]); self.height = new_height; } - (offset_x as PosInt, offset_y as PosInt) + (offset_x as isize, offset_y as isize) } pub fn trim_size(&mut self, margin: usize) -> (usize, usize) { diff --git a/src/marble_engine/pos.rs b/src/marble_engine/pos.rs index 9eea35c..3979b3a 100644 --- a/src/marble_engine/pos.rs +++ b/src/marble_engine/pos.rs @@ -2,12 +2,10 @@ use std::ops::Add; use raylib::prelude::*; -pub type PosInt = i16; - #[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Pos { - pub x: PosInt, - pub y: PosInt, + pub x: isize, + pub y: isize, } impl Pos { @@ -36,8 +34,8 @@ impl Pos { impl From<(usize, usize)> for Pos { fn from(value: (usize, usize)) -> Self { Self { - x: value.0 as PosInt, - y: value.1 as PosInt, + x: value.0 as isize, + y: value.1 as isize, } } } @@ -45,8 +43,8 @@ impl From<(usize, usize)> for Pos { impl From<(i32, i32)> for Pos { fn from(value: (i32, i32)) -> Self { Self { - x: value.0 as PosInt, - y: value.1 as PosInt, + x: value.0 as isize, + y: value.1 as isize, } } } @@ -54,8 +52,8 @@ impl From<(i32, i32)> for Pos { impl From for Pos { fn from(vec: Vector2) -> Self { Self { - x: vec.x as PosInt, - y: vec.y as PosInt, + x: vec.x as isize, + y: vec.y as isize, } } }