rewrite power propagation
This commit is contained in:
parent
e6f99d3776
commit
e223a75c0e
5 changed files with 263 additions and 184 deletions
Binary file not shown.
Before Width: | Height: | Size: 98 B After Width: | Height: | Size: 92 B |
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
marble_engine::{
|
marble_engine::{
|
||||||
board::Board,
|
board::Board,
|
||||||
pos::{Pos, PosInt},
|
pos::{Pos, PosInt},
|
||||||
tile::{Direction, GateType, MarbleTarget, MathOp, MirrorType, OpenTile, PTile, Tile, WireType},
|
tile::{Claim, Direction, GateType, MathOp, MirrorType, OpenTile, PTile, Tile, WireType},
|
||||||
Machine,
|
Machine,
|
||||||
},
|
},
|
||||||
simple_button, simple_option_button, slider,
|
simple_button, simple_option_button, slider,
|
||||||
|
@ -179,7 +179,7 @@ impl Editor {
|
||||||
match self.sim_state {
|
match self.sim_state {
|
||||||
SimState::Editing => {
|
SimState::Editing => {
|
||||||
self.start_sim();
|
self.start_sim();
|
||||||
self.step();
|
// self.step();
|
||||||
}
|
}
|
||||||
SimState::Running => (),
|
SimState::Running => (),
|
||||||
SimState::Stepping => self.step(),
|
SimState::Stepping => self.step(),
|
||||||
|
@ -773,7 +773,7 @@ impl Editor {
|
||||||
tool_button((1, 0), "marble", Tool::SetTile(Tile::from_char('o')));
|
tool_button((1, 0), "marble", Tool::SetTile(Tile::from_char('o')));
|
||||||
match tool_button(
|
match tool_button(
|
||||||
(1, 1),
|
(1, 1),
|
||||||
&Tile::Powerable(PTile::Wire(self.tool_wire), false).texture(),
|
&Tile::Wire(self.tool_wire, false).texture(),
|
||||||
Tool::Wire,
|
Tool::Wire,
|
||||||
) {
|
) {
|
||||||
Some(Scroll::Down) => self.tool_wire.next(),
|
Some(Scroll::Down) => self.tool_wire.next(),
|
||||||
|
@ -935,7 +935,7 @@ impl Editor {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
for n in 0..10 {
|
for n in 0..10 {
|
||||||
if d.is_key_pressed(unsafe { transmute::<u32, KeyboardKey>(b'0' as u32 + n) }) {
|
if d.is_key_pressed(unsafe { transmute::<u32, KeyboardKey>(b'0' as u32 + n) }) {
|
||||||
self.set_tile(pos, Tile::Open(OpenTile::Digit(n as u8), MarbleTarget::Free));
|
self.set_tile(pos, Tile::Open(OpenTile::Digit(n as u8), Claim::Free));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -986,16 +986,14 @@ impl Editor {
|
||||||
Tool::Gate => {
|
Tool::Gate => {
|
||||||
self.set_tile(pos, Tile::Powerable(PTile::Gate(self.tool_gate), false))
|
self.set_tile(pos, Tile::Powerable(PTile::Gate(self.tool_gate), false))
|
||||||
}
|
}
|
||||||
Tool::Wire => {
|
Tool::Wire => self.set_tile(pos, Tile::Wire(self.tool_wire, false)),
|
||||||
self.set_tile(pos, Tile::Powerable(PTile::Wire(self.tool_wire), false))
|
|
||||||
}
|
|
||||||
Tool::Arrow => self.set_tile(pos, Tile::Arrow(self.tool_arrow)),
|
Tool::Arrow => self.set_tile(pos, Tile::Arrow(self.tool_arrow)),
|
||||||
Tool::Mirror => self.set_tile(pos, Tile::Mirror(self.tool_mirror)),
|
Tool::Mirror => self.set_tile(pos, Tile::Mirror(self.tool_mirror)),
|
||||||
Tool::Digits(_pos) => {
|
Tool::Digits(_pos) => {
|
||||||
self.active_tool = Tool::Digits(Some(pos));
|
self.active_tool = Tool::Digits(Some(pos));
|
||||||
if let Some(tile) = self.source_board.get_mut(pos) {
|
if let Some(tile) = self.source_board.get_mut(pos) {
|
||||||
if ! matches!(tile, Tile::Open(OpenTile::Digit(_), _)) {
|
if !matches!(tile, Tile::Open(OpenTile::Digit(_), _)) {
|
||||||
*tile = Tile::Open(OpenTile::Digit(0), MarbleTarget::Free);
|
*tile = Tile::Open(OpenTile::Digit(0), Claim::Free);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,6 @@ pub struct Machine {
|
||||||
board: Board,
|
board: Board,
|
||||||
marbles: Vec<Pos>,
|
marbles: Vec<Pos>,
|
||||||
powered: Vec<Pos>,
|
powered: Vec<Pos>,
|
||||||
flipper_events: Vec<Pos>,
|
|
||||||
|
|
||||||
input: Vec<u8>,
|
input: Vec<u8>,
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
output: Vec<u8>,
|
output: Vec<u8>,
|
||||||
|
@ -28,7 +26,6 @@ impl Machine {
|
||||||
board: Board::new_empty(width, width),
|
board: Board::new_empty(width, width),
|
||||||
marbles: Vec::new(),
|
marbles: Vec::new(),
|
||||||
powered: Vec::new(),
|
powered: Vec::new(),
|
||||||
flipper_events: Vec::new(),
|
|
||||||
input,
|
input,
|
||||||
input_index: 0,
|
input_index: 0,
|
||||||
output: Vec::new(),
|
output: Vec::new(),
|
||||||
|
@ -102,12 +99,115 @@ impl Machine {
|
||||||
|
|
||||||
pub fn step(&mut self) {
|
pub fn step(&mut self) {
|
||||||
self.steps += 1;
|
self.steps += 1;
|
||||||
// reset wires
|
|
||||||
for &p in &self.powered {
|
let old_marbles = self.marbles.len();
|
||||||
let Some(Tile::Powerable(_, state)) = self.board.get_mut(p) else {
|
|
||||||
|
let mut new_marbles = Vec::new();
|
||||||
|
// activate all powered machines
|
||||||
|
for &pos in &self.powered {
|
||||||
|
match self.board.get_mut(pos) {
|
||||||
|
Some(Tile::Powerable(machine, state)) => {
|
||||||
|
*state = false;
|
||||||
|
let machine = *machine;
|
||||||
|
for dir in Direction::ALL {
|
||||||
|
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::Trigger(true)) => (),
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
let Some(front_tile) = self.board.get_mut(front_pos) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
// `machine`` is being powered, in direction `dir``
|
||||||
|
match machine {
|
||||||
|
PTile::Gate(_) => (), // handled at the power propagation stage (end of step)
|
||||||
|
PTile::Math(op) => {
|
||||||
|
if front_tile.is_blank() {
|
||||||
|
let pos_a = dir.left().step(pos);
|
||||||
|
let pos_b = dir.right().step(pos);
|
||||||
|
let val_a = self.board.get_or_blank(pos_a).read_value();
|
||||||
|
let val_b = self.board.get_or_blank(pos_b).read_value();
|
||||||
|
|
||||||
|
let value = match op {
|
||||||
|
MathOp::Add => val_a.wrapping_add(val_b),
|
||||||
|
MathOp::Sub => val_a.wrapping_sub(val_b),
|
||||||
|
MathOp::Mul => val_a.wrapping_mul(val_b),
|
||||||
|
MathOp::Div => val_a.checked_div(val_b).unwrap_or_default(),
|
||||||
|
MathOp::Rem => val_a.checked_rem(val_b).unwrap_or_default(),
|
||||||
|
};
|
||||||
|
new_marbles.push((front_pos, value, dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PTile::IO => {
|
||||||
|
if front_tile == &Tile::default()
|
||||||
|
&& self.input_index < self.input.len()
|
||||||
|
{
|
||||||
|
let value = self.input[self.input_index] as MarbleValue;
|
||||||
|
self.input_index += 1;
|
||||||
|
new_marbles.push((front_pos, value, dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PTile::Bag => {
|
||||||
|
if front_tile == &Tile::default() {
|
||||||
|
new_marbles.push((front_pos, 0, dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PTile::Flipper => {
|
||||||
|
match front_tile {
|
||||||
|
Tile::Wire(wire_type, _) => {
|
||||||
|
*wire_type = match *wire_type {
|
||||||
|
WireType::Vertical => WireType::Horizontal,
|
||||||
|
WireType::Horizontal => WireType::Vertical,
|
||||||
|
WireType::Cross => WireType::Cross,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tile::Mirror(mirror) => mirror.flip(),
|
||||||
|
Tile::Arrow(dir) => *dir = dir.opposite(),
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Tile::Trigger(_state)) => (),
|
||||||
|
Some(Tile::Wire(_, _state)) => (),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for &(pos, _val, _dir) in &new_marbles {
|
||||||
|
let Some(Tile::Open(OpenTile::Blank, claim)) = self.board.get_mut(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
*state = false;
|
*claim = match claim {
|
||||||
|
Claim::Free => Claim::Claimed,
|
||||||
|
Claim::Claimed | Claim::Blocked => Claim::Blocked,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// new marbles are past old_marbles index, so will not move this step
|
||||||
|
for (pos, value, dir) in new_marbles {
|
||||||
|
let Some(Tile::Open(OpenTile::Blank, Claim::Claimed)) = self.board.get_mut(pos) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
self.board.set(pos, Tile::Marble { value, dir });
|
||||||
|
self.marbles.push(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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::Trigger(state)) => *state = false,
|
||||||
|
Some(Tile::Wire(_, state)) => *state = false,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.powered.clear();
|
self.powered.clear();
|
||||||
|
|
||||||
|
@ -170,16 +270,14 @@ impl Machine {
|
||||||
};
|
};
|
||||||
if will_reverse_direction[i] {
|
if will_reverse_direction[i] {
|
||||||
*dir = dir.opposite();
|
*dir = dir.opposite();
|
||||||
} else {
|
} else if let DirInfluence::One(new_dir) = influenced_direction[i] {
|
||||||
if let DirInfluence::One(new_dir) = influenced_direction[i] {
|
|
||||||
*dir = new_dir;
|
*dir = new_dir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut claim_positions = Vec::new();
|
let mut claim_positions = Vec::new();
|
||||||
// mark claims to figure out what spaces can be moved to
|
// mark claims to figure out what spaces can be moved to
|
||||||
for &pos in &self.marbles {
|
for &pos in &self.marbles[..old_marbles] {
|
||||||
let Some(Tile::Marble { value: _, dir }) = self.board.get(pos) else {
|
let Some(Tile::Marble { value: _, dir }) = self.board.get(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
@ -189,51 +287,44 @@ impl Machine {
|
||||||
};
|
};
|
||||||
if let Tile::Open(_type, claim) = front_tile {
|
if let Tile::Open(_type, claim) = front_tile {
|
||||||
*claim = match claim {
|
*claim = match claim {
|
||||||
MarbleTarget::Free => {
|
Claim::Free => {
|
||||||
claim_positions.push(front_pos);
|
claim_positions.push(front_pos);
|
||||||
MarbleTarget::Claimed
|
Claim::Claimed
|
||||||
}
|
}
|
||||||
MarbleTarget::ClaimedIndirect => MarbleTarget::Claimed,
|
Claim::ClaimedIndirect => Claim::Claimed,
|
||||||
MarbleTarget::BlockedIndirect => MarbleTarget::Claimed,
|
Claim::BlockedIndirect => Claim::Claimed,
|
||||||
MarbleTarget::Claimed => MarbleTarget::Blocked,
|
Claim::Claimed => Claim::Blocked,
|
||||||
MarbleTarget::Blocked => MarbleTarget::Blocked,
|
Claim::Blocked => Claim::Blocked,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
let target_pos;
|
let target_pos = match front_tile {
|
||||||
match front_tile {
|
Tile::Arrow(d) => d.step(front_pos),
|
||||||
Tile::Arrow(d) => {
|
Tile::Mirror(m) => m.new_dir(dir).step(front_pos),
|
||||||
target_pos = d.step(front_pos);
|
Tile::Trigger(_) => dir.step(front_pos),
|
||||||
}
|
|
||||||
Tile::Mirror(m) => {
|
|
||||||
target_pos = m.new_dir(dir).step(front_pos);
|
|
||||||
}
|
|
||||||
Tile::Powerable(PTile::Trigger, _) => {
|
|
||||||
target_pos = dir.step(front_pos);
|
|
||||||
}
|
|
||||||
_ => continue,
|
_ => continue,
|
||||||
}
|
};
|
||||||
let Some(target_tile) = self.board.get_mut(target_pos) else {
|
let Some(target_tile) = self.board.get_mut(target_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if let Tile::Open(_type, claim) = target_tile {
|
if let Tile::Open(_type, claim) = target_tile {
|
||||||
*claim = match claim {
|
*claim = match claim {
|
||||||
MarbleTarget::Free => {
|
Claim::Free => {
|
||||||
claim_positions.push(front_pos);
|
claim_positions.push(front_pos);
|
||||||
MarbleTarget::ClaimedIndirect
|
Claim::ClaimedIndirect
|
||||||
}
|
}
|
||||||
MarbleTarget::ClaimedIndirect => MarbleTarget::BlockedIndirect,
|
Claim::ClaimedIndirect => Claim::BlockedIndirect,
|
||||||
MarbleTarget::BlockedIndirect => MarbleTarget::BlockedIndirect,
|
Claim::BlockedIndirect => Claim::BlockedIndirect,
|
||||||
MarbleTarget::Claimed => MarbleTarget::Claimed,
|
Claim::Claimed => Claim::Claimed,
|
||||||
MarbleTarget::Blocked => MarbleTarget::Blocked,
|
Claim::Blocked => Claim::Blocked,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut triggers_activated = Vec::new();
|
// let mut triggers_activated = Vec::new();
|
||||||
let mut removed_marbles = Vec::new();
|
let mut removed_marbles = Vec::new();
|
||||||
// move marbles
|
// move marbles
|
||||||
for (i, pos) in self.marbles.iter_mut().enumerate() {
|
for (i, pos) in self.marbles[..old_marbles].iter_mut().enumerate() {
|
||||||
let Some(Tile::Marble { value, dir }) = self.board.get(*pos) else {
|
let Some(Tile::Marble { value, dir }) = self.board.get(*pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
@ -253,9 +344,9 @@ impl Machine {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Tile::Open(space_type, claim_state) = front_tile {
|
if let Tile::Open(space_type, claim_state) = front_tile {
|
||||||
if *claim_state == MarbleTarget::Claimed {
|
if *claim_state == Claim::Claimed {
|
||||||
move_to(*space_type, front_pos, dir, &mut self.board);
|
move_to(*space_type, front_pos, dir, &mut self.board);
|
||||||
} else if *claim_state != MarbleTarget::Free {
|
} else if *claim_state != Claim::Free {
|
||||||
// (Free means a marble was just here but moved earlier this tick)
|
// (Free means a marble was just here but moved earlier this tick)
|
||||||
// bounce on failed direct movement
|
// bounce on failed direct movement
|
||||||
self.board.set(
|
self.board.set(
|
||||||
|
@ -279,7 +370,7 @@ impl Machine {
|
||||||
new_dir = m.new_dir(dir);
|
new_dir = m.new_dir(dir);
|
||||||
target_pos = new_dir.step(front_pos);
|
target_pos = new_dir.step(front_pos);
|
||||||
}
|
}
|
||||||
Tile::Powerable(PTile::Trigger, _) => {
|
Tile::Trigger(_) => {
|
||||||
is_trigger = true;
|
is_trigger = true;
|
||||||
target_pos = dir.step(front_pos);
|
target_pos = dir.step(front_pos);
|
||||||
}
|
}
|
||||||
|
@ -297,10 +388,10 @@ impl Machine {
|
||||||
let Some(target_tile) = self.board.get_mut(target_pos) else {
|
let Some(target_tile) = self.board.get_mut(target_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if let Tile::Open(space_type, MarbleTarget::ClaimedIndirect) = target_tile {
|
if let Tile::Open(space_type, Claim::ClaimedIndirect) = target_tile {
|
||||||
move_to(*space_type, target_pos, new_dir, &mut self.board);
|
move_to(*space_type, target_pos, new_dir, &mut self.board);
|
||||||
if is_trigger {
|
if is_trigger {
|
||||||
triggers_activated.push(front_pos);
|
self.powered.push(front_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,7 +399,7 @@ impl Machine {
|
||||||
|
|
||||||
for pos in claim_positions {
|
for pos in claim_positions {
|
||||||
if let Some(Tile::Open(_, claim_state)) = self.board.get_mut(pos) {
|
if let Some(Tile::Open(_, claim_state)) = self.board.get_mut(pos) {
|
||||||
*claim_state = MarbleTarget::Free;
|
*claim_state = Claim::Free;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,105 +409,77 @@ impl Machine {
|
||||||
self.marbles.swap_remove(i);
|
self.marbles.swap_remove(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process triggers
|
// propagate power
|
||||||
self.flipper_events.clear();
|
let mut i = 0;
|
||||||
for pos in triggers_activated {
|
while i < self.powered.len() {
|
||||||
let Some(Tile::Powerable(PTile::Trigger, state)) = self.board.get_mut(pos) else {
|
let pos = self.powered[i];
|
||||||
|
let Some(tile) = self.board.get_mut(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
self.powered.push(pos);
|
match tile {
|
||||||
|
Tile::Trigger(state) => {
|
||||||
*state = true;
|
*state = true;
|
||||||
for dir in Direction::ALL {
|
for dir in Direction::ALL {
|
||||||
self.propagate_power(dir, dir.step(pos));
|
let target_pos = dir.step(pos);
|
||||||
|
match self.board.get_mut(target_pos) {
|
||||||
|
Some(Tile::Powerable(_, state)) => {
|
||||||
|
if !*state {
|
||||||
|
*state = true;
|
||||||
|
self.powered.push(target_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(Tile::Wire(_, state)) => {
|
||||||
// process flipped tiles
|
// only push it if it hasnt already been pushed
|
||||||
for &p in &self.flipper_events {
|
if !*state {
|
||||||
let Some(target) = self.board.get_mut(p) else {
|
*state = true;
|
||||||
|
self.powered.push(target_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tile::Wire(wiretype, state) => {
|
||||||
|
*state = true;
|
||||||
|
for dir in wiretype.directions() {
|
||||||
|
let target_pos = dir.step(pos);
|
||||||
|
match self.board.get_mut(target_pos) {
|
||||||
|
Some(Tile::Powerable(_, state)) => {
|
||||||
|
if !*state {
|
||||||
|
*state = true;
|
||||||
|
self.powered.push(target_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Tile::Wire(_, state)) => {
|
||||||
|
// only push it if it hasnt already been pushed
|
||||||
|
if !*state {
|
||||||
|
*state = true;
|
||||||
|
self.powered.push(target_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tile::Powerable(PTile::Gate(gate), state) => {
|
||||||
|
*state = true;
|
||||||
|
let gate = *gate;
|
||||||
|
for dir in Direction::ALL {
|
||||||
|
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::Trigger(true)) => (),
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
let Some(front_tile) = self.board.get_mut(front_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match target {
|
if matches!(front_tile, Tile::Wire(_, _) | Tile::Powerable(_, _)) {
|
||||||
Tile::Powerable(PTile::Wire(wire_type), _) => {
|
|
||||||
*wire_type = match *wire_type {
|
|
||||||
WireType::Vertical => WireType::Horizontal,
|
|
||||||
WireType::Horizontal => WireType::Vertical,
|
|
||||||
WireType::Cross => WireType::Cross,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Tile::Mirror(mirror) => mirror.flip(),
|
|
||||||
Tile::Arrow(dir) => *dir = dir.opposite(),
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn propagate_power(&mut self, dir: Direction, pos: Pos) {
|
|
||||||
let Some(tile) = self.board.get_mut(pos) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let front_pos = dir.step(pos);
|
|
||||||
if let Tile::Powerable(tile, state) = tile {
|
|
||||||
if let PTile::Trigger = tile {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let was_powered = *state;
|
|
||||||
*state = true;
|
|
||||||
self.powered.push(pos);
|
|
||||||
match tile {
|
|
||||||
PTile::Wire(wiretype) => {
|
|
||||||
if was_powered {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let dirs = wiretype.directions();
|
|
||||||
for d in dirs {
|
|
||||||
self.propagate_power(*d, d.step(pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PTile::Bag => {
|
|
||||||
if let Some(front) = self.board.get_blank_mut(front_pos) {
|
|
||||||
*front = Tile::Marble { value: 0, dir };
|
|
||||||
self.marbles.push(front_pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PTile::IO => {
|
|
||||||
if let Some(front) = self.board.get_blank_mut(front_pos) {
|
|
||||||
if self.input_index < self.input.len() {
|
|
||||||
let value = self.input[self.input_index] as MarbleValue;
|
|
||||||
self.input_index += 1;
|
|
||||||
*front = Tile::Marble { value, dir };
|
|
||||||
self.marbles.push(front_pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PTile::Math(op) => {
|
|
||||||
let op = *op;
|
|
||||||
let pos_a = dir.left().step(pos);
|
|
||||||
let pos_b = dir.right().step(pos);
|
|
||||||
let val_a = self.board.get_or_blank(pos_a).read_value();
|
|
||||||
let val_b = self.board.get_or_blank(pos_b).read_value();
|
|
||||||
if !self.board.get_or_blank(pos_a).is_blank()
|
|
||||||
|| !self.board.get_or_blank(pos_b).is_blank()
|
|
||||||
{
|
|
||||||
let value = match op {
|
|
||||||
MathOp::Add => val_a.wrapping_add(val_b),
|
|
||||||
MathOp::Sub => val_a.wrapping_sub(val_b),
|
|
||||||
MathOp::Mul => val_a.wrapping_mul(val_b),
|
|
||||||
MathOp::Div => val_a.checked_div(val_b).unwrap_or_default(),
|
|
||||||
MathOp::Rem => val_a.checked_rem(val_b).unwrap_or_default(),
|
|
||||||
};
|
|
||||||
// println!("{op:?} a:{val_a} b:{val_b}");
|
|
||||||
if let Some(front) = self.board.get_blank_mut(front_pos) {
|
|
||||||
*front = Tile::Marble { value, dir };
|
|
||||||
self.marbles.push(front_pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PTile::Flipper => {
|
|
||||||
self.flipper_events.push(front_pos);
|
|
||||||
}
|
|
||||||
PTile::Gate(gate) => {
|
|
||||||
let gate = *gate;
|
|
||||||
let pos_a = dir.left().step(pos);
|
let pos_a = dir.left().step(pos);
|
||||||
let pos_b = dir.right().step(pos);
|
let pos_b = dir.right().step(pos);
|
||||||
let val_a = self.board.get_or_blank(pos_a).read_value();
|
let val_a = self.board.get_or_blank(pos_a).read_value();
|
||||||
|
@ -429,11 +492,19 @@ impl Machine {
|
||||||
GateType::NotEqual => val_a != val_b,
|
GateType::NotEqual => val_a != val_b,
|
||||||
};
|
};
|
||||||
if result {
|
if result {
|
||||||
self.propagate_power(dir, dir.step(pos));
|
self.powered.push(front_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PTile::Trigger => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// state may be false if it was powered by a machine in earlier step
|
||||||
|
Tile::Powerable(_, state) => *state = true,
|
||||||
|
_ => {
|
||||||
|
dbg!(tile);
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,16 +101,6 @@ impl Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_blank_mut(&mut self, p: Pos) -> Option<&mut Tile> {
|
|
||||||
if self.in_bounds(p) {
|
|
||||||
let tile = &mut self.rows[p.y as usize][p.x as usize];
|
|
||||||
if let Tile::Open(OpenTile::Blank, _) = tile{
|
|
||||||
return Some(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&mut self, p: Pos, tile: Tile) {
|
pub fn set(&mut self, p: Pos, tile: Tile) {
|
||||||
if self.in_bounds(p) {
|
if self.in_bounds(p) {
|
||||||
self.rows[p.y as usize][p.x as usize] = tile;
|
self.rows[p.y as usize][p.x as usize] = tile;
|
||||||
|
|
|
@ -4,16 +4,18 @@ pub type MarbleValue = u32;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Tile {
|
pub enum Tile {
|
||||||
Open(OpenTile, MarbleTarget),
|
Open(OpenTile, Claim),
|
||||||
Block,
|
Block,
|
||||||
Marble { value: MarbleValue, dir: Direction },
|
Marble { value: MarbleValue, dir: Direction },
|
||||||
Mirror(MirrorType),
|
Mirror(MirrorType),
|
||||||
Arrow(Direction),
|
Arrow(Direction),
|
||||||
|
Trigger(bool),
|
||||||
|
Wire(WireType, bool),
|
||||||
Powerable(PTile, bool),
|
Powerable(PTile, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum MarbleTarget {
|
pub enum Claim {
|
||||||
Free,
|
Free,
|
||||||
ClaimedIndirect,
|
ClaimedIndirect,
|
||||||
BlockedIndirect,
|
BlockedIndirect,
|
||||||
|
@ -29,8 +31,6 @@ pub enum OpenTile {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum PTile {
|
pub enum PTile {
|
||||||
Trigger,
|
|
||||||
Wire(WireType),
|
|
||||||
Gate(GateType),
|
Gate(GateType),
|
||||||
Math(MathOp),
|
Math(MathOp),
|
||||||
Bag,
|
Bag,
|
||||||
|
@ -78,7 +78,7 @@ pub enum Direction {
|
||||||
|
|
||||||
impl Default for Tile {
|
impl Default for Tile {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Tile::Open(OpenTile::Blank, MarbleTarget::Free)
|
Tile::Open(OpenTile::Blank, Claim::Free)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,10 +89,10 @@ impl Tile {
|
||||||
value: 0,
|
value: 0,
|
||||||
dir: Direction::Down,
|
dir: Direction::Down,
|
||||||
},
|
},
|
||||||
'*' => Tile::Powerable(PTile::Trigger, false),
|
'*' => Tile::Trigger(false),
|
||||||
'-' => Tile::Powerable(PTile::Wire(WireType::Horizontal), false),
|
'-' => Tile::Wire(WireType::Horizontal, false),
|
||||||
'|' => Tile::Powerable(PTile::Wire(WireType::Vertical), false),
|
'|' => Tile::Wire(WireType::Vertical, false),
|
||||||
'+' => Tile::Powerable(PTile::Wire(WireType::Cross), false),
|
'+' => Tile::Wire(WireType::Cross, false),
|
||||||
'/' => Tile::Mirror(MirrorType::Forward),
|
'/' => Tile::Mirror(MirrorType::Forward),
|
||||||
'\\' => Tile::Mirror(MirrorType::Back),
|
'\\' => Tile::Mirror(MirrorType::Back),
|
||||||
'^' => Tile::Arrow(Direction::Up),
|
'^' => Tile::Arrow(Direction::Up),
|
||||||
|
@ -111,9 +111,9 @@ impl Tile {
|
||||||
'D' => Tile::Powerable(PTile::Math(MathOp::Div), false),
|
'D' => Tile::Powerable(PTile::Math(MathOp::Div), false),
|
||||||
'R' => Tile::Powerable(PTile::Math(MathOp::Rem), false),
|
'R' => Tile::Powerable(PTile::Math(MathOp::Rem), false),
|
||||||
'B' => Tile::Powerable(PTile::Bag, false),
|
'B' => Tile::Powerable(PTile::Bag, false),
|
||||||
d @ '0'..='9' => Tile::Open(OpenTile::Digit(d as u8 - b'0'), MarbleTarget::Free),
|
d @ '0'..='9' => Tile::Open(OpenTile::Digit(d as u8 - b'0'), Claim::Free),
|
||||||
'#' => Tile::Block,
|
'#' => Tile::Block,
|
||||||
_ => Tile::Open(OpenTile::Blank, MarbleTarget::Free),
|
_ => Tile::Open(OpenTile::Blank, Claim::Free),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,13 +133,13 @@ impl Tile {
|
||||||
Direction::Left => '<',
|
Direction::Left => '<',
|
||||||
Direction::Right => '>',
|
Direction::Right => '>',
|
||||||
},
|
},
|
||||||
Tile::Powerable(tile, _) => match tile {
|
Tile::Trigger(_) => '*',
|
||||||
PTile::Trigger => '*',
|
Tile::Wire(wire, _) => match wire {
|
||||||
PTile::Wire(wire) => match wire {
|
|
||||||
WireType::Vertical => '|',
|
WireType::Vertical => '|',
|
||||||
WireType::Horizontal => '-',
|
WireType::Horizontal => '-',
|
||||||
WireType::Cross => '+',
|
WireType::Cross => '+',
|
||||||
},
|
},
|
||||||
|
Tile::Powerable(tile, _) => match tile {
|
||||||
PTile::Gate(gate) => match gate {
|
PTile::Gate(gate) => match gate {
|
||||||
GateType::LessThan => 'L',
|
GateType::LessThan => 'L',
|
||||||
GateType::GreaterThan => 'G',
|
GateType::GreaterThan => 'G',
|
||||||
|
@ -180,10 +180,22 @@ impl Tile {
|
||||||
Tile::Open(OpenTile::Digit(n), _) => return format!("tile_digit_{n}"),
|
Tile::Open(OpenTile::Digit(n), _) => return format!("tile_digit_{n}"),
|
||||||
Tile::Mirror(mirror) => mirror.texture_name(),
|
Tile::Mirror(mirror) => mirror.texture_name(),
|
||||||
Tile::Arrow(dir) => dir.arrow_tile_texture_name(),
|
Tile::Arrow(dir) => dir.arrow_tile_texture_name(),
|
||||||
|
Tile::Trigger(state) => {
|
||||||
|
if *state {
|
||||||
|
"trigger_on"
|
||||||
|
} else {
|
||||||
|
"trigger_off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tile::Wire(wire, state) => {
|
||||||
|
return format!(
|
||||||
|
"{}_{}",
|
||||||
|
wire.texture_name(),
|
||||||
|
if *state { "on" } else { "off" }
|
||||||
|
)
|
||||||
|
}
|
||||||
Tile::Powerable(tile, state) => {
|
Tile::Powerable(tile, state) => {
|
||||||
let root = match tile {
|
let root = match tile {
|
||||||
PTile::Trigger => "trigger",
|
|
||||||
PTile::Wire(wire) => wire.texture_name(),
|
|
||||||
PTile::Gate(gate) => gate.texture_name(),
|
PTile::Gate(gate) => gate.texture_name(),
|
||||||
PTile::Math(math_op) => math_op.texture_name(),
|
PTile::Math(math_op) => math_op.texture_name(),
|
||||||
PTile::Bag => "bag",
|
PTile::Bag => "bag",
|
||||||
|
@ -257,6 +269,14 @@ impl Direction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WireType {
|
impl WireType {
|
||||||
|
pub fn has_output(self, dir: Direction) -> bool {
|
||||||
|
match self {
|
||||||
|
WireType::Vertical => matches!(dir, Direction::Up | Direction::Down),
|
||||||
|
WireType::Horizontal => matches!(dir, Direction::Right | Direction::Left),
|
||||||
|
WireType::Cross => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn directions(self) -> &'static [Direction] {
|
pub fn directions(self) -> &'static [Direction] {
|
||||||
match self {
|
match self {
|
||||||
WireType::Vertical => &[Direction::Up, Direction::Down],
|
WireType::Vertical => &[Direction::Up, Direction::Down],
|
||||||
|
|
Loading…
Reference in a new issue