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,
marbles: Vec<Pos>,
powered: Vec<Pos>,
events: Vec<Event>,
input: Vec<u8>,
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<u8>, 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,39 +129,26 @@ 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,
}
let mut marble_events: Vec<Event> = self
.marbles
.iter()
.map(|&pos| {
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 {
return Event::Stay;
self.events.push(Event::Stay);
continue;
};
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 {
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(_)));
@ -172,8 +175,7 @@ impl Machine {
}
_ => None,
};
if can_move_to(Some(front_tile)) {
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);
@ -200,29 +202,29 @@ impl Machine {
}
} else {
Event::Stay
};
self.events.push(e);
}
})
.collect();
// 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);
}