From e6d8c1246fdfbee7d784766be7f27ebeceab92fe Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Sat, 7 Dec 2024 20:22:52 +0100 Subject: [PATCH] remove a vec allocation from simulation step --- src/marble_engine.rs | 182 ++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 90 deletions(-) diff --git a/src/marble_engine.rs b/src/marble_engine.rs index 6b521b9..e4c997c 100644 --- a/src/marble_engine.rs +++ b/src/marble_engine.rs @@ -16,6 +16,7 @@ pub struct Machine { board: Board, marbles: Vec, powered: Vec, + events: Vec, input: Vec, input_index: usize, @@ -23,12 +24,27 @@ 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(), @@ -113,116 +129,102 @@ impl Machine { return; } - #[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, - } + 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; + }; - 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 { + 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; + } - 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_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 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) { - Event::Trigger(target_pos, dir, front_pos) + Some((target_pos, new_dir)) } else { - Event::Stay + None } - } 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(), - ) + } + 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 { - Event::Stay + 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 } - }) - .collect(); + } 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); + } // resolve bounces - for i in 0..marble_events.len() { - let event = marble_events[i]; + for i in 0..self.events.len() { + let event = self.events[i]; if let Event::Bounce(other_index, dir) = event { - match marble_events[other_index] { + match self.events[other_index] { // cancel bounces on marble that are about to disappear Event::Remove => { - marble_events[i] = Event::MoveTo(self.marbles[other_index], dir.opposite()) + self.events[i] = Event::MoveTo(self.marbles[other_index], dir.opposite()) } // let already bouncing marbles continue Event::Bounce(_, _) => (), // interrupt any other movement/staying to bounce - _ => marble_events[other_index] = Event::Bounce(i, dir.opposite()), + _ => self.events[other_index] = Event::Bounce(i, dir.opposite()), } } } // resolve deletions of tiles - for (i, event) in marble_events.iter().enumerate() { + for (i, event) in self.events.iter().enumerate() { if let Event::Remove = event { self.board.set(self.marbles[i], Tile::Blank); } @@ -230,7 +232,7 @@ impl Machine { // resolve triggers let mut triggers_activated = Vec::new(); - for event in &mut marble_events { + for event in &mut self.events { if let Event::Trigger(new_pos, dir, trigger_pos) = event { triggers_activated.push(*trigger_pos); *event = Event::MoveTo(*new_pos, *dir); @@ -238,10 +240,10 @@ impl Machine { } // resolve collisions (multiple marbles entering the same space) - for i in 0..(marble_events.len() - 1) { - let event = marble_events[i]; + for i in 0..(self.events.len() - 1) { + let event = self.events[i]; if let Event::MoveTo(new_pos, _dir) = event { - for other_event in &mut marble_events[(i + 1)..] { + for other_event in &mut self.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 { @@ -253,7 +255,7 @@ impl Machine { } // resolve movement - for (i, &event) in marble_events.iter().enumerate() { + for (i, &event) in self.events.iter().enumerate() { if let Event::Remove = event { continue; } @@ -281,7 +283,7 @@ impl Machine { } // resolve deletions of marbles - for (i, event) in marble_events.iter().enumerate().rev() { + for (i, event) in self.events.iter().enumerate().rev() { if let Event::Remove = event { self.marbles.remove(i); }