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,116 +129,102 @@ impl Machine {
return; return;
} }
#[derive(Debug, Clone, Copy)] self.events.clear();
enum Event { for &pos in &self.marbles {
Stay, let marble = self.board.get(pos).unwrap();
/// (new_pos, new_dir) let Tile::Marble { value, dir } = marble else {
MoveTo(Pos, Direction), panic!("broken marble");
/// (new_pos, new_dir, trigger_pos) };
Trigger(Pos, Direction, Pos), let front_pos = dir.step(pos);
/// (other, new_dir) let Some(front_tile) = self.board.get(front_pos) else {
/// other marble should be set to reverse of new_dir self.events.push(Event::Stay);
/// and should be cancelled if it had a movement event planned continue;
Bounce(usize, Direction), };
Remove,
}
let mut marble_events: Vec<Event> = self if let Tile::Powerable(PTile::Bag, _) = front_tile {
.marbles self.events.push(Event::Remove);
.iter() continue;
.map(|&pos| { }
let marble = self.board.get(pos).unwrap(); if let Tile::Powerable(PTile::IO, _) = front_tile {
let Tile::Marble { value, dir } = marble else { self.output.push(value as u8);
panic!("broken marble"); self.events.push(Event::Remove);
}; continue;
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 { let can_move_to = |tile| matches!(tile, Some(Tile::Blank | Tile::Digit(_)));
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 can_move_over = |tile| match tile { let new_dir = mirror.new_dir(dir);
Tile::Mirror(mirror) => { let target_pos = new_dir.step(front_pos);
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); let target = self.board.get(target_pos);
if can_move_to(target) { if can_move_to(target) {
Event::Trigger(target_pos, dir, front_pos) Some((target_pos, new_dir))
} else { } else {
Event::Stay None
} }
} else if let Some((new_pos, new_dir)) = can_move_over(front_tile) { }
Event::MoveTo(new_pos, new_dir) Tile::Arrow(new_dir) => {
} else if let Tile::Marble { let target_pos = new_dir.step(front_pos);
value: _, let target = self.board.get(target_pos);
dir: other_dir, if target_pos == pos || can_move_to(target) {
} = front_tile Some((target_pos, new_dir))
{
if other_dir != dir {
Event::Bounce(
self.marbles.iter().position(|m| m == &front_pos).unwrap(),
dir.opposite(),
)
} else { } 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 { } else {
Event::Stay Event::Stay
} }
}) } else if let Some((new_pos, new_dir)) = can_move_over(front_tile) {
.collect(); 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 // 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);
} }