Compare commits
2 commits
353f5b74e5
...
635f0cf641
Author | SHA1 | Date | |
---|---|---|---|
635f0cf641 | |||
0275e0c9b5 |
5 changed files with 253 additions and 195 deletions
|
@ -6,7 +6,6 @@ logic mostly like https://git.crispypin.cc/CrispyPin/marble
|
|||
## todo
|
||||
- undo/redo
|
||||
- more levels
|
||||
- make marble movement symmetric and order-independent
|
||||
- make power propagation not recursive
|
||||
- story/lore
|
||||
- cut selections, copy to system clipboard
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
marble_engine::{
|
||||
board::Board,
|
||||
pos::{Pos, PosInt},
|
||||
tile::{Direction, GateType, MathOp, MirrorType, PTile, Tile, WireType},
|
||||
tile::{Direction, GateType, MarbleTarget, MathOp, MirrorType, OpenTile, PTile, Tile, WireType},
|
||||
Machine,
|
||||
},
|
||||
simple_button, simple_option_button, slider,
|
||||
|
@ -722,7 +722,7 @@ impl Editor {
|
|||
let max = selection.0.max(selection.1);
|
||||
for x in min.x..=max.x {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -935,7 +935,7 @@ impl Editor {
|
|||
let pos = *pos;
|
||||
for n in 0..10 {
|
||||
if d.is_key_pressed(unsafe { transmute::<u32, KeyboardKey>(b'0' as u32 + n) }) {
|
||||
self.set_tile(pos, Tile::Digit(n as u8));
|
||||
self.set_tile(pos, Tile::Open(OpenTile::Digit(n as u8), MarbleTarget::Free));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -994,9 +994,8 @@ impl Editor {
|
|||
Tool::Digits(_pos) => {
|
||||
self.active_tool = Tool::Digits(Some(pos));
|
||||
if let Some(tile) = self.source_board.get_mut(pos) {
|
||||
if let Tile::Digit(_) = tile {
|
||||
} else {
|
||||
*tile = Tile::Digit(0);
|
||||
if ! matches!(tile, Tile::Open(OpenTile::Digit(_), _)) {
|
||||
*tile = Tile::Open(OpenTile::Digit(0), MarbleTarget::Free);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1019,7 +1018,7 @@ impl Editor {
|
|||
if d.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT)
|
||||
&& 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 d.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT) {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::hint::unreachable_unchecked;
|
||||
|
||||
use raylib::prelude::*;
|
||||
|
||||
pub mod board;
|
||||
|
@ -16,7 +14,6 @@ pub struct Machine {
|
|||
board: Board,
|
||||
marbles: Vec<Pos>,
|
||||
powered: Vec<Pos>,
|
||||
events: Vec<Event>,
|
||||
flipper_events: Vec<Pos>,
|
||||
|
||||
input: Vec<u8>,
|
||||
|
@ -25,19 +22,6 @@ pub struct Machine {
|
|||
steps: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Event {
|
||||
Stay,
|
||||
/// (new_pos, new_dir)
|
||||
MoveTo(Pos, Direction),
|
||||
/// (new_pos, new_dir, trigger_pos)
|
||||
Trigger(Pos, Direction, Pos),
|
||||
/// (other, new_dir)
|
||||
/// other marble should be set to reverse of new_dir
|
||||
/// and should be cancelled if it had a movement event planned
|
||||
Bounce(usize, Direction),
|
||||
Remove,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new_empty(input: Vec<u8>, width: usize) -> Self {
|
||||
|
@ -45,7 +29,6 @@ impl Machine {
|
|||
board: Board::new_empty(width, width),
|
||||
marbles: Vec::new(),
|
||||
powered: Vec::new(),
|
||||
events: Vec::new(),
|
||||
flipper_events: Vec::new(),
|
||||
input,
|
||||
input_index: 0,
|
||||
|
@ -58,10 +41,12 @@ impl Machine {
|
|||
self.steps = 0;
|
||||
self.input_index = 0;
|
||||
self.output.clear();
|
||||
self.powered.clear();
|
||||
}
|
||||
|
||||
pub fn set_board(&mut self, board: Board) {
|
||||
self.marbles = board.get_marbles();
|
||||
self.powered.clear();
|
||||
self.board = board;
|
||||
}
|
||||
|
||||
|
@ -121,7 +106,7 @@ impl Machine {
|
|||
// reset wires
|
||||
for &p in &self.powered {
|
||||
let Some(Tile::Powerable(_, state)) = self.board.get_mut(p) else {
|
||||
unsafe { unreachable_unchecked() }
|
||||
unreachable!()
|
||||
};
|
||||
*state = false;
|
||||
}
|
||||
|
@ -131,169 +116,229 @@ impl Machine {
|
|||
return;
|
||||
}
|
||||
|
||||
self.events.clear();
|
||||
for &pos in &self.marbles {
|
||||
let marble = self.board.get(pos).unwrap();
|
||||
let Tile::Marble { value, dir } = marble else {
|
||||
panic!("broken marble");
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum DirInfluence {
|
||||
None,
|
||||
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
|
||||
let mut influenced_direction = vec![DirInfluence::None; self.marbles.len()];
|
||||
|
||||
for (i, &pos) in self.marbles.iter().enumerate() {
|
||||
let Some(Tile::Marble { value: _, dir }) = self.board.get(pos) else {
|
||||
unreachable!()
|
||||
};
|
||||
let front_pos = dir.step(pos);
|
||||
let Some(front_tile) = self.board.get(front_pos) else {
|
||||
self.events.push(Event::Stay);
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Tile::Powerable(PTile::Bag, _) = front_tile {
|
||||
self.events.push(Event::Remove);
|
||||
continue;
|
||||
}
|
||||
if let Tile::Powerable(PTile::IO, _) = front_tile {
|
||||
self.output.push(value as u8);
|
||||
self.events.push(Event::Remove);
|
||||
continue;
|
||||
}
|
||||
|
||||
let can_move_to = |tile| matches!(tile, Some(Tile::Blank | Tile::Digit(_)));
|
||||
|
||||
let can_move_over = |tile| match tile {
|
||||
Tile::Mirror(mirror) => {
|
||||
let new_dir = mirror.new_dir(dir);
|
||||
let target_pos = new_dir.step(front_pos);
|
||||
let target = self.board.get(target_pos);
|
||||
if can_move_to(target) {
|
||||
Some((target_pos, new_dir))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Tile::Arrow(new_dir) => {
|
||||
let target_pos = new_dir.step(front_pos);
|
||||
let target = self.board.get(target_pos);
|
||||
if target_pos == pos || can_move_to(target) {
|
||||
Some((target_pos, new_dir))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let e = if can_move_to(Some(front_tile)) {
|
||||
Event::MoveTo(front_pos, dir)
|
||||
} else if let Tile::Powerable(PTile::Trigger, _) = front_tile {
|
||||
let target_pos = dir.step(front_pos);
|
||||
let target = self.board.get(target_pos);
|
||||
if can_move_to(target) {
|
||||
Event::Trigger(target_pos, dir, front_pos)
|
||||
} else {
|
||||
Event::Stay
|
||||
}
|
||||
} else if let Some((new_pos, new_dir)) = can_move_over(front_tile) {
|
||||
Event::MoveTo(new_pos, new_dir)
|
||||
} else if let Tile::Marble {
|
||||
value: _,
|
||||
dir: other_dir,
|
||||
} = front_tile
|
||||
{
|
||||
if other_dir != dir {
|
||||
Event::Bounce(
|
||||
self.marbles.iter().position(|m| m == &front_pos).unwrap(),
|
||||
dir.opposite(),
|
||||
)
|
||||
} else {
|
||||
Event::Stay
|
||||
}
|
||||
} else {
|
||||
Event::Stay
|
||||
};
|
||||
self.events.push(e);
|
||||
}
|
||||
|
||||
// resolve bounces
|
||||
for i in 0..self.events.len() {
|
||||
let event = self.events[i];
|
||||
if let Event::Bounce(other_index, dir) = event {
|
||||
match self.events[other_index] {
|
||||
// cancel bounces on marble that are about to disappear
|
||||
Event::Remove => {
|
||||
self.events[i] = Event::MoveTo(self.marbles[other_index], dir.opposite())
|
||||
}
|
||||
// let already bouncing marbles continue
|
||||
Event::Bounce(_, _) => (),
|
||||
// interrupt any other movement/staying to bounce
|
||||
_ => self.events[other_index] = Event::Bounce(i, dir.opposite()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolve deletions of tiles
|
||||
for (i, event) in self.events.iter().enumerate() {
|
||||
if let Event::Remove = event {
|
||||
self.board.set(self.marbles[i], Tile::Blank);
|
||||
}
|
||||
}
|
||||
|
||||
// resolve triggers
|
||||
let mut triggers_activated = Vec::new();
|
||||
for event in &mut self.events {
|
||||
if let Event::Trigger(new_pos, dir, trigger_pos) = event {
|
||||
triggers_activated.push(*trigger_pos);
|
||||
*event = Event::MoveTo(*new_pos, *dir);
|
||||
}
|
||||
}
|
||||
|
||||
// resolve collisions (multiple marbles entering the same space)
|
||||
for i in 0..(self.events.len() - 1) {
|
||||
let event = self.events[i];
|
||||
if let Event::MoveTo(new_pos, _dir) = event {
|
||||
for other_event in &mut self.events[(i + 1)..] {
|
||||
if let Event::MoveTo(other_pos, _other_dir) = other_event {
|
||||
// todo: maybe sort by direction so the sucessful direction is consistent
|
||||
if other_pos == &new_pos {
|
||||
*other_event = Event::Stay;
|
||||
match front_tile {
|
||||
Tile::Marble {
|
||||
value: _,
|
||||
dir: other_dir,
|
||||
} => {
|
||||
if other_dir != dir {
|
||||
// this marble is facing another marble, and will therefore definitely bounce
|
||||
will_reverse_direction[i] = true;
|
||||
// the other marble will bounce too, either
|
||||
let other_index =
|
||||
self.marbles.iter().position(|m| *m == front_pos).unwrap();
|
||||
let influence = &mut influenced_direction[other_index];
|
||||
*influence = match *influence {
|
||||
DirInfluence::None => DirInfluence::One(dir),
|
||||
DirInfluence::One(_) => DirInfluence::Multiple,
|
||||
DirInfluence::Multiple => DirInfluence::Multiple,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolve movement
|
||||
for (i, &event) in self.events.iter().enumerate() {
|
||||
if let Event::Remove = event {
|
||||
continue;
|
||||
}
|
||||
let marble = self.board.get_mut(self.marbles[i]).unwrap();
|
||||
let Tile::Marble { value, dir } = marble else {
|
||||
panic!("invalid marble");
|
||||
};
|
||||
match event {
|
||||
Event::MoveTo(new_pos, new_dir) => {
|
||||
let mut value = *value;
|
||||
self.board.set(self.marbles[i], Tile::Blank);
|
||||
self.marbles[i] = new_pos;
|
||||
let new_tile = self.board.get_mut(new_pos).unwrap();
|
||||
if let Tile::Digit(n) = new_tile {
|
||||
value = value.wrapping_mul(10).wrapping_add(*n as MarbleValue);
|
||||
Tile::Arrow(arrow_dir) => {
|
||||
if arrow_dir == dir.opposite() {
|
||||
// bounce on a reverse facing arrow
|
||||
will_reverse_direction[i] = true;
|
||||
}
|
||||
*new_tile = Tile::Marble {
|
||||
value,
|
||||
dir: new_dir,
|
||||
};
|
||||
}
|
||||
Event::Bounce(_other, new_dir) => *dir = new_dir,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// resolve deletions of marbles
|
||||
for (i, event) in self.events.iter().enumerate().rev() {
|
||||
if let Event::Remove = event {
|
||||
self.marbles.remove(i);
|
||||
// apply all direct bounces
|
||||
for (i, &pos) in self.marbles.iter().enumerate() {
|
||||
let Some(Tile::Marble { value: _, dir }) = self.board.get_mut(pos) else {
|
||||
unreachable!()
|
||||
};
|
||||
if will_reverse_direction[i] {
|
||||
*dir = dir.opposite();
|
||||
} else {
|
||||
if let DirInfluence::One(new_dir) = influenced_direction[i] {
|
||||
*dir = new_dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut claim_positions = Vec::new();
|
||||
// mark claims to figure out what spaces can be moved to
|
||||
for &pos in &self.marbles {
|
||||
let Some(Tile::Marble { value: _, dir }) = self.board.get(pos) else {
|
||||
unreachable!()
|
||||
};
|
||||
let front_pos = dir.step(pos);
|
||||
let Some(front_tile) = self.board.get_mut(front_pos) else {
|
||||
continue;
|
||||
};
|
||||
if let Tile::Open(_type, claim) = front_tile {
|
||||
*claim = match claim {
|
||||
MarbleTarget::Free => {
|
||||
claim_positions.push(front_pos);
|
||||
MarbleTarget::Claimed
|
||||
}
|
||||
MarbleTarget::ClaimedIndirect => MarbleTarget::Claimed,
|
||||
MarbleTarget::BlockedIndirect => MarbleTarget::Claimed,
|
||||
MarbleTarget::Claimed => MarbleTarget::Blocked,
|
||||
MarbleTarget::Blocked => MarbleTarget::Blocked,
|
||||
};
|
||||
} else {
|
||||
let target_pos;
|
||||
match front_tile {
|
||||
Tile::Arrow(d) => {
|
||||
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,
|
||||
}
|
||||
let Some(target_tile) = self.board.get_mut(target_pos) else {
|
||||
continue;
|
||||
};
|
||||
if let Tile::Open(_type, claim) = target_tile {
|
||||
*claim = match claim {
|
||||
MarbleTarget::Free => {
|
||||
claim_positions.push(front_pos);
|
||||
MarbleTarget::ClaimedIndirect
|
||||
}
|
||||
MarbleTarget::ClaimedIndirect => MarbleTarget::BlockedIndirect,
|
||||
MarbleTarget::BlockedIndirect => MarbleTarget::BlockedIndirect,
|
||||
MarbleTarget::Claimed => MarbleTarget::Claimed,
|
||||
MarbleTarget::Blocked => MarbleTarget::Blocked,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut triggers_activated = Vec::new();
|
||||
let mut removed_marbles = Vec::new();
|
||||
// move marbles
|
||||
for (i, pos) in self.marbles.iter_mut().enumerate() {
|
||||
let Some(Tile::Marble { value, dir }) = self.board.get(*pos) else {
|
||||
unreachable!()
|
||||
};
|
||||
let front_pos = dir.step(*pos);
|
||||
let Some(front_tile) = self.board.get_mut(front_pos) else {
|
||||
continue;
|
||||
};
|
||||
if let Tile::Open(space_type, claim_state) = front_tile {
|
||||
if *claim_state == MarbleTarget::Claimed {
|
||||
let value = match space_type {
|
||||
OpenTile::Blank => value,
|
||||
OpenTile::Digit(n) => {
|
||||
value.wrapping_mul(10).wrapping_add(*n as MarbleValue)
|
||||
}
|
||||
};
|
||||
self.board.set(*pos, Tile::default());
|
||||
self.board.set(front_pos, Tile::Marble { value, dir });
|
||||
*pos = front_pos;
|
||||
} else if *claim_state != MarbleTarget::Free {
|
||||
// (Free means a marble was just here but moved earlier this tick)
|
||||
// bounce on failed direct movement
|
||||
self.board.set(
|
||||
*pos,
|
||||
Tile::Marble {
|
||||
value,
|
||||
dir: dir.opposite(),
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let target_pos;
|
||||
let mut is_trigger = false;
|
||||
let mut new_dir = dir;
|
||||
match front_tile {
|
||||
Tile::Arrow(d) => {
|
||||
target_pos = d.step(front_pos);
|
||||
new_dir = *d;
|
||||
}
|
||||
Tile::Mirror(m) => {
|
||||
new_dir = m.new_dir(dir);
|
||||
target_pos = new_dir.step(front_pos);
|
||||
}
|
||||
Tile::Powerable(PTile::Trigger, _) => {
|
||||
is_trigger = true;
|
||||
target_pos = dir.step(front_pos);
|
||||
}
|
||||
Tile::Powerable(PTile::Bag, _) => {
|
||||
removed_marbles.push(i);
|
||||
self.board.set(*pos, Tile::default());
|
||||
continue;
|
||||
}
|
||||
Tile::Powerable(PTile::IO, _) => {
|
||||
removed_marbles.push(i);
|
||||
self.board.set(*pos, Tile::default());
|
||||
self.output.push(value as u8);
|
||||
continue;
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
let Some(target_tile) = self.board.get_mut(target_pos) else {
|
||||
continue;
|
||||
};
|
||||
if let Tile::Open(space_type, MarbleTarget::ClaimedIndirect) = target_tile {
|
||||
let value = match space_type {
|
||||
OpenTile::Blank => value,
|
||||
OpenTile::Digit(n) => {
|
||||
value.wrapping_mul(10).wrapping_add(*n as MarbleValue)
|
||||
}
|
||||
};
|
||||
self.board.set(*pos, Tile::default());
|
||||
self.board.set(
|
||||
target_pos,
|
||||
Tile::Marble {
|
||||
value,
|
||||
dir: new_dir,
|
||||
},
|
||||
);
|
||||
*pos = target_pos;
|
||||
if is_trigger {
|
||||
triggers_activated.push(front_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for pos in claim_positions {
|
||||
if let Some(Tile::Open(_, claim_state)) = self.board.get_mut(pos) {
|
||||
*claim_state = MarbleTarget::Free;
|
||||
}
|
||||
}
|
||||
|
||||
// remove marbles
|
||||
for &i in removed_marbles.iter().rev() {
|
||||
self.marbles.swap_remove(i);
|
||||
}
|
||||
|
||||
// process triggers
|
||||
self.flipper_events.clear();
|
||||
for pos in triggers_activated {
|
||||
let Some(Tile::Powerable(PTile::Trigger, state)) = self.board.get_mut(pos) else {
|
||||
unreachable!()
|
||||
};
|
||||
self.powered.push(pos);
|
||||
*state = true;
|
||||
for dir in Direction::ALL {
|
||||
self.propagate_power(dir, dir.step(pos));
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ impl Board {
|
|||
rows.push(tiles);
|
||||
}
|
||||
for line in &mut rows {
|
||||
line.resize(width, Tile::Blank);
|
||||
line.resize(width, Tile::default());
|
||||
}
|
||||
|
||||
Board::new(rows)
|
||||
|
@ -44,7 +44,7 @@ impl Board {
|
|||
}
|
||||
|
||||
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 {
|
||||
rows,
|
||||
width,
|
||||
|
@ -65,7 +65,7 @@ impl Board {
|
|||
for row in &self.rows {
|
||||
for tile in row {
|
||||
match tile {
|
||||
Tile::Blank | Tile::Block => (),
|
||||
Tile::Open(OpenTile::Blank, _) | Tile::Block => (),
|
||||
_ => sum += 1,
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ 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 tile == &Tile::Blank {
|
||||
if let Tile::Open(OpenTile::Blank, _) = tile{
|
||||
return Some(tile);
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ impl Board {
|
|||
if p.x < 0 {
|
||||
let len = p.x.unsigned_abs() as usize;
|
||||
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);
|
||||
*row = new_row;
|
||||
}
|
||||
|
@ -156,21 +156,21 @@ impl Board {
|
|||
} else if p.x as usize >= self.width {
|
||||
let new_width = p.x as usize + 1;
|
||||
for row in &mut self.rows {
|
||||
row.resize(new_width, Tile::Blank);
|
||||
row.resize(new_width, Tile::default());
|
||||
}
|
||||
self.width = new_width;
|
||||
}
|
||||
|
||||
if p.y < 0 {
|
||||
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);
|
||||
self.rows = new_rows;
|
||||
offset_y = len;
|
||||
self.height += len;
|
||||
} else if p.y as usize >= self.height {
|
||||
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;
|
||||
}
|
||||
(offset_x as PosInt, offset_y as PosInt)
|
||||
|
|
|
@ -2,21 +2,31 @@ use crate::marble_engine::Pos;
|
|||
|
||||
pub type MarbleValue = u32;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Tile {
|
||||
#[default]
|
||||
Blank,
|
||||
Open(OpenTile, MarbleTarget),
|
||||
Block,
|
||||
Marble {
|
||||
value: MarbleValue,
|
||||
dir: Direction,
|
||||
},
|
||||
Digit(u8),
|
||||
Marble { value: MarbleValue, dir: Direction },
|
||||
Mirror(MirrorType),
|
||||
Arrow(Direction),
|
||||
Powerable(PTile, bool),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum MarbleTarget {
|
||||
Free,
|
||||
ClaimedIndirect,
|
||||
BlockedIndirect,
|
||||
Claimed,
|
||||
Blocked,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum OpenTile {
|
||||
Blank,
|
||||
Digit(u8),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum PTile {
|
||||
Trigger,
|
||||
|
@ -66,6 +76,12 @@ pub enum Direction {
|
|||
Right,
|
||||
}
|
||||
|
||||
impl Default for Tile {
|
||||
fn default() -> Self {
|
||||
Tile::Open(OpenTile::Blank, MarbleTarget::Free)
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
pub const fn from_char(c: char) -> Tile {
|
||||
match c {
|
||||
|
@ -95,19 +111,18 @@ impl Tile {
|
|||
'D' => Tile::Powerable(PTile::Math(MathOp::Div), false),
|
||||
'R' => Tile::Powerable(PTile::Math(MathOp::Rem), false),
|
||||
'B' => Tile::Powerable(PTile::Bag, false),
|
||||
d @ '0'..='9' => Tile::Digit(d as u8 - b'0'),
|
||||
d @ '0'..='9' => Tile::Open(OpenTile::Digit(d as u8 - b'0'), MarbleTarget::Free),
|
||||
'#' => Tile::Block,
|
||||
' ' => Tile::Blank,
|
||||
_ => Tile::Blank,
|
||||
_ => Tile::Open(OpenTile::Blank, MarbleTarget::Free),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_char(self) -> char {
|
||||
match self {
|
||||
Tile::Blank => ' ',
|
||||
Tile::Open(OpenTile::Blank, _) => ' ',
|
||||
Tile::Block => '#',
|
||||
Tile::Marble { value: _, dir: _ } => 'o',
|
||||
Tile::Digit(n) => (b'0' + n) as char,
|
||||
Tile::Open(OpenTile::Digit(n), _) => (b'0' + n) as char,
|
||||
Tile::Mirror(dir) => match dir {
|
||||
MirrorType::Forward => '/',
|
||||
MirrorType::Back => '\\',
|
||||
|
@ -146,23 +161,23 @@ impl Tile {
|
|||
}
|
||||
|
||||
pub fn is_blank(&self) -> bool {
|
||||
matches!(self, Tile::Blank)
|
||||
matches!(self, Tile::Open(OpenTile::Blank, _))
|
||||
}
|
||||
|
||||
pub fn read_value(&self) -> MarbleValue {
|
||||
match self {
|
||||
Tile::Marble { value, dir: _ } => *value,
|
||||
Tile::Digit(d) => *d as MarbleValue,
|
||||
Tile::Open(OpenTile::Digit(d), _) => *d as MarbleValue,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn texture(&self) -> String {
|
||||
match self {
|
||||
Tile::Blank => "",
|
||||
Tile::Open(OpenTile::Blank, _) => "",
|
||||
Tile::Block => "block",
|
||||
Tile::Marble { value: _, dir: _ } => "marble",
|
||||
Tile::Digit(n) => return format!("tile_digit_{n}"),
|
||||
Tile::Open(OpenTile::Digit(n), _) => return format!("tile_digit_{n}"),
|
||||
Tile::Mirror(mirror) => mirror.texture_name(),
|
||||
Tile::Arrow(dir) => dir.arrow_tile_texture_name(),
|
||||
Tile::Powerable(tile, state) => {
|
||||
|
|
Loading…
Add table
Reference in a new issue