remove a vec allocation from simulation step
This commit is contained in:
parent
e0d0a297b9
commit
e6d8c1246f
1 changed files with 92 additions and 90 deletions
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue