diff --git a/CHANGELOG.md b/CHANGELOG.md index e3ad623..f91ab89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ Game store page: https://crispypin.itch.io/marble-machinations ## [unreleased] +### changed +- when multiple I/O silos (or multiple directions of one) are activated in the same tick, they will all output the same value instead of pulling input in an arbitrary order + ### fixed +- input bytes are consumed even if the marble can't be created because another one was taking its place - crash when saving config if no user dir exists - keybindings activated even when typing in a text field, making especially renaming blueprints difficult - after removing a binding that was a superset of another, the remaining one did not stop being blocked by the removed ones additional modifiers until another binding was added or edited diff --git a/README.md b/README.md index f2233e7..5114600 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ logic mostly like https://git.crispypin.cc/CrispyPin/marble - engine tests - blag post about marble movement logic? ### bugs -- input tiles still consume input when their marble creation is blocked - Shift+A and A+Shift conflict - rigt side grid rendering broken ### features diff --git a/src/marble_engine.rs b/src/marble_engine.rs index aee8eee..7bd40a3 100644 --- a/src/marble_engine.rs +++ b/src/marble_engine.rs @@ -21,7 +21,7 @@ pub struct Machine { // used across steps powered: Vec, // used within steps - new_marbles: Vec<(Pos, MarbleValue, Direction)>, + new_marbles: Vec<(Pos, MarbleValue, Direction, bool)>, influenced_direction: Vec<(bool, DirInfluence)>, claim_positions: Vec, removed_marbles: Vec, @@ -216,20 +216,19 @@ impl Machine { MathOp::Div => val_a.checked_div(val_b).unwrap_or_default(), MathOp::Rem => val_a.checked_rem(val_b).unwrap_or_default(), }; - self.new_marbles.push((front_pos, value, dir)); + self.new_marbles.push((front_pos, value, dir, false)); } } PTile::IO => { if front_tile == &Tile::BLANK && self.input_index < self.input.len() { let value = self.input[self.input_index] as MarbleValue; - self.input_index += 1; - self.new_marbles.push((front_pos, value, dir)); + self.new_marbles.push((front_pos, value, dir, true)); } } PTile::Silo => { if front_tile == &Tile::BLANK { - self.new_marbles.push((front_pos, 0, dir)); + self.new_marbles.push((front_pos, 0, dir, false)); } } PTile::Flipper => { @@ -321,9 +320,9 @@ impl Machine { #[cfg_attr(feature = "inline_less", inline(never), no_mangle)] fn step_prepare_creating_marbles(&mut self) { // #### new marbles #### - // self.claim_positions.clear(); // already drained - // prepare creating the new marbles - for &(pos, _val, _dir) in &self.new_marbles { + self.claim_positions.clear(); // already drained + // prepare creating the new marbles + for &(pos, _val, _dir, _is_input) in &self.new_marbles { let Some(Tile::Open(OpenTile::Blank, claim)) = self.grid.get_mut(pos) else { unreachable!() }; @@ -336,15 +335,20 @@ impl Machine { #[cfg_attr(feature = "inline_less", inline(never), no_mangle)] fn step_create_marbles(&mut self) { // create new marbles + let mut advance_input = false; // new marbles are past old_marbles index, so will not move this step - for (pos, value, dir) in self.new_marbles.drain(..) { + for (pos, value, dir, is_input) in self.new_marbles.drain(..) { let Some(Tile::Open(OpenTile::Blank, Claim::ClaimedIndirect)) = self.grid.get_mut(pos) else { continue; }; + advance_input |= is_input; self.grid.set(pos, Tile::Marble { value, dir }); self.marbles.push(pos); } + if advance_input { + self.input_index += 1; + } } #[cfg_attr(feature = "inline_less", inline(never), no_mangle)] diff --git a/tests/main.rs b/tests/main.rs index 1951b9f..1d194f2 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,8 +1,9 @@ use marble_machinations::marble_engine::{grid::Grid, Machine}; -fn no_input_test(steps: usize, output: &[u8], grid: &str) { +fn do_test(steps: usize, input: &[u8], output: &[u8], grid: &str) { let mut engine = Machine::new_empty(); engine.set_grid(Grid::from_ascii(grid)); + engine.set_input(input.to_owned()); for _ in 0..(steps - 1) { engine.step(); } @@ -11,11 +12,25 @@ fn no_input_test(steps: usize, output: &[u8], grid: &str) { assert_eq!(engine.output(), output, "expected output"); } +fn no_input_test(steps: usize, output: &[u8], grid: &str) { + do_test(steps, &[], output, grid) +} + #[test] fn creating_marbles_cause_indirect_claim() { no_input_test(3, &[1], " o \n|-*-|\n| 1 |\n-B B-\n I\n"); } +#[test] +fn bug_input_consumed_when_marble_creation_blocked() { + do_test( + 4, + &[1, 2, 3], + &[1, 1], + "# +-+\nIIo| I\n *+B\nII\n++*\n# #\n", + ); +} + #[test] fn bug_overlapping_marble_creation_blocks_tile_forever() { no_input_test(