Compare commits

..

No commits in common. "e9e763332fbc8bcc3b39c6576152be2436825b86" and "e6f99d377666071025d2bee120a6bebb1d0d6b20" have entirely different histories.

5 changed files with 211 additions and 284 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 B

After

Width:  |  Height:  |  Size: 98 B

View file

@ -14,7 +14,7 @@ use crate::{
marble_engine::{ marble_engine::{
board::Board, board::Board,
pos::{Pos, PosInt}, pos::{Pos, PosInt},
tile::{Claim, Direction, GateType, MathOp, MirrorType, OpenTile, PTile, Tile, WireType}, tile::{Direction, GateType, MarbleTarget, 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(),
@ -722,7 +722,7 @@ impl Editor {
let max = selection.0.max(selection.1); let max = selection.0.max(selection.1);
for x in min.x..=max.x { for x in min.x..=max.x {
for y in min.y..=max.y { for y in min.y..=max.y {
self.source_board.set(Pos { x, y }, Tile::BLANK); self.source_board.set(Pos { x, y }, Tile::default());
} }
} }
} }
@ -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::Wire(self.tool_wire, false).texture(), &Tile::Powerable(PTile::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), Claim::Free)); self.set_tile(pos, Tile::Open(OpenTile::Digit(n as u8), MarbleTarget::Free));
} }
} }
} }
@ -986,14 +986,16 @@ 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 => self.set_tile(pos, Tile::Wire(self.tool_wire, false)), Tool::Wire => {
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), Claim::Free); *tile = Tile::Open(OpenTile::Digit(0), MarbleTarget::Free);
} }
} }
} }
@ -1016,7 +1018,7 @@ impl Editor {
if d.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT) if d.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT)
&& self.active_tool == Tool::Erase && self.active_tool == Tool::Erase
{ {
self.set_tile(tile_pos.into(), Tile::BLANK) self.set_tile(tile_pos.into(), Tile::default())
} }
if let Tool::SelectArea(selection) = &mut self.active_tool { if let Tool::SelectArea(selection) = &mut self.active_tool {
if d.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT) { if d.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT) {

View file

@ -14,6 +14,8 @@ 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>,
@ -26,6 +28,7 @@ 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(),
@ -99,114 +102,12 @@ impl Machine {
pub fn step(&mut self) { pub fn step(&mut self) {
self.steps += 1; self.steps += 1;
// reset wires
let old_marbles = self.marbles.len(); for &p in &self.powered {
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::BLANK && 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::BLANK {
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!()
}; };
*claim = match claim { *state = false;
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();
@ -269,14 +170,16 @@ impl Machine {
}; };
if will_reverse_direction[i] { if will_reverse_direction[i] {
*dir = dir.opposite(); *dir = dir.opposite();
} else if let DirInfluence::One(new_dir) = influenced_direction[i] { } else {
*dir = new_dir; if let DirInfluence::One(new_dir) = influenced_direction[i] {
*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[..old_marbles] { for &pos in &self.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!()
}; };
@ -286,44 +189,51 @@ impl Machine {
}; };
if let Tile::Open(_type, claim) = front_tile { if let Tile::Open(_type, claim) = front_tile {
*claim = match claim { *claim = match claim {
Claim::Free => { MarbleTarget::Free => {
claim_positions.push(front_pos); claim_positions.push(front_pos);
Claim::Claimed MarbleTarget::Claimed
} }
Claim::ClaimedIndirect => Claim::Claimed, MarbleTarget::ClaimedIndirect => MarbleTarget::Claimed,
Claim::BlockedIndirect => Claim::Claimed, MarbleTarget::BlockedIndirect => MarbleTarget::Claimed,
Claim::Claimed => Claim::Blocked, MarbleTarget::Claimed => MarbleTarget::Blocked,
Claim::Blocked => Claim::Blocked, MarbleTarget::Blocked => MarbleTarget::Blocked,
}; };
} else { } else {
let target_pos = match front_tile { let target_pos;
Tile::Arrow(d) => d.step(front_pos), match front_tile {
Tile::Mirror(m) => m.new_dir(dir).step(front_pos), Tile::Arrow(d) => {
Tile::Trigger(_) => dir.step(front_pos), target_pos = d.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 {
Claim::Free => { MarbleTarget::Free => {
claim_positions.push(front_pos); claim_positions.push(front_pos);
Claim::ClaimedIndirect MarbleTarget::ClaimedIndirect
} }
Claim::ClaimedIndirect => Claim::BlockedIndirect, MarbleTarget::ClaimedIndirect => MarbleTarget::BlockedIndirect,
Claim::BlockedIndirect => Claim::BlockedIndirect, MarbleTarget::BlockedIndirect => MarbleTarget::BlockedIndirect,
Claim::Claimed => Claim::Claimed, MarbleTarget::Claimed => MarbleTarget::Claimed,
Claim::Blocked => Claim::Blocked, MarbleTarget::Blocked => MarbleTarget::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[..old_marbles].iter_mut().enumerate() { for (i, pos) in self.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!()
}; };
@ -337,15 +247,15 @@ impl Machine {
OpenTile::Blank => value, OpenTile::Blank => value,
OpenTile::Digit(n) => value.wrapping_mul(10).wrapping_add(n as MarbleValue), OpenTile::Digit(n) => value.wrapping_mul(10).wrapping_add(n as MarbleValue),
}; };
board.set(*pos, Tile::BLANK); board.set(*pos, Tile::default());
board.set(target_pos, Tile::Marble { value, dir }); board.set(target_pos, Tile::Marble { value, dir });
*pos = target_pos; *pos = target_pos;
}; };
if let Tile::Open(space_type, claim_state) = front_tile { if let Tile::Open(space_type, claim_state) = front_tile {
if *claim_state == Claim::Claimed { if *claim_state == MarbleTarget::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 != Claim::Free { } else if *claim_state != MarbleTarget::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(
@ -369,7 +279,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::Trigger(_) => { Tile::Powerable(PTile::Trigger, _) => {
is_trigger = true; is_trigger = true;
target_pos = dir.step(front_pos); target_pos = dir.step(front_pos);
} }
@ -387,10 +297,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, Claim::ClaimedIndirect) = target_tile { if let Tile::Open(space_type, MarbleTarget::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 {
self.powered.push(front_pos); triggers_activated.push(front_pos);
} }
} }
} }
@ -398,112 +308,132 @@ 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 = Claim::Free; *claim_state = MarbleTarget::Free;
} }
} }
// remove marbles // remove marbles
for &i in removed_marbles.iter().rev() { for &i in removed_marbles.iter().rev() {
self.board.set(self.marbles[i], Tile::BLANK); self.board.set(self.marbles[i], Tile::default());
self.marbles.swap_remove(i); self.marbles.swap_remove(i);
} }
// propagate power // process triggers
let mut i = 0; self.flipper_events.clear();
while i < self.powered.len() { for pos in triggers_activated {
let pos = self.powered[i]; let Some(Tile::Powerable(PTile::Trigger, state)) = self.board.get_mut(pos) else {
let Some(tile) = self.board.get_mut(pos) else {
unreachable!() unreachable!()
}; };
match tile { self.powered.push(pos);
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 { // process flipped tiles
*state = true; for &p in &self.flipper_events {
self.powered.push(target_pos); let Some(target) = self.board.get_mut(p) else {
} continue;
} };
Some(Tile::Wire(_, state)) => { match target {
// only push it if it hasnt already been pushed Tile::Powerable(PTile::Wire(wire_type), _) => {
if !*state { *wire_type = match *wire_type {
*state = true; WireType::Vertical => WireType::Horizontal,
self.powered.push(target_pos); WireType::Horizontal => WireType::Vertical,
} WireType::Cross => WireType::Cross,
} }
_ => (), }
} Tile::Mirror(mirror) => mirror.flip(),
} Tile::Arrow(dir) => *dir = dir.opposite(),
} _ => (),
Tile::Wire(wiretype, state) => { };
*state = true; }
for dir in wiretype.directions() { }
let target_pos = dir.step(pos);
match self.board.get_mut(target_pos) { fn propagate_power(&mut self, dir: Direction, pos: Pos) {
Some(Tile::Powerable(_, state)) => { let Some(tile) = self.board.get_mut(pos) else {
if !*state { return;
*state = true; };
self.powered.push(target_pos); let front_pos = dir.step(pos);
} if let Tile::Powerable(tile, state) = tile {
} if let PTile::Trigger = tile {
Some(Tile::Wire(_, state)) => { return;
// only push it if it hasnt already been pushed }
if !*state { let was_powered = *state;
*state = true; *state = true;
self.powered.push(target_pos); self.powered.push(pos);
} match tile {
} PTile::Wire(wiretype) => {
_ => (), if was_powered {
} return;
} }
} let dirs = wiretype.directions();
Tile::Powerable(PTile::Gate(gate), state) => { for d in dirs {
*state = true; self.propagate_power(*d, d.step(pos));
let gate = *gate; }
for dir in Direction::ALL { }
let front_pos = dir.step(pos); PTile::Bag => {
let source_pos = dir.opposite().step(pos); if let Some(front) = self.board.get_blank_mut(front_pos) {
match self.board.get(source_pos) { *front = Tile::Marble { value: 0, dir };
Some(Tile::Wire(wiretype, true)) => { self.marbles.push(front_pos);
if !wiretype.has_output(dir) { }
continue; }
} PTile::IO => {
} if let Some(front) = self.board.get_blank_mut(front_pos) {
Some(Tile::Trigger(true)) => (), if self.input_index < self.input.len() {
_ => continue, let value = self.input[self.input_index] as MarbleValue;
} self.input_index += 1;
let Some(front_tile) = self.board.get_mut(front_pos) else { *front = Tile::Marble { value, dir };
continue; self.marbles.push(front_pos);
}; }
if matches!(front_tile, Tile::Wire(_, _) | Tile::Powerable(_, _)) { }
let pos_a = dir.left().step(pos); }
let pos_b = dir.right().step(pos); PTile::Math(op) => {
let val_a = self.board.get_or_blank(pos_a).read_value(); let op = *op;
let val_b = self.board.get_or_blank(pos_b).read_value(); let pos_a = dir.left().step(pos);
let pos_b = dir.right().step(pos);
let result = match gate { let val_a = self.board.get_or_blank(pos_a).read_value();
GateType::LessThan => val_a < val_b, let val_b = self.board.get_or_blank(pos_b).read_value();
GateType::GreaterThan => val_a > val_b, if !self.board.get_or_blank(pos_a).is_blank()
GateType::Equal => val_a == val_b, || !self.board.get_or_blank(pos_b).is_blank()
GateType::NotEqual => val_a != val_b, {
}; let value = match op {
if result { MathOp::Add => val_a.wrapping_add(val_b),
self.powered.push(front_pos); 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(),
} };
// state may be false if it was powered by a machine in earlier step // println!("{op:?} a:{val_a} b:{val_b}");
Tile::Powerable(_, state) => *state = true, if let Some(front) = self.board.get_blank_mut(front_pos) {
_ => { *front = Tile::Marble { value, dir };
dbg!(tile); self.marbles.push(front_pos);
unreachable!() }
} }
}
PTile::Flipper => {
self.flipper_events.push(front_pos);
}
PTile::Gate(gate) => {
let gate = *gate;
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 result = match gate {
GateType::LessThan => val_a < val_b,
GateType::GreaterThan => val_a > val_b,
GateType::Equal => val_a == val_b,
GateType::NotEqual => val_a != val_b,
};
if result {
self.propagate_power(dir, dir.step(pos));
}
}
PTile::Trigger => (),
} }
i += 1;
} }
} }
} }

