remove a vec allocation from simulation step

This commit is contained in:
Crispy 2024-12-07 20:22:52 +01:00
parent e0d0a297b9
commit e6d8c1246f

View file

@ -16,6 +16,7 @@ pub struct Machine {
board: Board, board: Board,
marbles: Vec<Pos>, marbles: Vec<Pos>,
powered: Vec<Pos>, powered: Vec<Pos>,
events: Vec<Event>,
input: Vec<u8>, input: Vec<u8>,
input_index: usize, input_index: usize,
@ -23,12 +24,27 @@ pub struct Machine {
steps: usize, 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 { impl Machine {
pub fn new_empty(input: Vec<u8>, width: usize) -> Self { pub fn new_empty(input: Vec<u8>, width: usize) -> Self {
Self { Self {
board: Board::new_empty(width, width), board: Board::new_empty(width, width),
marbles: Vec::new(), marbles: Vec::new(),
powered: Vec::new(), powered: Vec::new(),
events: Vec::new(),
input, input,
input_index: 0, input_index: 0,
output: Vec::new(), output: Vec::new(),
@ -113,39 +129,26 @@ impl Machine {
return; return;
} }
#[derive(Debug, Clone, Copy)] self.events.clear();
enum Event { for &pos in &self.marbles {
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<Event> = self
.marbles
.iter()
.map(|&pos| {
let marble = self.board.get(pos).unwrap(); let marble = self.board.get(pos).unwrap();
let Tile::Marble { value, dir } = marble else { let Tile::Marble { value, dir } = marble else {
panic!("broken marble"); panic!("broken marble");
}; };
let front_pos = dir.step(pos); let front_pos = dir.step(pos);
let Some(front_tile) = self.board.get(front_pos) else { let Some(front_tile) = self.board.get(front_pos) else {
return Event::Stay; self.events.push(Event::Stay);
continue;
}; };
if let Tile::Powerable(PTile::Bag, _) = front_tile { if let Tile::Powerable(PTile::Bag, _) = front_tile {
return Event::Remove; self.events.push(Event::Remove);
continue;
} }
if let Tile::Powerable(PTile::IO, _) = front_tile { if let Tile::Powerable(PTile::IO, _) = front_tile {
self.output.push(value as u8); self.output.push(value as u8);
return Event::Remove; self.events.push(Event::Remove);
continue;
} }
let can_move_to = |tile| matches!(tile, Some(Tile::Blank | Tile::Digit(_))); let can_move_to = |tile| matches!(tile, Some(Tile::Blank | Tile::Digit(_)));
@ -172,8 +175,7 @@ impl Machine {
} }
_ => None, _ => None,
}; };
let e = if can_move_to(Some(front_tile)) {
if can_move_to(Some(front_tile)) {
Event::MoveTo(front_pos, dir) Event::MoveTo(front_pos, dir)
} else if let Tile::Powerable(PTile::Trigger, _) = front_tile { } else if let Tile::Powerable(PTile::Trigger, _) = front_tile {
let target_pos = dir.step(front_pos); let target_pos = dir.step(front_pos);
@ -200,29 +202,29 @@ impl Machine {
} }
} else { } else {
Event::Stay Event::Stay
};
self.events.push(e);
} }
})
.collect();
// resolve bounces // resolve bounces
for i in 0..marble_events.len() { for i in 0..self.events.len() {
let event = marble_events[i]; let event = self.events[i];
if let Event::Bounce(other_index, dir) = event { 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 // cancel bounces on marble that are about to disappear
Event::Remove => { 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 // let already bouncing marbles continue
Event::Bounce(_, _) => (), Event::Bounce(_, _) => (),
// interrupt any other movement/staying to 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 // 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 { if let Event::Remove = event {
self.board.set(self.marbles[i], Tile::Blank); self.board.set(self.marbles[i], Tile::Blank);
} }
@ -230,7 +232,7 @@ impl Machine {
// resolve triggers // resolve triggers
let mut triggers_activated = Vec::new(); 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 { if let Event::Trigger(new_pos, dir, trigger_pos) = event {
triggers_activated.push(*trigger_pos); triggers_activated.push(*trigger_pos);
*event = Event::MoveTo(*new_pos, *dir); *event = Event::MoveTo(*new_pos, *dir);
@ -238,10 +240,10 @@ impl Machine {
} }
// resolve collisions (multiple marbles entering the same space) // resolve collisions (multiple marbles entering the same space)
for i in 0..(marble_events.len() - 1) { for i in 0..(self.events.len() - 1) {
let event = marble_events[i]; let event = self.events[i];
if let Event::MoveTo(new_pos, _dir) = event { 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 { if let Event::MoveTo(other_pos, _other_dir) = other_event {
// todo: maybe sort by direction so the sucessful direction is consistent // todo: maybe sort by direction so the sucessful direction is consistent
if other_pos == &new_pos { if other_pos == &new_pos {
@ -253,7 +255,7 @@ impl Machine {
} }
// resolve movement // resolve movement
for (i, &event) in marble_events.iter().enumerate() { for (i, &event) in self.events.iter().enumerate() {
if let Event::Remove = event { if let Event::Remove = event {
continue; continue;
} }
@ -281,7 +283,7 @@ impl Machine {
} }
// resolve deletions of marbles // 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 { if let Event::Remove = event {
self.marbles.remove(i); self.marbles.remove(i);
} }