add sprites for most tiles
BIN
assets/tiles/add_off.png
Normal file
After Width: | Height: | Size: 176 B |
BIN
assets/tiles/add_on.png
Normal file
After Width: | Height: | Size: 176 B |
BIN
assets/tiles/bag_off.png
Normal file
After Width: | Height: | Size: 200 B |
BIN
assets/tiles/bag_on.png
Normal file
After Width: | Height: | Size: 190 B |
BIN
assets/tiles/div_off.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
assets/tiles/div_on.png
Normal file
After Width: | Height: | Size: 195 B |
BIN
assets/tiles/down.png
Normal file
After Width: | Height: | Size: 198 B |
BIN
assets/tiles/eq_off.png
Normal file
After Width: | Height: | Size: 158 B |
BIN
assets/tiles/eq_on.png
Normal file
After Width: | Height: | Size: 158 B |
BIN
assets/tiles/flipper_off.png
Normal file
After Width: | Height: | Size: 209 B |
BIN
assets/tiles/flipper_on.png
Normal file
After Width: | Height: | Size: 200 B |
BIN
assets/tiles/gt_off.png
Normal file
After Width: | Height: | Size: 198 B |
BIN
assets/tiles/gt_on.png
Normal file
After Width: | Height: | Size: 198 B |
BIN
assets/tiles/input_off.png
Normal file
After Width: | Height: | Size: 196 B |
BIN
assets/tiles/input_on.png
Normal file
After Width: | Height: | Size: 196 B |
BIN
assets/tiles/left.png
Normal file
After Width: | Height: | Size: 211 B |
BIN
assets/tiles/lt_off.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
assets/tiles/lt_on.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
assets/tiles/mul_off.png
Normal file
After Width: | Height: | Size: 209 B |
BIN
assets/tiles/mul_on.png
Normal file
After Width: | Height: | Size: 210 B |
BIN
assets/tiles/neq_off.png
Normal file
After Width: | Height: | Size: 206 B |
BIN
assets/tiles/neq_on.png
Normal file
After Width: | Height: | Size: 207 B |
BIN
assets/tiles/output_off.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
assets/tiles/output_on.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
assets/tiles/rem_off.png
Normal file
After Width: | Height: | Size: 215 B |
BIN
assets/tiles/rem_on.png
Normal file
After Width: | Height: | Size: 215 B |
BIN
assets/tiles/right.png
Normal file
After Width: | Height: | Size: 220 B |
BIN
assets/tiles/sub_off.png
Normal file
After Width: | Height: | Size: 177 B |
BIN
assets/tiles/sub_on.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
assets/tiles/trigger_off.png
Normal file
After Width: | Height: | Size: 147 B |
BIN
assets/tiles/trigger_on.png
Normal file
After Width: | Height: | Size: 148 B |
BIN
assets/tiles/up.png
Normal file
After Width: | Height: | Size: 205 B |
BIN
assets/tiles/wire_cross_off.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
assets/tiles/wire_cross_on.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
assets/tiles/wire_horizontal_off.png
Normal file
After Width: | Height: | Size: 126 B |
BIN
assets/tiles/wire_horizontal_on.png
Normal file
After Width: | Height: | Size: 125 B |
BIN
assets/tiles/wire_vertical_off.png
Normal file
After Width: | Height: | Size: 136 B |
BIN
assets/tiles/wire_vertical_on.png
Normal file
After Width: | Height: | Size: 127 B |
26
src/main.rs
|
@ -1,4 +1,7 @@
|
||||||
use std::fs::read_to_string;
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs::{read_dir, read_to_string},
|
||||||
|
};
|
||||||
|
|
||||||
use marble_engine::parse;
|
use marble_engine::parse;
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
@ -6,13 +9,28 @@ use raylib::prelude::*;
|
||||||
mod marble_engine;
|
mod marble_engine;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (mut rl, thread) = raylib::init().resizable().title("good window title").build();
|
let (mut rl, thread) = raylib::init()
|
||||||
|
.resizable()
|
||||||
|
.title("good window title")
|
||||||
|
.build();
|
||||||
rl.set_target_fps(60);
|
rl.set_target_fps(60);
|
||||||
|
|
||||||
let board = parse(&read_to_string("boards/adder.mbl").unwrap());
|
let board = parse(&read_to_string("boards/counter.mbl").unwrap());
|
||||||
let mut pos_offset = Vector2::zero();
|
let mut pos_offset = Vector2::zero();
|
||||||
let mut machine = marble_engine::Machine::new(board, "Vec::new()".bytes().collect());
|
let mut machine = marble_engine::Machine::new(board, "Vec::new()".bytes().collect());
|
||||||
|
|
||||||
|
let mut textures: HashMap<String, Texture2D> = HashMap::new();
|
||||||
|
for d in read_dir("assets/tiles").unwrap().flatten() {
|
||||||
|
let name = d.file_name();
|
||||||
|
if d.path().is_file() {
|
||||||
|
let name = name.to_string_lossy();
|
||||||
|
let texture = rl
|
||||||
|
.load_texture(&thread, &format!("assets/tiles/{name}"))
|
||||||
|
.unwrap();
|
||||||
|
textures.insert(name.to_string(), texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while !rl.window_should_close() {
|
while !rl.window_should_close() {
|
||||||
if rl.is_key_pressed(KeyboardKey::KEY_SPACE) {
|
if rl.is_key_pressed(KeyboardKey::KEY_SPACE) {
|
||||||
machine.step();
|
machine.step();
|
||||||
|
@ -27,7 +45,7 @@ fn main() {
|
||||||
let mut d = rl.begin_drawing(&thread);
|
let mut d = rl.begin_drawing(&thread);
|
||||||
d.clear_background(Color::new(64, 64, 64, 255));
|
d.clear_background(Color::new(64, 64, 64, 255));
|
||||||
|
|
||||||
machine.draw(&mut d, pos_offset);
|
machine.draw(&mut d, &textures, pos_offset);
|
||||||
|
|
||||||
d.draw_fps(2, 2);
|
d.draw_fps(2, 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use raylib::{drawing::RaylibDrawHandle, math::Vector2};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use raylib::{drawing::RaylibDrawHandle, math::Vector2, texture::Texture2D};
|
||||||
|
|
||||||
mod board;
|
mod board;
|
||||||
mod tile;
|
mod tile;
|
||||||
|
@ -48,14 +50,19 @@ impl Machine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self, d: &mut RaylibDrawHandle, offset: Vector2) {
|
pub fn draw(
|
||||||
|
&self,
|
||||||
|
d: &mut RaylibDrawHandle,
|
||||||
|
textures: &HashMap<String, Texture2D>,
|
||||||
|
offset: Vector2,
|
||||||
|
) {
|
||||||
let tile_size = 32;
|
let tile_size = 32;
|
||||||
for x in 0..self.board.width() {
|
for x in 0..self.board.width() {
|
||||||
for y in 0..self.board.height() {
|
for y in 0..self.board.height() {
|
||||||
if let Some(tile) = self.board.get((x, y).into()) {
|
if let Some(tile) = self.board.get((x, y).into()) {
|
||||||
let px = x as i32 * tile_size + offset.x as i32 + tile_size / 2;
|
let px = x as i32 * tile_size + offset.x as i32 + tile_size / 2;
|
||||||
let py = y as i32 * tile_size + offset.y as i32 + tile_size / 2;
|
let py = y as i32 * tile_size + offset.y as i32 + tile_size / 2;
|
||||||
tile.draw(d, px, py, tile_size);
|
tile.draw(d, textures, px, py, tile_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,10 +73,8 @@ impl Machine {
|
||||||
// reset wires
|
// reset wires
|
||||||
for y in 0..self.board.height() {
|
for y in 0..self.board.height() {
|
||||||
for x in 0..self.board.width() {
|
for x in 0..self.board.width() {
|
||||||
match self.board.get_mut((x, y).into()) {
|
if let Tile::Powerable(_, state) = self.board.get_mut((x, y).into()) {
|
||||||
Tile::Powerable(_, state) => *state = false,
|
*state = false;
|
||||||
Tile::Trigger(state) => *state = false,
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +125,7 @@ impl Machine {
|
||||||
*other_dir = dir;
|
*other_dir = dir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Tile::Trigger(state) => {
|
Tile::Powerable(PTile::Trigger, state) => {
|
||||||
*state = true;
|
*state = true;
|
||||||
triggers.push(next_pos);
|
triggers.push(next_pos);
|
||||||
let far_pos = dir.step(next_pos);
|
let far_pos = dir.step(next_pos);
|
||||||
|
@ -191,6 +196,9 @@ impl Machine {
|
||||||
let tile = self.board.get_mut(pos);
|
let tile = self.board.get_mut(pos);
|
||||||
let front_pos = dir.step(pos);
|
let front_pos = dir.step(pos);
|
||||||
if let Tile::Powerable(tile, state) = tile {
|
if let Tile::Powerable(tile, state) = tile {
|
||||||
|
if let PTile::Trigger = tile {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if *state {
|
if *state {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -285,6 +293,7 @@ impl Machine {
|
||||||
self.propagate_power(dir, dir.step(pos));
|
self.propagate_power(dir, dir.step(pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PTile::Trigger => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,7 +325,7 @@ pub fn parse(source: &str) -> Board {
|
||||||
value: 0,
|
value: 0,
|
||||||
dir: Direction::Down,
|
dir: Direction::Down,
|
||||||
},
|
},
|
||||||
'*' => Tile::Trigger(false),
|
'*' => Tile::Powerable(PTile::Trigger, false),
|
||||||
'-' => Tile::Powerable(PTile::Wire(WireType::Horizontal), false),
|
'-' => Tile::Powerable(PTile::Wire(WireType::Horizontal), false),
|
||||||
'|' => Tile::Powerable(PTile::Wire(WireType::Vertical), false),
|
'|' => Tile::Powerable(PTile::Wire(WireType::Vertical), false),
|
||||||
'+' => Tile::Powerable(PTile::Wire(WireType::Cross), false),
|
'+' => Tile::Powerable(PTile::Wire(WireType::Cross), false),
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use raylib::{
|
use raylib::{
|
||||||
color::Color,
|
color::Color,
|
||||||
drawing::{RaylibDraw, RaylibDrawHandle},
|
drawing::{RaylibDraw, RaylibDrawHandle},
|
||||||
ffi::Rectangle,
|
ffi::Rectangle,
|
||||||
math::Vector2,
|
math::Vector2,
|
||||||
|
texture::Texture2D,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::board::Pos;
|
use super::board::Pos;
|
||||||
|
@ -19,7 +22,6 @@ pub enum Tile {
|
||||||
value: MarbleValue,
|
value: MarbleValue,
|
||||||
dir: Direction,
|
dir: Direction,
|
||||||
},
|
},
|
||||||
Trigger(bool),
|
|
||||||
Digit(u8),
|
Digit(u8),
|
||||||
Mirror(MirrorType),
|
Mirror(MirrorType),
|
||||||
Arrow(Direction),
|
Arrow(Direction),
|
||||||
|
@ -28,6 +30,7 @@ pub enum Tile {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum PTile {
|
pub enum PTile {
|
||||||
|
Trigger,
|
||||||
Wire(WireType),
|
Wire(WireType),
|
||||||
Gate(GateType),
|
Gate(GateType),
|
||||||
Math(MathOp),
|
Math(MathOp),
|
||||||
|
@ -88,16 +91,73 @@ impl Tile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self, d: &mut RaylibDrawHandle, x: i32, y: i32, size: i32) {
|
pub fn draw(
|
||||||
let up = y - size / 2 + 1;
|
&self,
|
||||||
let down = y + size / 2 - 1;
|
d: &mut RaylibDrawHandle,
|
||||||
let left = x - size / 2 + 1;
|
textures: &HashMap<String, Texture2D>,
|
||||||
let right = x + size / 2 - 1;
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
size: i32,
|
||||||
|
) {
|
||||||
|
let tex_name = match self {
|
||||||
|
Tile::Blank => "",
|
||||||
|
Tile::Block => "",
|
||||||
|
Tile::Comment(_) => "",
|
||||||
|
Tile::Marble { value, dir } => "todo!()",
|
||||||
|
Tile::Digit(_) => "",
|
||||||
|
Tile::Mirror(_) => "",
|
||||||
|
Tile::Arrow(dir) => match dir {
|
||||||
|
Direction::Up => "up",
|
||||||
|
Direction::Down => "down",
|
||||||
|
Direction::Left => "left",
|
||||||
|
Direction::Right => "right",
|
||||||
|
},
|
||||||
|
Tile::Powerable(tile, state) => {
|
||||||
|
let t = match tile {
|
||||||
|
PTile::Trigger => "trigger",
|
||||||
|
PTile::Wire(wire) => match wire {
|
||||||
|
WireType::Vertical => "wire_vertical",
|
||||||
|
WireType::Horizontal => "wire_horizontal",
|
||||||
|
WireType::Cross => "wire_cross",
|
||||||
|
},
|
||||||
|
PTile::Gate(gate) => match gate {
|
||||||
|
GateType::LessThan => "lt",
|
||||||
|
GateType::GreaterThan => "gt",
|
||||||
|
GateType::Equal => "eq",
|
||||||
|
GateType::NotEqual => "neq",
|
||||||
|
},
|
||||||
|
PTile::Math(math_op) => match math_op {
|
||||||
|
MathOp::Add => "add",
|
||||||
|
MathOp::Sub => "sub",
|
||||||
|
MathOp::Mul => "mul",
|
||||||
|
MathOp::Div => "div",
|
||||||
|
MathOp::Rem => "rem",
|
||||||
|
},
|
||||||
|
PTile::Bag => "bag",
|
||||||
|
PTile::Flipper => "flipper",
|
||||||
|
PTile::Input => "input",
|
||||||
|
PTile::Output => "output",
|
||||||
|
};
|
||||||
|
&format!("{t}_{}", if *state { "on" } else { "off" })
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let tex_name = format!("{tex_name}.png");
|
||||||
|
if let Some(texture) = textures.get(&tex_name) {
|
||||||
|
d.draw_texture_ex(
|
||||||
|
texture,
|
||||||
|
Vector2::new((x - size / 2) as f32, (y - size / 2) as f32),
|
||||||
|
0.0,
|
||||||
|
2.0,
|
||||||
|
Color::WHITE,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Tile::Blank => (),
|
Tile::Blank => (),
|
||||||
Tile::Block => d.draw_rectangle(x - size / 2, y - size / 2, size, size, Color::DIMGRAY),
|
Tile::Block => d.draw_rectangle(x - size / 2, y - size / 2, size, size, Color::DIMGRAY),
|
||||||
Tile::Comment(c) => {
|
Tile::Comment(c) => {
|
||||||
d.draw_rectangle(x-size/2, y-size/2, size, size, Color::DIMGRAY);
|
d.draw_rectangle(x - size / 2, y - size / 2, size, size, Color::DIMGRAY);
|
||||||
d.draw_text(&format!("{}", *c as char), x - 10, y - 10, 20, Color::WHITE);
|
d.draw_text(&format!("{}", *c as char), x - 10, y - 10, 20, Color::WHITE);
|
||||||
}
|
}
|
||||||
Tile::Marble { value, dir } => {
|
Tile::Marble { value, dir } => {
|
||||||
|
@ -110,10 +170,6 @@ impl Tile {
|
||||||
Color::MAGENTA,
|
Color::MAGENTA,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Tile::Trigger(state) => {
|
|
||||||
let color = if *state { Color::RED } else { Color::LIGHTGRAY };
|
|
||||||
d.draw_rectangle(x - size / 4, y - size / 4, size / 2, size / 2, color)
|
|
||||||
}
|
|
||||||
Tile::Digit(n) => {
|
Tile::Digit(n) => {
|
||||||
d.draw_text(&String::from(*n as char), x - 10, y - 10, 20, Color::ORANGE)
|
d.draw_text(&String::from(*n as char), x - 10, y - 10, 20, Color::ORANGE)
|
||||||
}
|
}
|
||||||
|
@ -132,45 +188,7 @@ impl Tile {
|
||||||
};
|
};
|
||||||
d.draw_rectangle_pro(rec, Vector2::new(width, height) * 0.5, rot, Color::CYAN);
|
d.draw_rectangle_pro(rec, Vector2::new(width, height) * 0.5, rot, Color::CYAN);
|
||||||
}
|
}
|
||||||
Tile::Arrow(dir) => {
|
_ => d.draw_rectangle(x - size / 2, y - size / 2, size, size, Color::YELLOW),
|
||||||
let up = Vector2::from((x as f32, up as f32));
|
|
||||||
let down = Vector2::from((x as f32, down as f32));
|
|
||||||
let left = Vector2::from((left as f32, y as f32));
|
|
||||||
let right = Vector2::from((right as f32, y as f32));
|
|
||||||
let (v1, v2, v3) = match dir {
|
|
||||||
Direction::Up => (up, left, right),
|
|
||||||
Direction::Down => (down, right, left),
|
|
||||||
Direction::Left => (left, down, up),
|
|
||||||
Direction::Right => (right, up, down),
|
|
||||||
};
|
|
||||||
d.draw_triangle(v1, v2, v3, Color::CYAN);
|
|
||||||
}
|
|
||||||
Tile::Powerable(tile, state) => {
|
|
||||||
let color = if *state { Color::RED } else { Color::LIGHTGRAY };
|
|
||||||
match tile {
|
|
||||||
PTile::Bag => {
|
|
||||||
d.draw_circle(x, y, size as f32 * 0.4, color);
|
|
||||||
d.draw_circle(x, y, size as f32 * 0.2, Color::GRAY);
|
|
||||||
}
|
|
||||||
PTile::Wire(wire) => {
|
|
||||||
let vertical = !matches!(wire, WireType::Horizontal);
|
|
||||||
let horizontal = !matches!(wire, WireType::Vertical);
|
|
||||||
if vertical {
|
|
||||||
d.draw_rectangle(x - size / 8, y - size / 2, size / 4, size, color)
|
|
||||||
}
|
|
||||||
if horizontal {
|
|
||||||
d.draw_rectangle(x - size / 2, y - size / 8, size, size / 4, color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// PTile::Gate(_) => todo!(),
|
|
||||||
// PTile::Math(_) => todo!(),
|
|
||||||
// PTile::Print => todo!(),
|
|
||||||
// PTile::Input => todo!(),
|
|
||||||
// PTile::Flip => todo!(),
|
|
||||||
_ => d.draw_rectangle(x - size / 2, y - size / 2, size, size, color),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|