fix input being consumed even when marble creation is blocked; made multiple simultaneous inputs use the same value
This commit is contained in:
parent
f0b878e93d
commit
80ab3b676e
4 changed files with 33 additions and 11 deletions
|
@ -2,7 +2,11 @@
|
||||||
Game store page: https://crispypin.itch.io/marble-machinations
|
Game store page: https://crispypin.itch.io/marble-machinations
|
||||||
|
|
||||||
## [unreleased]
|
## [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
|
### 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
|
- crash when saving config if no user dir exists
|
||||||
- keybindings activated even when typing in a text field, making especially renaming blueprints difficult
|
- 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
|
- 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
|
||||||
|
|
|
@ -9,7 +9,6 @@ logic mostly like https://git.crispypin.cc/CrispyPin/marble
|
||||||
- engine tests
|
- engine tests
|
||||||
- blag post about marble movement logic?
|
- blag post about marble movement logic?
|
||||||
### bugs
|
### bugs
|
||||||
- input tiles still consume input when their marble creation is blocked
|
|
||||||
- Shift+A and A+Shift conflict
|
- Shift+A and A+Shift conflict
|
||||||
- rigt side grid rendering broken
|
- rigt side grid rendering broken
|
||||||
### features
|
### features
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub struct Machine {
|
||||||
// used across steps
|
// used across steps
|
||||||
powered: Vec<Pos>,
|
powered: Vec<Pos>,
|
||||||
// used within steps
|
// used within steps
|
||||||
new_marbles: Vec<(Pos, MarbleValue, Direction)>,
|
new_marbles: Vec<(Pos, MarbleValue, Direction, bool)>,
|
||||||
influenced_direction: Vec<(bool, DirInfluence)>,
|
influenced_direction: Vec<(bool, DirInfluence)>,
|
||||||
claim_positions: Vec<Pos>,
|
claim_positions: Vec<Pos>,
|
||||||
removed_marbles: Vec<usize>,
|
removed_marbles: Vec<usize>,
|
||||||
|
@ -216,20 +216,19 @@ 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(),
|
||||||
};
|
};
|
||||||
self.new_marbles.push((front_pos, value, dir));
|
self.new_marbles.push((front_pos, value, dir, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PTile::IO => {
|
PTile::IO => {
|
||||||
if front_tile == &Tile::BLANK && self.input_index < self.input.len()
|
if front_tile == &Tile::BLANK && self.input_index < self.input.len()
|
||||||
{
|
{
|
||||||
let value = self.input[self.input_index] as MarbleValue;
|
let value = self.input[self.input_index] as MarbleValue;
|
||||||
self.input_index += 1;
|
self.new_marbles.push((front_pos, value, dir, true));
|
||||||
self.new_marbles.push((front_pos, value, dir));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PTile::Silo => {
|
PTile::Silo => {
|
||||||
if front_tile == &Tile::BLANK {
|
if front_tile == &Tile::BLANK {
|
||||||
self.new_marbles.push((front_pos, 0, dir));
|
self.new_marbles.push((front_pos, 0, dir, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PTile::Flipper => {
|
PTile::Flipper => {
|
||||||
|
@ -321,9 +320,9 @@ impl Machine {
|
||||||
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
fn step_prepare_creating_marbles(&mut self) {
|
fn step_prepare_creating_marbles(&mut self) {
|
||||||
// #### new marbles ####
|
// #### new marbles ####
|
||||||
// self.claim_positions.clear(); // already drained
|
self.claim_positions.clear(); // already drained
|
||||||
// prepare creating the new marbles
|
// 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 {
|
let Some(Tile::Open(OpenTile::Blank, claim)) = self.grid.get_mut(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
@ -336,15 +335,20 @@ impl Machine {
|
||||||
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
fn step_create_marbles(&mut self) {
|
fn step_create_marbles(&mut self) {
|
||||||
// create new marbles
|
// create new marbles
|
||||||
|
let mut advance_input = false;
|
||||||
// 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 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)
|
let Some(Tile::Open(OpenTile::Blank, Claim::ClaimedIndirect)) = self.grid.get_mut(pos)
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
advance_input |= is_input;
|
||||||
self.grid.set(pos, Tile::Marble { value, dir });
|
self.grid.set(pos, Tile::Marble { value, dir });
|
||||||
self.marbles.push(pos);
|
self.marbles.push(pos);
|
||||||
}
|
}
|
||||||
|
if advance_input {
|
||||||
|
self.input_index += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
#[cfg_attr(feature = "inline_less", inline(never), no_mangle)]
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use marble_machinations::marble_engine::{grid::Grid, Machine};
|
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();
|
let mut engine = Machine::new_empty();
|
||||||
engine.set_grid(Grid::from_ascii(grid));
|
engine.set_grid(Grid::from_ascii(grid));
|
||||||
|
engine.set_input(input.to_owned());
|
||||||
for _ in 0..(steps - 1) {
|
for _ in 0..(steps - 1) {
|
||||||
engine.step();
|
engine.step();
|
||||||
}
|
}
|
||||||
|
@ -11,11 +12,25 @@ fn no_input_test(steps: usize, output: &[u8], grid: &str) {
|
||||||
assert_eq!(engine.output(), output, "expected output");
|
assert_eq!(engine.output(), output, "expected output");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn no_input_test(steps: usize, output: &[u8], grid: &str) {
|
||||||
|
do_test(steps, &[], output, grid)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn creating_marbles_cause_indirect_claim() {
|
fn creating_marbles_cause_indirect_claim() {
|
||||||
no_input_test(3, &[1], " o \n|-*-|\n| 1 |\n-B B-\n I\n");
|
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]
|
#[test]
|
||||||
fn bug_overlapping_marble_creation_blocks_tile_forever() {
|
fn bug_overlapping_marble_creation_blocks_tile_forever() {
|
||||||
no_input_test(
|
no_input_test(
|
||||||
|
|
Loading…
Add table
Reference in a new issue