View file

@ -26,7 +26,7 @@ impl Board {
rows.push(tiles); rows.push(tiles);
} }
for line in &mut rows { for line in &mut rows {
line.resize(width, Tile::BLANK); line.resize(width, Tile::default());
} }
Board::new(rows) Board::new(rows)
@ -44,7 +44,7 @@ impl Board {
} }
pub fn new_empty(width: usize, height: usize) -> Self { pub fn new_empty(width: usize, height: usize) -> Self {
let rows = vec![vec![Tile::BLANK; width]; height]; let rows = vec![vec![Tile::default(); width]; height];
Self { Self {
rows, rows,
width, width,
@ -89,7 +89,7 @@ impl Board {
if self.in_bounds(p) { if self.in_bounds(p) {
self.rows[p.y as usize][p.x as usize] self.rows[p.y as usize][p.x as usize]
} else { } else {
Tile::BLANK Tile::default()
} }
} }
@ -101,6 +101,16 @@ 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;
@ -137,7 +147,7 @@ impl Board {
if p.x < 0 { if p.x < 0 {
let len = p.x.unsigned_abs() as usize; let len = p.x.unsigned_abs() as usize;
for row in &mut self.rows { for row in &mut self.rows {
let mut new_row = vec![Tile::BLANK; len]; let mut new_row = vec![Tile::default(); len];
new_row.append(row); new_row.append(row);
*row = new_row; *row = new_row;
} }
@ -146,21 +156,21 @@ impl Board {
} else if p.x as usize >= self.width { } else if p.x as usize >= self.width {
let new_width = p.x as usize + 1; let new_width = p.x as usize + 1;
for row in &mut self.rows { for row in &mut self.rows {
row.resize(new_width, Tile::BLANK); row.resize(new_width, Tile::default());
} }
self.width = new_width; self.width = new_width;
} }
if p.y < 0 { if p.y < 0 {
let len = p.y.unsigned_abs() as usize; let len = p.y.unsigned_abs() as usize;
let mut new_rows = vec![vec![Tile::BLANK; self.width]; len]; let mut new_rows = vec![vec![Tile::default(); self.width]; len];
new_rows.append(&mut self.rows); new_rows.append(&mut self.rows);
self.rows = new_rows; self.rows = new_rows;
offset_y = len; offset_y = len;
self.height += len; self.height += len;
} else if p.y as usize >= self.height { } else if p.y as usize >= self.height {
let new_height = p.y as usize + 1; let new_height = p.y as usize + 1;
self.rows.resize(new_height, vec![Tile::BLANK; self.width]); self.rows.resize(new_height, vec![Tile::default(); self.width]);
self.height = new_height; self.height = new_height;
} }
(offset_x as PosInt, offset_y as PosInt) (offset_x as PosInt, offset_y as PosInt)

View file

@ -4,18 +4,16 @@ pub type MarbleValue = u32;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Tile { pub enum Tile {
Open(OpenTile, Claim), Open(OpenTile, MarbleTarget),
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 Claim { pub enum MarbleTarget {
Free, Free,
ClaimedIndirect, ClaimedIndirect,
BlockedIndirect, BlockedIndirect,
@ -31,6 +29,8 @@ 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,
@ -76,18 +76,23 @@ pub enum Direction {
Right, Right,
} }
impl Default for Tile {
fn default() -> Self {
Tile::Open(OpenTile::Blank, MarbleTarget::Free)
}
}
impl Tile { impl Tile {
pub const BLANK: Self = Tile::Open(OpenTile::Blank, Claim::Free);
pub const fn from_char(c: char) -> Tile { pub const fn from_char(c: char) -> Tile {
match c { match c {
'o' => Tile::Marble { 'o' => Tile::Marble {
value: 0, value: 0,
dir: Direction::Down, dir: Direction::Down,
}, },
'*' => Tile::Trigger(false), '*' => Tile::Powerable(PTile::Trigger, false),
'-' => Tile::Wire(WireType::Horizontal, false), '-' => Tile::Powerable(PTile::Wire(WireType::Horizontal), false),
'|' => Tile::Wire(WireType::Vertical, false), '|' => Tile::Powerable(PTile::Wire(WireType::Vertical), false),
'+' => Tile::Wire(WireType::Cross, false), '+' => Tile::Powerable(PTile::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),
@ -106,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'), Claim::Free), d @ '0'..='9' => Tile::Open(OpenTile::Digit(d as u8 - b'0'), MarbleTarget::Free),
'#' => Tile::Block, '#' => Tile::Block,
_ => Tile::Open(OpenTile::Blank, Claim::Free), _ => Tile::Open(OpenTile::Blank, MarbleTarget::Free),
} }
} }
@ -128,13 +133,13 @@ impl Tile {
Direction::Left => '<', Direction::Left => '<',
Direction::Right => '>', Direction::Right => '>',
}, },
Tile::Trigger(_) => '*',
Tile::Wire(wire, _) => match wire {
WireType::Vertical => '|',
WireType::Horizontal => '-',
WireType::Cross => '+',
},
Tile::Powerable(tile, _) => match tile { Tile::Powerable(tile, _) => match tile {
PTile::Trigger => '*',
PTile::Wire(wire) => match wire {
WireType::Vertical => '|',
WireType::Horizontal => '-',
WireType::Cross => '+',
},
PTile::Gate(gate) => match gate { PTile::Gate(gate) => match gate {
GateType::LessThan => 'L', GateType::LessThan => 'L',
GateType::GreaterThan => 'G', GateType::GreaterThan => 'G',
@ -175,22 +180,10 @@ 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",
@ -264,14 +257,6 @@ 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],