fix input being consumed even when marble creation is blocked; made multiple simultaneous inputs use the same value

This commit is contained in:
Crispy 2025-04-20 20:35:22 +02:00
parent f0b878e93d
commit 80ab3b676e
4 changed files with 33 additions and 11 deletions

View file

@ -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

View file

@ -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

View file

@ -21,7 +21,7 @@ pub struct Machine {
// used across steps
powered: Vec<Pos>,
// used within steps
new_marbles: Vec<(Pos, MarbleValue, Direction)>,
new_marbles: Vec<(Pos, MarbleValue, Direction, bool)>,
influenced_direction: Vec<(bool, DirInfluence)>,
claim_positions: Vec<Pos>,
removed_marbles: Vec<usize>,
@ -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
self.claim_positions.clear(); // already drained
// prepare creating the new marbles
for &(pos, _val, _dir) in &self.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)]

View file

@ -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(