split step into functions and set up stuff for flamegraph to be usable
This commit is contained in:
parent
48b5b3f3c7
commit
660746f16b
4 changed files with 115 additions and 54 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,3 +2,5 @@
|
||||||
/user*
|
/user*
|
||||||
*.zip
|
*.zip
|
||||||
version.txt
|
version.txt
|
||||||
|
perf.data*
|
||||||
|
flamegraph.svg
|
||||||
|
|
|
@ -10,6 +10,10 @@ raylib = "5.5"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# affects simulation sub-steps, used for profiling
|
||||||
|
inline_less = []
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "bench"
|
name = "bench"
|
||||||
path = "src/benchmark.rs"
|
path = "src/benchmark.rs"
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -22,3 +22,9 @@ windows:
|
||||||
rm -rf ${RELEASE_DIRNAME}_win
|
rm -rf ${RELEASE_DIRNAME}_win
|
||||||
|
|
||||||
all: windows linux
|
all: windows linux
|
||||||
|
|
||||||
|
flamegraph:
|
||||||
|
cargo flamegraph --release --bin bench --features "inline_less"
|
||||||
|
|
||||||
|
bench:
|
||||||
|
cargo run --release --bin bench
|
||||||
|
|
|
@ -12,13 +12,19 @@ use tile::*;
|
||||||
pub struct Machine {
|
pub struct Machine {
|
||||||
grid: Grid,
|
grid: Grid,
|
||||||
marbles: Vec<Pos>,
|
marbles: Vec<Pos>,
|
||||||
powered: Vec<Pos>,
|
|
||||||
input: Vec<u8>,
|
input: Vec<u8>,
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
output: Vec<u8>,
|
output: Vec<u8>,
|
||||||
steps: usize,
|
steps: usize,
|
||||||
pub subtick_index: usize,
|
pub subtick_index: usize,
|
||||||
pub debug_subticks: Vec<DebugSubTick>,
|
pub debug_subticks: Vec<DebugSubTick>,
|
||||||
|
// used across steps
|
||||||
|
powered: Vec<Pos>,
|
||||||
|
// used within steps
|
||||||
|
new_marbles: Vec<(Pos, MarbleValue, Direction)>,
|
||||||
|
influenced_direction: Vec<(bool, DirInfluence)>,
|
||||||
|
claim_positions: Vec<Pos>,
|
||||||
|
removed_marbles: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -27,18 +33,29 @@ pub struct DebugSubTick {
|
||||||
pub pos: Option<Pos>,
|
pub pos: Option<Pos>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum DirInfluence {
|
||||||
|
None,
|
||||||
|
One(Direction),
|
||||||
|
Multiple,
|
||||||
|
}
|
||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
pub fn new_empty() -> Self {
|
pub fn new_empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
grid: Grid::new_empty(5, 5),
|
grid: Grid::new_empty(5, 5),
|
||||||
marbles: Vec::new(),
|
marbles: Vec::new(),
|
||||||
powered: Vec::new(),
|
|
||||||
input: Vec::new(),
|
input: Vec::new(),
|
||||||
input_index: 0,
|
input_index: 0,
|
||||||
output: Vec::new(),
|
output: Vec::new(),
|
||||||
steps: 0,
|
steps: 0,
|
||||||
subtick_index: 0,
|
subtick_index: 0,
|
||||||
debug_subticks: Vec::new(),
|
debug_subticks: Vec::new(),
|
||||||
|
powered: Vec::new(),
|
||||||
|
new_marbles: Vec::new(),
|
||||||
|
influenced_direction: Vec::new(),
|
||||||
|
claim_positions: Vec::new(),
|
||||||
|
removed_marbles: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,9 +136,51 @@ impl Machine {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.step_power_complete();
|
||||||
|
|
||||||
|
if self.marbles.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.step_find_bounces();
|
||||||
|
self.step_apply_direct_bounces();
|
||||||
|
|
||||||
|
self.step_prepare_creating_marbles();
|
||||||
let old_marbles = self.marbles.len();
|
let old_marbles = self.marbles.len();
|
||||||
|
|
||||||
let mut new_marbles = Vec::new();
|
self.step_create_marbles();
|
||||||
|
|
||||||
|
// #### movement ####
|
||||||
|
// mark claims to figure out what spaces can be moved to
|
||||||
|
self.step_mark_claims(old_marbles);
|
||||||
|
self.step_move_marbles(old_marbles);
|
||||||
|
|
||||||
|
for pos in self.claim_positions.drain(..) {
|
||||||
|
if let Some(Tile::Open(_, claim_state)) = self.grid.get_mut(pos) {
|
||||||
|
*claim_state = Claim::Free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove marbles
|
||||||
|
for &i in self.removed_marbles.iter().rev() {
|
||||||
|
self.grid.set(self.marbles[i], Tile::BLANK);
|
||||||
|
self.marbles.swap_remove(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.step_propagate_power();
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
self.debug_subticks.push(DebugSubTick {
|
||||||
|
grid: self.grid.clone(),
|
||||||
|
pos: None,
|
||||||
|
});
|
||||||
|
self.subtick_index = self.debug_subticks.len() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
|
fn step_power_complete(&mut self) {
|
||||||
// activate all powered machines
|
// activate all powered machines
|
||||||
for &pos in &self.powered {
|
for &pos in &self.powered {
|
||||||
match self.grid.get_mut(pos) {
|
match self.grid.get_mut(pos) {
|
||||||
|
@ -157,7 +216,7 @@ impl Machine {
|
||||||
MathOp::Div => val_a.checked_div(val_b).unwrap_or_default(),
|
MathOp::Div => val_a.checked_div(val_b).unwrap_or_default(),
|
||||||
MathOp::Rem => val_a.checked_rem(val_b).unwrap_or_default(),
|
MathOp::Rem => val_a.checked_rem(val_b).unwrap_or_default(),
|
||||||
};
|
};
|
||||||
new_marbles.push((front_pos, value, dir));
|
self.new_marbles.push((front_pos, value, dir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PTile::IO => {
|
PTile::IO => {
|
||||||
|
@ -165,12 +224,12 @@ impl Machine {
|
||||||
{
|
{
|
||||||
let value = self.input[self.input_index] as MarbleValue;
|
let value = self.input[self.input_index] as MarbleValue;
|
||||||
self.input_index += 1;
|
self.input_index += 1;
|
||||||
new_marbles.push((front_pos, value, dir));
|
self.new_marbles.push((front_pos, value, dir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PTile::Silo => {
|
PTile::Silo => {
|
||||||
if front_tile == &Tile::BLANK {
|
if front_tile == &Tile::BLANK {
|
||||||
new_marbles.push((front_pos, 0, dir));
|
self.new_marbles.push((front_pos, 0, dir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PTile::Flipper => {
|
PTile::Flipper => {
|
||||||
|
@ -198,21 +257,13 @@ impl Machine {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.powered.clear();
|
self.powered.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if self.marbles.is_empty() {
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
return;
|
fn step_find_bounces(&mut self) {
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
enum DirInfluence {
|
|
||||||
None,
|
|
||||||
One(Direction),
|
|
||||||
Multiple,
|
|
||||||
}
|
|
||||||
// #### find all direct bounces ####
|
// #### find all direct bounces ####
|
||||||
let mut will_reverse_direction = vec![false; self.marbles.len()];
|
|
||||||
// todo store in tile to remove search through self.marbles
|
// todo store in tile to remove search through self.marbles
|
||||||
let mut influenced_direction = vec![DirInfluence::None; self.marbles.len()];
|
self.influenced_direction = vec![(false, DirInfluence::None); self.marbles.len()];
|
||||||
|
|
||||||
for (i, &pos) in self.marbles.iter().enumerate() {
|
for (i, &pos) in self.marbles.iter().enumerate() {
|
||||||
let Some(Tile::Marble { value: _, dir }) = self.grid.get(pos) else {
|
let Some(Tile::Marble { value: _, dir }) = self.grid.get(pos) else {
|
||||||
|
@ -229,11 +280,11 @@ impl Machine {
|
||||||
} => {
|
} => {
|
||||||
if other_dir != dir {
|
if other_dir != dir {
|
||||||
// this marble is facing another marble, and will therefore definitely bounce
|
// this marble is facing another marble, and will therefore definitely bounce
|
||||||
will_reverse_direction[i] = true;
|
self.influenced_direction[i].0 = true;
|
||||||
// the other marble will bounce too, either
|
// the other marble will bounce too, either
|
||||||
let other_index =
|
let other_index =
|
||||||
self.marbles.iter().position(|m| *m == front_pos).unwrap();
|
self.marbles.iter().position(|m| *m == front_pos).unwrap();
|
||||||
let influence = &mut influenced_direction[other_index];
|
let influence = &mut self.influenced_direction[other_index].1;
|
||||||
*influence = match *influence {
|
*influence = match *influence {
|
||||||
DirInfluence::None => DirInfluence::One(dir),
|
DirInfluence::None => DirInfluence::One(dir),
|
||||||
DirInfluence::One(_) => DirInfluence::Multiple,
|
DirInfluence::One(_) => DirInfluence::Multiple,
|
||||||
|
@ -244,38 +295,49 @@ impl Machine {
|
||||||
Tile::Arrow(arrow_dir) => {
|
Tile::Arrow(arrow_dir) => {
|
||||||
if arrow_dir == dir.opposite() {
|
if arrow_dir == dir.opposite() {
|
||||||
// bounce on a reverse facing arrow
|
// bounce on a reverse facing arrow
|
||||||
will_reverse_direction[i] = true;
|
self.influenced_direction[i].0 = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
|
fn step_apply_direct_bounces(&mut self) {
|
||||||
// #### apply all direct bounces ####
|
// #### apply all direct bounces ####
|
||||||
for (i, &pos) in self.marbles.iter().enumerate() {
|
for (i, &pos) in self.marbles.iter().enumerate() {
|
||||||
let Some(Tile::Marble { value: _, dir }) = self.grid.get_mut(pos) else {
|
let Some(Tile::Marble { value: _, dir }) = self.grid.get_mut(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
if will_reverse_direction[i] {
|
if self.influenced_direction[i].0 {
|
||||||
*dir = dir.opposite();
|
*dir = dir.opposite();
|
||||||
} else if let DirInfluence::One(new_dir) = influenced_direction[i] {
|
} else if let DirInfluence::One(new_dir) = self.influenced_direction[i].1 {
|
||||||
*dir = new_dir;
|
*dir = new_dir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
|
fn step_prepare_creating_marbles(&mut self) {
|
||||||
// #### new marbles ####
|
// #### new marbles ####
|
||||||
let mut claim_positions = Vec::new();
|
// self.claim_positions.clear(); // already drained
|
||||||
// prepare creating the new marbles
|
// prepare creating the new marbles
|
||||||
for &(pos, _val, _dir) in &new_marbles {
|
for &(pos, _val, _dir) in &self.new_marbles {
|
||||||
let Some(Tile::Open(OpenTile::Blank, claim)) = self.grid.get_mut(pos) else {
|
let Some(Tile::Open(OpenTile::Blank, claim)) = self.grid.get_mut(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
if claim.claim_indirect() {
|
if claim.claim_indirect() {
|
||||||
claim_positions.push(pos);
|
self.claim_positions.push(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
|
fn step_create_marbles(&mut self) {
|
||||||
// create new marbles
|
// create new marbles
|
||||||
// new marbles are past old_marbles index, so will not move this step
|
// new marbles are past old_marbles index, so will not move this step
|
||||||
for (pos, value, dir) in new_marbles {
|
for (pos, value, dir) in self.new_marbles.drain(..) {
|
||||||
let Some(Tile::Open(OpenTile::Blank, Claim::ClaimedIndirect)) = self.grid.get_mut(pos)
|
let Some(Tile::Open(OpenTile::Blank, Claim::ClaimedIndirect)) = self.grid.get_mut(pos)
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -283,9 +345,10 @@ impl Machine {
|
||||||
self.grid.set(pos, Tile::Marble { value, dir });
|
self.grid.set(pos, Tile::Marble { value, dir });
|
||||||
self.marbles.push(pos);
|
self.marbles.push(pos);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// #### movement ####
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
// mark claims to figure out what spaces can be moved to
|
fn step_mark_claims(&mut self, old_marbles: usize) {
|
||||||
for &pos in &self.marbles[..old_marbles] {
|
for &pos in &self.marbles[..old_marbles] {
|
||||||
let Some(Tile::Marble { value: _, dir }) = self.grid.get(pos) else {
|
let Some(Tile::Marble { value: _, dir }) = self.grid.get(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -296,7 +359,7 @@ impl Machine {
|
||||||
};
|
};
|
||||||
if let Tile::Open(_type, claim) = front_tile {
|
if let Tile::Open(_type, claim) = front_tile {
|
||||||
if claim.claim() {
|
if claim.claim() {
|
||||||
claim_positions.push(front_pos);
|
self.claim_positions.push(front_pos);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let target_pos = match front_tile {
|
let target_pos = match front_tile {
|
||||||
|
@ -310,13 +373,16 @@ impl Machine {
|
||||||
};
|
};
|
||||||
if let Tile::Open(_type, claim) = target_tile {
|
if let Tile::Open(_type, claim) = target_tile {
|
||||||
if claim.claim_indirect() {
|
if claim.claim_indirect() {
|
||||||
claim_positions.push(front_pos);
|
self.claim_positions.push(front_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut removed_marbles = Vec::new();
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
|
fn step_move_marbles(&mut self, old_marbles: usize) {
|
||||||
|
self.removed_marbles.clear();
|
||||||
// move marbles
|
// move marbles
|
||||||
for (i, pos) in self.marbles[..old_marbles].iter_mut().enumerate() {
|
for (i, pos) in self.marbles[..old_marbles].iter_mut().enumerate() {
|
||||||
let Some(Tile::Marble { value, dir }) = self.grid.get(*pos) else {
|
let Some(Tile::Marble { value, dir }) = self.grid.get(*pos) else {
|
||||||
|
@ -369,11 +435,11 @@ impl Machine {
|
||||||
target_pos = dir.step(front_pos);
|
target_pos = dir.step(front_pos);
|
||||||
}
|
}
|
||||||
Tile::Powerable(PTile::Silo, _) => {
|
Tile::Powerable(PTile::Silo, _) => {
|
||||||
removed_marbles.push(i);
|
self.removed_marbles.push(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Tile::Powerable(PTile::IO, _) => {
|
Tile::Powerable(PTile::IO, _) => {
|
||||||
removed_marbles.push(i);
|
self.removed_marbles.push(i);
|
||||||
self.output.push(value as u8);
|
self.output.push(value as u8);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -390,19 +456,10 @@ impl Machine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for pos in claim_positions {
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
if let Some(Tile::Open(_, claim_state)) = self.grid.get_mut(pos) {
|
fn step_propagate_power(&mut self) {
|
||||||
*claim_state = Claim::Free;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove marbles
|
|
||||||
for &i in removed_marbles.iter().rev() {
|
|
||||||
self.grid.set(self.marbles[i], Tile::BLANK);
|
|
||||||
self.marbles.swap_remove(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// propagate power
|
// propagate power
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < self.powered.len() {
|
while i < self.powered.len() {
|
||||||
|
@ -527,13 +584,5 @@ impl Machine {
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
{
|
|
||||||
self.debug_subticks.push(DebugSubTick {
|
|
||||||
grid: self.grid.clone(),
|
|
||||||
pos: None,
|
|
||||||
});
|
|
||||||
self.subtick_index = self.debug_subticks.len() - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue