diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index e24355e..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -# Marble Machinations Change Log - -## [Unreleased] -- added (dev): sub-tick visualisation in debug mode -- fixed: equal comparator did not output one of two incoming signals in some cases, depending on wire length and update order -- changed: comparators can now power other tiles without a wire between, including other comparators -- changed: directly moving marbles (to adjactent tile without anything between) now have priority over new marbles being created, instead of the two events cancelling each other - -## v0.2.1 - 2025-03-14 -- added: "Simple comparison" level -- fixed: phantom marble (empty tile causing other marbles to bounce away) appearing after multiple machines tried to output to the same location at once - -## v0.2.0 - 2024-12-24 diff --git a/assets/debug_arrow_down.png b/assets/debug_arrow_down.png deleted file mode 100644 index 8c8b802..0000000 Binary files a/assets/debug_arrow_down.png and /dev/null differ diff --git a/assets/debug_arrow_left.png b/assets/debug_arrow_left.png deleted file mode 100644 index eec8a79..0000000 Binary files a/assets/debug_arrow_left.png and /dev/null differ diff --git a/assets/debug_arrow_right.png b/assets/debug_arrow_right.png deleted file mode 100644 index e1ea79c..0000000 Binary files a/assets/debug_arrow_right.png and /dev/null differ diff --git a/assets/debug_arrow_up.png b/assets/debug_arrow_up.png deleted file mode 100644 index 909b4b8..0000000 Binary files a/assets/debug_arrow_up.png and /dev/null differ diff --git a/src/editor.rs b/src/editor.rs index 644ffb4..548f201 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -536,14 +536,6 @@ impl Editor { self.rotate_tool(rl.is_key_down(KeyboardKey::KEY_LEFT_SHIFT)); } - if rl.is_key_pressed(KeyboardKey::KEY_N) { - self.machine.subtick_index += 1; - self.machine.subtick_index %= self.machine.debug_subticks.len(); - } - if rl.is_key_pressed(KeyboardKey::KEY_M) { - self.machine.subtick_index = self.machine.subtick_index.saturating_sub(1); - } - if self.sim_state == SimState::Editing { if rl.is_key_down(KeyboardKey::KEY_LEFT_CONTROL) { if rl.is_key_pressed(KeyboardKey::KEY_V) { @@ -565,20 +557,9 @@ impl Editor { self.source_board .draw(d, textures, self.view_offset, self.zoom); } else { - if self.machine.debug_subticks.is_empty() { - self.machine - .board() - .draw(d, textures, self.view_offset, self.zoom); - } else { - let subframe = &self.machine.debug_subticks[self.machine.subtick_index]; - subframe - .board - .draw(d, textures, self.view_offset, self.zoom); - if let Some(pos) = subframe.pos { - let p = self.pos_to_screen(pos.to_vec()); - d.draw_texture_ex(textures.get("selection"), p, 0., self.zoom, Color::ORANGE); - } - } + self.machine + .board() + .draw(d, textures, self.view_offset, self.zoom); if self.draw_overlay { self.machine .draw_marble_values(d, textures, self.view_offset, self.zoom); @@ -919,12 +900,6 @@ impl Editor { draw_usize(d, textures, self.step_time as usize, (260, 42), 9, 1); draw_usize(d, textures, self.max_step_time as usize, (260, 60), 9, 1); - #[cfg(debug_assertions)] - { - draw_usize(d, textures, self.machine.subtick_index, (260, 80), 9, 1); - let subtick_count = self.machine.debug_subticks.len(); - draw_usize(d, textures, subtick_count, (260, 100), 9, 1); - } d.draw_text("input:", 603, 8, 10, Color::WHITE); if simple_button((d, &self.mouse), 600, 20, 35, 15) { @@ -1341,14 +1316,11 @@ impl Editor { Tool::None | Tool::Erase | Tool::SelectArea(_) => (), Tool::SetTile(tile) => self.set_tile(pos, tile), Tool::Math => { - self.set_tile( - pos, - Tile::Powerable(PTile::Math(self.tool_math), Power::OFF), - ); + self.set_tile(pos, Tile::Powerable(PTile::Math(self.tool_math), false)); } Tool::Comparator => self.set_tile( pos, - Tile::Powerable(PTile::Comparator(self.tool_comparator), Power::OFF), + Tile::Powerable(PTile::Comparator(self.tool_comparator), false), ), Tool::Wire => self.set_tile(pos, Tile::Wire(self.tool_wire, false)), Tool::Arrow => self.set_tile(pos, Tile::Arrow(self.tool_arrow)), diff --git a/src/marble_engine.rs b/src/marble_engine.rs index 36ebf83..bcaf731 100644 --- a/src/marble_engine.rs +++ b/src/marble_engine.rs @@ -17,14 +17,6 @@ pub struct Machine { input_index: usize, output: Vec, steps: usize, - pub subtick_index: usize, - pub debug_subticks: Vec, -} - -#[derive(Debug)] -pub struct DebugSubTick { - pub board: Board, - pub pos: Option, } impl Machine { @@ -37,8 +29,6 @@ impl Machine { input_index: 0, output: Vec::new(), steps: 0, - subtick_index: 0, - debug_subticks: Vec::new(), } } @@ -47,8 +37,6 @@ impl Machine { self.input_index = 0; self.output.clear(); self.powered.clear(); - self.debug_subticks.clear(); - self.subtick_index = 0; } pub fn set_board(&mut self, board: Board) { @@ -109,15 +97,6 @@ impl Machine { pub fn step(&mut self) { self.steps += 1; - #[cfg(debug_assertions)] - { - self.subtick_index = 0; - self.debug_subticks.clear(); - self.debug_subticks.push(DebugSubTick { - board: self.board.clone(), - pos: None, - }); - } let old_marbles = self.marbles.len(); @@ -125,24 +104,27 @@ impl Machine { // activate all powered machines for &pos in &self.powered { match self.board.get_mut(pos) { - Some(Tile::Powerable(PTile::Comparator(_), board_power_state)) => { - // already handled at the power propagation stage (end of sim step) - *board_power_state = Power::OFF; - } - Some(Tile::Powerable(machine, board_power_state)) => { - let state = *board_power_state; - *board_power_state = Power::OFF; + Some(Tile::Powerable(machine, state)) => { + *state = false; let machine = *machine; for dir in Direction::ALL { - if !state.get_dir(dir) { - continue; - } let front_pos = dir.step(pos); + let source_pos = dir.opposite().step(pos); + match self.board.get(source_pos) { + Some(Tile::Wire(wiretype, true)) => { + if !wiretype.has_output(dir) { + continue; + } + } + Some(Tile::Button(true)) => (), + _ => continue, + } let Some(front_tile) = self.board.get_mut(front_pos) else { continue; }; - // `machine` is being powered, in direction `dir` + // `machine`` is being powered, in direction `dir`` match machine { + PTile::Comparator(_) => (), // handled at the power propagation stage (end of step) PTile::Math(op) => { if front_tile.is_blank() { let pos_a = dir.left().step(pos); @@ -187,16 +169,24 @@ impl Machine { _ => (), }; } - PTile::Comparator(_) => unreachable!(), } } } - Some(Tile::Button(state) | Tile::Wire(_, state)) => { - *state = false; - } - _ => unreachable!("non-powerable tile at {pos:?} in self.powered"), + Some(Tile::Button(_state)) => (), + Some(Tile::Wire(_, _state)) => (), + _ => unreachable!(), }; } + + // old wires have to be reset after machine processing, + // so they can figure out which directions they are powered from + for &p in &self.powered { + match self.board.get_mut(p) { + Some(Tile::Button(state)) => *state = false, + Some(Tile::Wire(_, state)) => *state = false, + _ => (), + } + } self.powered.clear(); if self.marbles.is_empty() { @@ -209,6 +199,7 @@ impl Machine { One(Direction), Multiple, } + // #### find all direct bounces #### let mut will_reverse_direction = vec![false; self.marbles.len()]; // todo store in tile to remove search through self.marbles @@ -417,8 +408,8 @@ impl Machine { let target_pos = dir.step(pos); match self.board.get_mut(target_pos) { Some(Tile::Powerable(_, state)) => { - if !state.get_dir(dir) { - state.add_dir(dir); + if !*state { + *state = true; self.powered.push(target_pos); } } @@ -432,11 +423,6 @@ impl Machine { _ => (), } } - #[cfg(debug_assertions)] - self.debug_subticks.push(DebugSubTick { - board: self.board.clone(), - pos: Some(pos), - }); } Tile::Wire(wiretype, state) => { *state = true; @@ -444,8 +430,8 @@ impl Machine { let target_pos = dir.step(pos); match self.board.get_mut(target_pos) { Some(Tile::Powerable(_, state)) => { - if !state.get_dir(*dir) { - state.add_dir(*dir); + if !*state { + *state = true; self.powered.push(target_pos); } } @@ -459,20 +445,22 @@ impl Machine { _ => (), } } - #[cfg(debug_assertions)] - self.debug_subticks.push(DebugSubTick { - board: self.board.clone(), - pos: Some(pos), - }); } Tile::Powerable(PTile::Comparator(comp), state) => { + *state = true; let comp = *comp; - let state = *state; for dir in Direction::ALL { - if !state.get_dir(dir) { - continue; - } let front_pos = dir.step(pos); + let source_pos = dir.opposite().step(pos); + match self.board.get(source_pos) { + Some(Tile::Wire(wiretype, true)) => { + if !wiretype.has_output(dir) { + continue; + } + } + Some(Tile::Button(true)) => (), + _ => continue, + } let Some(front_tile) = self.board.get_mut(front_pos) else { continue; }; @@ -489,37 +477,13 @@ impl Machine { Comparison::NotEqual => val_a != val_b, }; if result { - match self.board.get_mut(front_pos) { - Some(Tile::Powerable(_, state)) => { - if !state.get_dir(dir) { - state.add_dir(dir); - self.powered.push(front_pos); - } - } - Some(Tile::Wire(_, state)) => { - if !*state { - *state = true; - self.powered.push(front_pos); - } - } - _ => (), - } + self.powered.push(front_pos); } } } - #[cfg(debug_assertions)] - self.debug_subticks.push(DebugSubTick { - board: self.board.clone(), - pos: Some(pos), - }); - } - Tile::Powerable(_, _state) => { - #[cfg(debug_assertions)] - self.debug_subticks.push(DebugSubTick { - board: self.board.clone(), - pos: Some(pos), - }); } + // state may be false if it was powered by a machine in earlier step + Tile::Powerable(_, state) => *state = true, _ => { dbg!(tile); unreachable!() @@ -527,9 +491,5 @@ impl Machine { } i += 1; } - #[cfg(debug_assertions)] - { - self.subtick_index = self.debug_subticks.len() - 1; - } } } diff --git a/src/marble_engine/board.rs b/src/marble_engine/board.rs index 86f153e..e6d008f 100644 --- a/src/marble_engine/board.rs +++ b/src/marble_engine/board.rs @@ -214,16 +214,6 @@ impl Board { } let texture = textures.get(texname); draw_scaled_texture(d, texture, px, py, scale); - #[cfg(debug_assertions)] - // TODO: some in-game option to show power direction - if let Tile::Powerable(_, state) = &tile { - for dir in Direction::ALL { - if state.get_dir(dir) { - let texture = textures.get(dir.debug_arrow_texture_name()); - draw_scaled_texture(d, texture, px, py, scale); - } - } - } } else { d.draw_rectangle(px, py, tile_size, tile_size, Color::new(0, 0, 0, 80)); } diff --git a/src/marble_engine/tile.rs b/src/marble_engine/tile.rs index 798c51c..568411d 100644 --- a/src/marble_engine/tile.rs +++ b/src/marble_engine/tile.rs @@ -11,7 +11,7 @@ pub enum Tile { Arrow(Direction), Button(bool), Wire(WireType, bool), - Powerable(PTile, Power), + Powerable(PTile, bool), } #[derive(Debug, Clone, Copy, PartialEq)] @@ -38,11 +38,6 @@ pub enum PTile { IO, } -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Power { - directions: u8, -} - #[derive(Debug, Clone, Copy, PartialEq)] pub enum MirrorType { Forward, @@ -99,18 +94,18 @@ impl Tile { 'v' => Tile::Arrow(Direction::Down), '<' => Tile::Arrow(Direction::Left), '>' => Tile::Arrow(Direction::Right), - '=' => Tile::Powerable(PTile::Comparator(Comparison::Equal), Power::OFF), - '!' => Tile::Powerable(PTile::Comparator(Comparison::NotEqual), Power::OFF), - 'L' => Tile::Powerable(PTile::Comparator(Comparison::LessThan), Power::OFF), - 'G' => Tile::Powerable(PTile::Comparator(Comparison::GreaterThan), Power::OFF), - 'I' | 'P' => Tile::Powerable(PTile::IO, Power::OFF), - 'F' => Tile::Powerable(PTile::Flipper, Power::OFF), - 'A' => Tile::Powerable(PTile::Math(MathOp::Add), Power::OFF), - 'S' => Tile::Powerable(PTile::Math(MathOp::Sub), Power::OFF), - 'M' => Tile::Powerable(PTile::Math(MathOp::Mul), Power::OFF), - 'D' => Tile::Powerable(PTile::Math(MathOp::Div), Power::OFF), - 'R' => Tile::Powerable(PTile::Math(MathOp::Rem), Power::OFF), - 'B' => Tile::Powerable(PTile::Silo, Power::OFF), + '=' => Tile::Powerable(PTile::Comparator(Comparison::Equal), false), + '!' => Tile::Powerable(PTile::Comparator(Comparison::NotEqual), false), + 'L' => Tile::Powerable(PTile::Comparator(Comparison::LessThan), false), + 'G' => Tile::Powerable(PTile::Comparator(Comparison::GreaterThan), false), + 'I' | 'P' => Tile::Powerable(PTile::IO, false), + 'F' => Tile::Powerable(PTile::Flipper, false), + 'A' => Tile::Powerable(PTile::Math(MathOp::Add), false), + 'S' => Tile::Powerable(PTile::Math(MathOp::Sub), false), + 'M' => Tile::Powerable(PTile::Math(MathOp::Mul), false), + 'D' => Tile::Powerable(PTile::Math(MathOp::Div), false), + 'R' => Tile::Powerable(PTile::Math(MathOp::Rem), false), + 'B' => Tile::Powerable(PTile::Silo, false), d @ '0'..='9' => Tile::Open(OpenTile::Digit(d as u8 - b'0'), Claim::Free), '#' => Tile::Block, _ => Tile::Open(OpenTile::Blank, Claim::Free), @@ -205,7 +200,7 @@ impl Tile { wire.texture_name_off() } Tile::Powerable(tile, state) => { - if state.any() { + if state { return match tile { PTile::Comparator(comp) => comp.texture_name_on(), PTile::Math(math_op) => math_op.texture_name_on(), @@ -275,15 +270,6 @@ impl Direction { } } - pub const fn debug_arrow_texture_name(self) -> &'static str { - match self { - Direction::Up => "debug_arrow_up", - Direction::Down => "debug_arrow_down", - Direction::Left => "debug_arrow_left", - Direction::Right => "debug_arrow_right", - } - } - pub const fn arrow_tile_human_name(self) -> &'static str { match self { Direction::Up => "Up Arrow", @@ -531,22 +517,3 @@ impl Claim { was_free } } - -impl Power { - pub const OFF: Self = Self { directions: 0 }; - - #[inline] - pub fn any(self) -> bool { - self.directions != 0 - } - - #[inline] - pub fn get_dir(self, dir: Direction) -> bool { - self.directions & (1 << (dir as u8)) != 0 - } - - #[inline] - pub fn add_dir(&mut self, dir: Direction) { - self.directions |= 1 << (dir as u8) - } -}