add comment storage to boards
This commit is contained in:
parent
0b9f41cbf6
commit
5c48b531f6
13 changed files with 219 additions and 113 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,4 +1,4 @@
|
||||||
/target
|
/target
|
||||||
/user
|
/user*
|
||||||
*.zip
|
*.zip
|
||||||
version.txt
|
version.txt
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use marble_machinations::{
|
use marble_machinations::{level::Level, marble_engine::Machine, solution::Solution};
|
||||||
level::Level,
|
|
||||||
marble_engine::{board::Board, Machine},
|
|
||||||
solution::Solution,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
aoc_2024_1a();
|
aoc_2024_1a();
|
||||||
|
@ -23,7 +19,7 @@ fn benchmark(level: &str, solution: &str) {
|
||||||
let solution: Solution = serde_json::from_str(solution).unwrap();
|
let solution: Solution = serde_json::from_str(solution).unwrap();
|
||||||
let cycle_count = solution.score.unwrap().cycles;
|
let cycle_count = solution.score.unwrap().cycles;
|
||||||
let mut machine = Machine::new_empty();
|
let mut machine = Machine::new_empty();
|
||||||
machine.set_board(Board::parse(&solution.board));
|
machine.set_grid(solution.board.grid);
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
for (n, stage) in level.stages().iter().enumerate() {
|
for (n, stage) in level.stages().iter().enumerate() {
|
||||||
machine.set_input(stage.input().as_bytes().to_owned());
|
machine.set_input(stage.input().as_bytes().to_owned());
|
||||||
|
|
|
@ -6,24 +6,21 @@ use std::{
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{marble_engine::board::Board, util::userdata_dir};
|
use crate::{board::Board, util::userdata_dir};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Blueprint {
|
pub struct Blueprint {
|
||||||
id: usize,
|
id: usize,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub board: String,
|
pub board: Board,
|
||||||
#[serde(skip, default)]
|
|
||||||
tile_board: Option<Board>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blueprint {
|
impl Blueprint {
|
||||||
pub fn new(content: &Board, id: usize) -> Self {
|
pub fn new(board: Board, id: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
name: format!("Blueprint {id}"),
|
name: format!("Blueprint {id}"),
|
||||||
board: content.serialize(),
|
board,
|
||||||
tile_board: Some(content.clone()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,17 +28,6 @@ impl Blueprint {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_board(&mut self) -> &Board {
|
|
||||||
if self.tile_board.is_none() {
|
|
||||||
self.tile_board = Some(Board::parse(&self.board));
|
|
||||||
}
|
|
||||||
self.tile_board.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_board(&self) -> Option<&Board> {
|
|
||||||
self.tile_board.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path(&self) -> PathBuf {
|
fn path(&self) -> PathBuf {
|
||||||
let dir = userdata_dir().join("blueprints");
|
let dir = userdata_dir().join("blueprints");
|
||||||
fs::create_dir_all(&dir).unwrap();
|
fs::create_dir_all(&dir).unwrap();
|
||||||
|
|
104
src/board.rs
Normal file
104
src/board.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::marble_engine::{
|
||||||
|
grid::{Grid, ResizeDeltas},
|
||||||
|
pos::Pos,
|
||||||
|
tile::Tile,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Comment {
|
||||||
|
text: String,
|
||||||
|
pos: Pos,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(from = "CompatBoard")]
|
||||||
|
pub struct Board {
|
||||||
|
pub grid: Grid,
|
||||||
|
pub comments: Vec<Comment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum CompatBoard {
|
||||||
|
V1(String),
|
||||||
|
V2 { grid: Grid, comments: Vec<Comment> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CompatBoard> for Board {
|
||||||
|
fn from(value: CompatBoard) -> Self {
|
||||||
|
match value {
|
||||||
|
CompatBoard::V1(string) => Self {
|
||||||
|
grid: Grid::parse(&string),
|
||||||
|
comments: Vec::new(),
|
||||||
|
},
|
||||||
|
CompatBoard::V2 { grid, comments } => Self { grid, comments },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Board {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
grid: Grid::new_single(Tile::BLANK),
|
||||||
|
comments: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
pub fn new(grid: Grid) -> Self {
|
||||||
|
Self {
|
||||||
|
grid,
|
||||||
|
comments: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn single_tile(tile: Tile) -> Self {
|
||||||
|
Self {
|
||||||
|
grid: Grid::new_single(tile),
|
||||||
|
comments: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rect(&self, pos: Pos, width: usize, height: usize) -> Self {
|
||||||
|
// TODO filter for comments in the area
|
||||||
|
let comments = self.comments.clone();
|
||||||
|
Self {
|
||||||
|
grid: self.grid.get_rect(pos, width, height),
|
||||||
|
comments,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn paste_board(&mut self, pos: Pos, board: &Board) {
|
||||||
|
// TODO remove overlapping comments
|
||||||
|
self.grid.paste_grid(pos, &board.grid);
|
||||||
|
self.comments.extend_from_slice(&board.comments);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow(&mut self, deltas: &ResizeDeltas) {
|
||||||
|
self.grid.grow(deltas);
|
||||||
|
// TODO adjust comments
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrink(&mut self, deltas: &ResizeDeltas) {
|
||||||
|
self.grid.shrink(deltas);
|
||||||
|
// TODO adjust comments
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_user_str(source: &str) -> Self {
|
||||||
|
serde_json::from_str(source).unwrap_or_else(|_| Self {
|
||||||
|
grid: Grid::parse(source),
|
||||||
|
comments: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.grid.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.grid.height()
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,9 @@ use raylib::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
blueprint::Blueprint,
|
blueprint::Blueprint,
|
||||||
|
board::Board,
|
||||||
level::Level,
|
level::Level,
|
||||||
marble_engine::{board::*, pos::*, tile::*, Machine},
|
marble_engine::{grid::*, pos::*, tile::*, Machine},
|
||||||
solution::*,
|
solution::*,
|
||||||
theme::*,
|
theme::*,
|
||||||
ui::*,
|
ui::*,
|
||||||
|
@ -141,7 +142,7 @@ impl Editor {
|
||||||
info_text.set_text(level.description());
|
info_text.set_text(level.description());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
source_board: Board::parse(&solution.board),
|
source_board: solution.board.clone(),
|
||||||
machine,
|
machine,
|
||||||
sim_state: SimState::Editing,
|
sim_state: SimState::Editing,
|
||||||
view_offset: Vector2::zero(),
|
view_offset: Vector2::zero(),
|
||||||
|
@ -280,7 +281,7 @@ impl Editor {
|
||||||
|
|
||||||
fn reset_machine(&mut self) {
|
fn reset_machine(&mut self) {
|
||||||
self.machine.reset();
|
self.machine.reset();
|
||||||
self.machine.set_board(self.source_board.clone());
|
self.machine.set_grid(self.source_board.grid.clone());
|
||||||
if let Some(i) = self.stage {
|
if let Some(i) = self.stage {
|
||||||
let bytes = self.level.stages()[i].input().as_bytes();
|
let bytes = self.level.stages()[i].input().as_bytes();
|
||||||
self.machine.set_input(bytes.to_owned());
|
self.machine.set_input(bytes.to_owned());
|
||||||
|
@ -320,7 +321,7 @@ impl Editor {
|
||||||
self.sim_state = SimState::Stepping;
|
self.sim_state = SimState::Stepping;
|
||||||
self.score = Some(Score {
|
self.score = Some(Score {
|
||||||
cycles: self.total_steps + self.machine.step_count(),
|
cycles: self.total_steps + self.machine.step_count(),
|
||||||
tiles: self.source_board.count_tiles(),
|
tiles: self.source_board.grid.count_tiles(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if !stage.output().as_bytes().starts_with(self.machine.output()) {
|
} else if !stage.output().as_bytes().starts_with(self.machine.output()) {
|
||||||
|
@ -397,7 +398,7 @@ impl Editor {
|
||||||
fn save_blueprint(&mut self, selection: (Pos, Pos)) {
|
fn save_blueprint(&mut self, selection: (Pos, Pos)) {
|
||||||
let board = self.get_selected_as_board(selection);
|
let board = self.get_selected_as_board(selection);
|
||||||
let id = get_free_id(&self.blueprints, Blueprint::id);
|
let id = get_free_id(&self.blueprints, Blueprint::id);
|
||||||
let mut blueprint = Blueprint::new(&board, id);
|
let mut blueprint = Blueprint::new(board, id);
|
||||||
if !self.new_blueprint_name.is_empty() {
|
if !self.new_blueprint_name.is_empty() {
|
||||||
blueprint.name.clone_from(&self.new_blueprint_name);
|
blueprint.name.clone_from(&self.new_blueprint_name);
|
||||||
}
|
}
|
||||||
|
@ -442,7 +443,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tile(&mut self, pos: Pos, tile: Tile) {
|
fn set_tile(&mut self, pos: Pos, tile: Tile) {
|
||||||
self.set_area(pos, Board::new_single(tile));
|
self.set_area(pos, Board::single_tile(tile));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, rl: &RaylibHandle) {
|
pub fn update(&mut self, rl: &RaylibHandle) {
|
||||||
|
@ -548,7 +549,7 @@ impl Editor {
|
||||||
if rl.is_key_down(KeyboardKey::KEY_LEFT_CONTROL) {
|
if rl.is_key_down(KeyboardKey::KEY_LEFT_CONTROL) {
|
||||||
if rl.is_key_pressed(KeyboardKey::KEY_V) {
|
if rl.is_key_pressed(KeyboardKey::KEY_V) {
|
||||||
if let Ok(text) = rl.get_clipboard_text() {
|
if let Ok(text) = rl.get_clipboard_text() {
|
||||||
let b = Board::parse(&text);
|
let b = Board::from_user_str(&text);
|
||||||
self.pasting_board = Some(b);
|
self.pasting_board = Some(b);
|
||||||
}
|
}
|
||||||
} else if rl.is_key_pressed(KeyboardKey::KEY_Z) {
|
} else if rl.is_key_pressed(KeyboardKey::KEY_Z) {
|
||||||
|
@ -563,17 +564,16 @@ impl Editor {
|
||||||
fn draw_board(&self, d: &mut RaylibDrawHandle, textures: &Textures) {
|
fn draw_board(&self, d: &mut RaylibDrawHandle, textures: &Textures) {
|
||||||
if self.sim_state == SimState::Editing {
|
if self.sim_state == SimState::Editing {
|
||||||
self.source_board
|
self.source_board
|
||||||
|
.grid
|
||||||
.draw(d, textures, self.view_offset, self.zoom);
|
.draw(d, textures, self.view_offset, self.zoom);
|
||||||
} else {
|
} else {
|
||||||
if self.machine.debug_subticks.is_empty() {
|
if self.machine.debug_subticks.is_empty() {
|
||||||
self.machine
|
self.machine
|
||||||
.board()
|
.grid()
|
||||||
.draw(d, textures, self.view_offset, self.zoom);
|
.draw(d, textures, self.view_offset, self.zoom);
|
||||||
} else {
|
} else {
|
||||||
let subframe = &self.machine.debug_subticks[self.machine.subtick_index];
|
let subframe = &self.machine.debug_subticks[self.machine.subtick_index];
|
||||||
subframe
|
subframe.grid.draw(d, textures, self.view_offset, self.zoom);
|
||||||
.board
|
|
||||||
.draw(d, textures, self.view_offset, self.zoom);
|
|
||||||
if let Some(pos) = subframe.pos {
|
if let Some(pos) = subframe.pos {
|
||||||
let p = self.pos_to_screen(pos.to_vec());
|
let p = self.pos_to_screen(pos.to_vec());
|
||||||
d.draw_texture_ex(textures.get("selection"), p, 0., self.zoom, Color::ORANGE);
|
d.draw_texture_ex(textures.get("selection"), p, 0., self.zoom, Color::ORANGE);
|
||||||
|
@ -1037,8 +1037,10 @@ impl Editor {
|
||||||
if simple_button((d, &self.mouse), 232, y, 40, 40) {
|
if simple_button((d, &self.mouse), 232, y, 40, 40) {
|
||||||
let min = selection.0.min(selection.1);
|
let min = selection.0.min(selection.1);
|
||||||
let max = selection.0.max(selection.1);
|
let max = selection.0.max(selection.1);
|
||||||
let board =
|
let board = Board::new(Grid::new_empty(
|
||||||
Board::new_empty((max.x - min.x) as usize + 1, (max.y - min.y) as usize + 1);
|
(max.x - min.x) as usize + 1,
|
||||||
|
(max.y - min.y) as usize + 1,
|
||||||
|
));
|
||||||
self.set_area(min, board);
|
self.set_area(min, board);
|
||||||
}
|
}
|
||||||
draw_scaled_texture(d, textures.get("eraser"), 236, y + 4, 2.);
|
draw_scaled_texture(d, textures.get("eraser"), 236, y + 4, 2.);
|
||||||
|
@ -1262,7 +1264,8 @@ impl Editor {
|
||||||
offset.x -= offset.x.rem(tile_size);
|
offset.x -= offset.x.rem(tile_size);
|
||||||
offset.y -= offset.y.rem(tile_size);
|
offset.y -= offset.y.rem(tile_size);
|
||||||
offset += view_offset;
|
offset += view_offset;
|
||||||
board.draw(d, textures, offset, self.zoom);
|
board.grid.draw(d, textures, offset, self.zoom);
|
||||||
|
// TODO draw comments
|
||||||
if self.mouse.left_click() {
|
if self.mouse.left_click() {
|
||||||
let tile_pos = (self.mouse.pos() - self.view_offset) / tile_size;
|
let tile_pos = (self.mouse.pos() - self.view_offset) / tile_size;
|
||||||
let tile_pos = Vector2::new(tile_pos.x.floor(), tile_pos.y.floor());
|
let tile_pos = Vector2::new(tile_pos.x.floor(), tile_pos.y.floor());
|
||||||
|
@ -1355,7 +1358,7 @@ impl Editor {
|
||||||
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));
|
||||||
let tile = self.source_board.get_or_blank(pos);
|
let tile = self.source_board.grid.get_or_blank(pos);
|
||||||
if !matches!(tile, Tile::Open(OpenTile::Digit(_), _)) {
|
if !matches!(tile, Tile::Open(OpenTile::Digit(_), _)) {
|
||||||
self.set_tile(pos, Tile::Open(OpenTile::Digit(0), Claim::Free));
|
self.set_tile(pos, Tile::Open(OpenTile::Digit(0), Claim::Free));
|
||||||
}
|
}
|
||||||
|
@ -1363,8 +1366,7 @@ impl Editor {
|
||||||
Tool::Blueprint => {
|
Tool::Blueprint => {
|
||||||
if self.mouse.pos().x > SIDEBAR_WIDTH as f32 {
|
if self.mouse.pos().x > SIDEBAR_WIDTH as f32 {
|
||||||
if let Some(bp) = self.blueprints.get(self.selected_blueprint) {
|
if let Some(bp) = self.blueprints.get(self.selected_blueprint) {
|
||||||
let board = bp.get_board().unwrap().clone();
|
self.set_area(pos, bp.board.clone());
|
||||||
self.set_area(pos, board);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1390,7 +1392,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Tool::Blueprint = self.active_tool {
|
if let Tool::Blueprint = self.active_tool {
|
||||||
if let Some(bp) = self.blueprints.get_mut(self.selected_blueprint) {
|
if let Some(bp) = self.blueprints.get(self.selected_blueprint) {
|
||||||
let view_offset = Vector2::new(
|
let view_offset = Vector2::new(
|
||||||
self.view_offset.x.rem(tile_size),
|
self.view_offset.x.rem(tile_size),
|
||||||
self.view_offset.y.rem(tile_size),
|
self.view_offset.y.rem(tile_size),
|
||||||
|
@ -1399,7 +1401,8 @@ impl Editor {
|
||||||
offset.x -= offset.x.rem(tile_size);
|
offset.x -= offset.x.rem(tile_size);
|
||||||
offset.y -= offset.y.rem(tile_size);
|
offset.y -= offset.y.rem(tile_size);
|
||||||
offset += view_offset;
|
offset += view_offset;
|
||||||
bp.convert_board().draw(d, textures, offset, self.zoom);
|
bp.board.grid.draw(d, textures, offset, self.zoom);
|
||||||
|
// TODO draw comments
|
||||||
}
|
}
|
||||||
if self.mouse.pos().x < SIDEBAR_WIDTH as f32 {
|
if self.mouse.pos().x < SIDEBAR_WIDTH as f32 {
|
||||||
if self.mouse.scroll() == Some(Scroll::Down)
|
if self.mouse.scroll() == Some(Scroll::Down)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::board::Board;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Chapter {
|
pub struct Chapter {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
@ -12,7 +14,7 @@ pub struct Level {
|
||||||
name: String,
|
name: String,
|
||||||
description: String,
|
description: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
init_board: Option<String>,
|
init_board: Option<Board>,
|
||||||
/// no stages means sandbox
|
/// no stages means sandbox
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
stages: Vec<Stage>,
|
stages: Vec<Stage>,
|
||||||
|
@ -61,7 +63,7 @@ impl Level {
|
||||||
self.stages.is_empty()
|
self.stages.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_board(&self) -> Option<String> {
|
pub fn init_board(&self) -> Option<Board> {
|
||||||
self.init_board.clone()
|
self.init_board.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod blueprint;
|
pub mod blueprint;
|
||||||
|
pub mod board;
|
||||||
pub mod editor;
|
pub mod editor;
|
||||||
pub mod level;
|
pub mod level;
|
||||||
pub mod marble_engine;
|
pub mod marble_engine;
|
||||||
|
|
|
@ -56,7 +56,6 @@ impl Game {
|
||||||
|
|
||||||
let levels = get_levels();
|
let levels = get_levels();
|
||||||
let solutions = get_solutions();
|
let solutions = get_solutions();
|
||||||
let selected_solution = 0;
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
levels,
|
levels,
|
||||||
|
@ -65,7 +64,7 @@ impl Game {
|
||||||
open_editor: None,
|
open_editor: None,
|
||||||
textures,
|
textures,
|
||||||
selected_level: 0,
|
selected_level: 0,
|
||||||
selected_solution,
|
selected_solution: 0,
|
||||||
delete_solution: None,
|
delete_solution: None,
|
||||||
editing_solution_name: false,
|
editing_solution_name: false,
|
||||||
level_desc_text: ShapedText::new(20),
|
level_desc_text: ShapedText::new(20),
|
||||||
|
@ -83,7 +82,7 @@ impl Game {
|
||||||
ExitState::ExitAndSave => {
|
ExitState::ExitAndSave => {
|
||||||
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
||||||
[self.selected_solution];
|
[self.selected_solution];
|
||||||
solution.board = editor.source_board().serialize();
|
solution.board = editor.source_board().clone();
|
||||||
solution.score = editor.score();
|
solution.score = editor.score();
|
||||||
solution.save();
|
solution.save();
|
||||||
self.open_editor = None;
|
self.open_editor = None;
|
||||||
|
@ -91,7 +90,7 @@ impl Game {
|
||||||
ExitState::Save => {
|
ExitState::Save => {
|
||||||
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
||||||
[self.selected_solution];
|
[self.selected_solution];
|
||||||
solution.board = editor.source_board().serialize();
|
solution.board = editor.source_board().clone();
|
||||||
solution.score = editor.score();
|
solution.score = editor.score();
|
||||||
solution.save();
|
solution.save();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
|
||||||
pub mod board;
|
pub mod grid;
|
||||||
pub mod pos;
|
pub mod pos;
|
||||||
pub mod tile;
|
pub mod tile;
|
||||||
use crate::{theme::TILE_TEXTURE_SIZE, ui::draw_usize_small, util::Textures};
|
use crate::{theme::TILE_TEXTURE_SIZE, ui::draw_usize_small, util::Textures};
|
||||||
use board::Board;
|
use grid::Grid;
|
||||||
use pos::*;
|
use pos::*;
|
||||||
use tile::*;
|
use tile::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Machine {
|
pub struct Machine {
|
||||||
board: Board,
|
grid: Grid,
|
||||||
marbles: Vec<Pos>,
|
marbles: Vec<Pos>,
|
||||||
powered: Vec<Pos>,
|
powered: Vec<Pos>,
|
||||||
input: Vec<u8>,
|
input: Vec<u8>,
|
||||||
|
@ -23,14 +23,14 @@ pub struct Machine {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DebugSubTick {
|
pub struct DebugSubTick {
|
||||||
pub board: Board,
|
pub grid: Grid,
|
||||||
pub pos: Option<Pos>,
|
pub pos: Option<Pos>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
pub fn new_empty() -> Self {
|
pub fn new_empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
board: Board::new_empty(5, 5),
|
grid: Grid::new_empty(5, 5),
|
||||||
marbles: Vec::new(),
|
marbles: Vec::new(),
|
||||||
powered: Vec::new(),
|
powered: Vec::new(),
|
||||||
input: Vec::new(),
|
input: Vec::new(),
|
||||||
|
@ -51,14 +51,14 @@ impl Machine {
|
||||||
self.subtick_index = 0;
|
self.subtick_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_board(&mut self, board: Board) {
|
pub fn set_grid(&mut self, board: Grid) {
|
||||||
self.marbles = board.get_marbles();
|
self.marbles = board.get_marbles();
|
||||||
self.powered.clear();
|
self.powered.clear();
|
||||||
self.board = board;
|
self.grid = board;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn board(&self) -> &Board {
|
pub fn grid(&self) -> &Grid {
|
||||||
&self.board
|
&self.grid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output(&self) -> &[u8] {
|
pub fn output(&self) -> &[u8] {
|
||||||
|
@ -93,7 +93,7 @@ impl Machine {
|
||||||
for marble in &self.marbles {
|
for marble in &self.marbles {
|
||||||
let x = marble.x;
|
let x = marble.x;
|
||||||
let y = marble.y;
|
let y = marble.y;
|
||||||
if let Some(tile) = self.board.get(*marble) {
|
if let Some(tile) = self.grid.get(*marble) {
|
||||||
let px = x as i32 * tile_size + offset.x as i32;
|
let px = x as i32 * tile_size + offset.x as i32;
|
||||||
let py = y as i32 * tile_size + offset.y as i32;
|
let py = y as i32 * tile_size + offset.y as i32;
|
||||||
if let Tile::Marble { value, dir } = tile {
|
if let Tile::Marble { value, dir } = tile {
|
||||||
|
@ -114,7 +114,7 @@ impl Machine {
|
||||||
self.subtick_index = 0;
|
self.subtick_index = 0;
|
||||||
self.debug_subticks.clear();
|
self.debug_subticks.clear();
|
||||||
self.debug_subticks.push(DebugSubTick {
|
self.debug_subticks.push(DebugSubTick {
|
||||||
board: self.board.clone(),
|
grid: self.grid.clone(),
|
||||||
pos: None,
|
pos: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ impl Machine {
|
||||||
let mut new_marbles = Vec::new();
|
let mut new_marbles = Vec::new();
|
||||||
// activate all powered machines
|
// activate all powered machines
|
||||||
for &pos in &self.powered {
|
for &pos in &self.powered {
|
||||||
match self.board.get_mut(pos) {
|
match self.grid.get_mut(pos) {
|
||||||
Some(Tile::Powerable(PTile::Comparator(_), board_power_state)) => {
|
Some(Tile::Powerable(PTile::Comparator(_), board_power_state)) => {
|
||||||
// already handled at the power propagation stage (end of sim step)
|
// already handled at the power propagation stage (end of sim step)
|
||||||
*board_power_state = Power::OFF;
|
*board_power_state = Power::OFF;
|
||||||
|
@ -138,7 +138,7 @@ impl Machine {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let front_pos = dir.step(pos);
|
let front_pos = dir.step(pos);
|
||||||
let Some(front_tile) = self.board.get_mut(front_pos) else {
|
let Some(front_tile) = self.grid.get_mut(front_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
// `machine` is being powered, in direction `dir`
|
// `machine` is being powered, in direction `dir`
|
||||||
|
@ -147,8 +147,8 @@ impl Machine {
|
||||||
if front_tile.is_blank() {
|
if front_tile.is_blank() {
|
||||||
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.grid.get_or_blank(pos_a).read_value();
|
||||||
let val_b = self.board.get_or_blank(pos_b).read_value();
|
let val_b = self.grid.get_or_blank(pos_b).read_value();
|
||||||
|
|
||||||
let value = match op {
|
let value = match op {
|
||||||
MathOp::Add => val_a.wrapping_add(val_b),
|
MathOp::Add => val_a.wrapping_add(val_b),
|
||||||
|
@ -215,11 +215,11 @@ impl Machine {
|
||||||
let mut influenced_direction = vec![DirInfluence::None; self.marbles.len()];
|
let mut influenced_direction = vec![DirInfluence::None; self.marbles.len()];
|
||||||
|
|
||||||
for (i, &pos) in self.marbles.iter().enumerate() {
|
for (i, &pos) in self.marbles.iter().enumerate() {
|
||||||
let Some(Tile::Marble { value: _, dir }) = self.board.get(pos) else {
|
let Some(Tile::Marble { value: _, dir }) = self.grid.get(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
let front_pos = dir.step(pos);
|
let front_pos = dir.step(pos);
|
||||||
let Some(front_tile) = self.board.get(front_pos) else {
|
let Some(front_tile) = self.grid.get(front_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match front_tile {
|
match front_tile {
|
||||||
|
@ -252,7 +252,7 @@ impl Machine {
|
||||||
}
|
}
|
||||||
// #### apply all direct bounces ####
|
// #### apply all direct bounces ####
|
||||||
for (i, &pos) in self.marbles.iter().enumerate() {
|
for (i, &pos) in self.marbles.iter().enumerate() {
|
||||||
let Some(Tile::Marble { value: _, dir }) = self.board.get_mut(pos) else {
|
let Some(Tile::Marble { value: _, dir }) = self.grid.get_mut(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
if will_reverse_direction[i] {
|
if will_reverse_direction[i] {
|
||||||
|
@ -266,7 +266,7 @@ impl Machine {
|
||||||
let mut claim_positions = Vec::new();
|
let mut claim_positions = Vec::new();
|
||||||
// prepare creating the new marbles
|
// prepare creating the new marbles
|
||||||
for &(pos, _val, _dir) in &new_marbles {
|
for &(pos, _val, _dir) in &new_marbles {
|
||||||
let Some(Tile::Open(OpenTile::Blank, claim)) = self.board.get_mut(pos) else {
|
let Some(Tile::Open(OpenTile::Blank, claim)) = self.grid.get_mut(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
if claim.claim_indirect() {
|
if claim.claim_indirect() {
|
||||||
|
@ -276,22 +276,22 @@ impl Machine {
|
||||||
// create new marbles
|
// create new marbles
|
||||||
// new marbles are past old_marbles index, so will not move this step
|
// new marbles are past old_marbles index, so will not move this step
|
||||||
for (pos, value, dir) in new_marbles {
|
for (pos, value, dir) in new_marbles {
|
||||||
let Some(Tile::Open(OpenTile::Blank, Claim::ClaimedIndirect)) = self.board.get_mut(pos)
|
let Some(Tile::Open(OpenTile::Blank, Claim::ClaimedIndirect)) = self.grid.get_mut(pos)
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
self.board.set(pos, Tile::Marble { value, dir });
|
self.grid.set(pos, Tile::Marble { value, dir });
|
||||||
self.marbles.push(pos);
|
self.marbles.push(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #### movement ####
|
// #### movement ####
|
||||||
// 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[..old_marbles] {
|
||||||
let Some(Tile::Marble { value: _, dir }) = self.board.get(pos) else {
|
let Some(Tile::Marble { value: _, dir }) = self.grid.get(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
let front_pos = dir.step(pos);
|
let front_pos = dir.step(pos);
|
||||||
let Some(front_tile) = self.board.get_mut(front_pos) else {
|
let Some(front_tile) = self.grid.get_mut(front_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if let Tile::Open(_type, claim) = front_tile {
|
if let Tile::Open(_type, claim) = front_tile {
|
||||||
|
@ -305,7 +305,7 @@ impl Machine {
|
||||||
Tile::Button(_) => dir.step(front_pos),
|
Tile::Button(_) => dir.step(front_pos),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
let Some(target_tile) = self.board.get_mut(target_pos) else {
|
let Some(target_tile) = self.grid.get_mut(target_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if let Tile::Open(_type, claim) = target_tile {
|
if let Tile::Open(_type, claim) = target_tile {
|
||||||
|
@ -319,15 +319,15 @@ impl Machine {
|
||||||
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[..old_marbles].iter_mut().enumerate() {
|
||||||
let Some(Tile::Marble { value, dir }) = self.board.get(*pos) else {
|
let Some(Tile::Marble { value, dir }) = self.grid.get(*pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
let front_pos = dir.step(*pos);
|
let front_pos = dir.step(*pos);
|
||||||
let Some(front_tile) = self.board.get_mut(front_pos) else {
|
let Some(front_tile) = self.grid.get_mut(front_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut move_to = |tile, target_pos, dir, board: &mut Board| {
|
let mut move_to = |tile, target_pos, dir, board: &mut Grid| {
|
||||||
let value = match tile {
|
let value = match tile {
|
||||||
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),
|
||||||
|
@ -339,11 +339,11 @@ 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 == Claim::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.grid);
|
||||||
} else if *claim_state != Claim::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.grid.set(
|
||||||
*pos,
|
*pos,
|
||||||
Tile::Marble {
|
Tile::Marble {
|
||||||
value,
|
value,
|
||||||
|
@ -379,11 +379,11 @@ impl Machine {
|
||||||
}
|
}
|
||||||
_ => continue,
|
_ => continue,
|
||||||
}
|
}
|
||||||
let Some(target_tile) = self.board.get_mut(target_pos) else {
|
let Some(target_tile) = self.grid.get_mut(target_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if let Tile::Open(space_type, Claim::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.grid);
|
||||||
if is_button {
|
if is_button {
|
||||||
self.powered.push(front_pos);
|
self.powered.push(front_pos);
|
||||||
}
|
}
|
||||||
|
@ -392,14 +392,14 @@ 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.grid.get_mut(pos) {
|
||||||
*claim_state = Claim::Free;
|
*claim_state = Claim::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.grid.set(self.marbles[i], Tile::BLANK);
|
||||||
self.marbles.swap_remove(i);
|
self.marbles.swap_remove(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +407,7 @@ impl Machine {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < self.powered.len() {
|
while i < self.powered.len() {
|
||||||
let pos = self.powered[i];
|
let pos = self.powered[i];
|
||||||
let Some(tile) = self.board.get_mut(pos) else {
|
let Some(tile) = self.grid.get_mut(pos) else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
match tile {
|
match tile {
|
||||||
|
@ -415,7 +415,7 @@ impl Machine {
|
||||||
*state = true;
|
*state = true;
|
||||||
for dir in Direction::ALL {
|
for dir in Direction::ALL {
|
||||||
let target_pos = dir.step(pos);
|
let target_pos = dir.step(pos);
|
||||||
match self.board.get_mut(target_pos) {
|
match self.grid.get_mut(target_pos) {
|
||||||
Some(Tile::Powerable(_, state)) => {
|
Some(Tile::Powerable(_, state)) => {
|
||||||
if !state.get_dir(dir) {
|
if !state.get_dir(dir) {
|
||||||
state.add_dir(dir);
|
state.add_dir(dir);
|
||||||
|
@ -434,7 +434,7 @@ impl Machine {
|
||||||
}
|
}
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
self.debug_subticks.push(DebugSubTick {
|
self.debug_subticks.push(DebugSubTick {
|
||||||
board: self.board.clone(),
|
grid: self.grid.clone(),
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -442,7 +442,7 @@ impl Machine {
|
||||||
*state = true;
|
*state = true;
|
||||||
for dir in wiretype.directions() {
|
for dir in wiretype.directions() {
|
||||||
let target_pos = dir.step(pos);
|
let target_pos = dir.step(pos);
|
||||||
match self.board.get_mut(target_pos) {
|
match self.grid.get_mut(target_pos) {
|
||||||
Some(Tile::Powerable(_, state)) => {
|
Some(Tile::Powerable(_, state)) => {
|
||||||
if !state.get_dir(*dir) {
|
if !state.get_dir(*dir) {
|
||||||
state.add_dir(*dir);
|
state.add_dir(*dir);
|
||||||
|
@ -461,7 +461,7 @@ impl Machine {
|
||||||
}
|
}
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
self.debug_subticks.push(DebugSubTick {
|
self.debug_subticks.push(DebugSubTick {
|
||||||
board: self.board.clone(),
|
grid: self.grid.clone(),
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -473,14 +473,14 @@ impl Machine {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let front_pos = dir.step(pos);
|
let front_pos = dir.step(pos);
|
||||||
let Some(front_tile) = self.board.get_mut(front_pos) else {
|
let Some(front_tile) = self.grid.get_mut(front_pos) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if matches!(front_tile, Tile::Wire(_, _) | Tile::Powerable(_, _)) {
|
if matches!(front_tile, Tile::Wire(_, _) | Tile::Powerable(_, _)) {
|
||||||
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.grid.get_or_blank(pos_a).read_value();
|
||||||
let val_b = self.board.get_or_blank(pos_b).read_value();
|
let val_b = self.grid.get_or_blank(pos_b).read_value();
|
||||||
|
|
||||||
let result = match comp {
|
let result = match comp {
|
||||||
Comparison::LessThan => val_a < val_b,
|
Comparison::LessThan => val_a < val_b,
|
||||||
|
@ -489,7 +489,7 @@ impl Machine {
|
||||||
Comparison::NotEqual => val_a != val_b,
|
Comparison::NotEqual => val_a != val_b,
|
||||||
};
|
};
|
||||||
if result {
|
if result {
|
||||||
match self.board.get_mut(front_pos) {
|
match self.grid.get_mut(front_pos) {
|
||||||
Some(Tile::Powerable(_, state)) => {
|
Some(Tile::Powerable(_, state)) => {
|
||||||
if !state.get_dir(dir) {
|
if !state.get_dir(dir) {
|
||||||
state.add_dir(dir);
|
state.add_dir(dir);
|
||||||
|
@ -509,14 +509,14 @@ impl Machine {
|
||||||
}
|
}
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
self.debug_subticks.push(DebugSubTick {
|
self.debug_subticks.push(DebugSubTick {
|
||||||
board: self.board.clone(),
|
grid: self.grid.clone(),
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Tile::Powerable(_, _state) => {
|
Tile::Powerable(_, _state) => {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
self.debug_subticks.push(DebugSubTick {
|
self.debug_subticks.push(DebugSubTick {
|
||||||
board: self.board.clone(),
|
grid: self.grid.clone(),
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{tile::*, Pos, PosInt};
|
use super::{tile::*, Pos, PosInt};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -6,8 +7,9 @@ use crate::{
|
||||||
util::{draw_scaled_texture, Textures},
|
util::{draw_scaled_texture, Textures},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Board {
|
#[serde(into = "String", from = "String")]
|
||||||
|
pub struct Grid {
|
||||||
tiles: Vec<Tile>,
|
tiles: Vec<Tile>,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
|
@ -21,7 +23,7 @@ pub struct ResizeDeltas {
|
||||||
pub y_neg: usize,
|
pub y_neg: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Board {
|
impl Grid {
|
||||||
pub fn parse(source: &str) -> Self {
|
pub fn parse(source: &str) -> Self {
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ impl Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize(&self) -> String {
|
pub fn to_str(&self) -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
for y in 0..self.height {
|
for y in 0..self.height {
|
||||||
for x in 0..self.width {
|
for x in 0..self.width {
|
||||||
|
@ -125,7 +127,7 @@ impl Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paste_board(&mut self, pos: Pos, source: &Board) {
|
pub fn paste_grid(&mut self, pos: Pos, source: &Grid) {
|
||||||
for x in 0..source.width() {
|
for x in 0..source.width() {
|
||||||
for y in 0..source.height() {
|
for y in 0..source.height() {
|
||||||
let offset = (x, y).into();
|
let offset = (x, y).into();
|
||||||
|
@ -136,8 +138,8 @@ impl Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rect(&self, pos: Pos, width: usize, height: usize) -> Board {
|
pub fn get_rect(&self, pos: Pos, width: usize, height: usize) -> Grid {
|
||||||
let mut out = Board::new_empty(width, height);
|
let mut out = Grid::new_empty(width, height);
|
||||||
for x in 0..width {
|
for x in 0..width {
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
let offset = (x, y).into();
|
let offset = (x, y).into();
|
||||||
|
@ -152,27 +154,27 @@ impl Board {
|
||||||
pub fn grow(&mut self, deltas: &ResizeDeltas) {
|
pub fn grow(&mut self, deltas: &ResizeDeltas) {
|
||||||
let new_width = self.width + deltas.x_neg + deltas.x_pos;
|
let new_width = self.width + deltas.x_neg + deltas.x_pos;
|
||||||
let new_height = self.height + deltas.y_neg + deltas.y_pos;
|
let new_height = self.height + deltas.y_neg + deltas.y_pos;
|
||||||
let mut new_board = Board::new_empty(new_width, new_height);
|
let mut new_grid = Grid::new_empty(new_width, new_height);
|
||||||
for x in 0..self.width {
|
for x in 0..self.width {
|
||||||
for y in 0..self.height {
|
for y in 0..self.height {
|
||||||
let tile = self.get_unchecked((x, y).into());
|
let tile = self.get_unchecked((x, y).into());
|
||||||
new_board.set((x + deltas.x_neg, y + deltas.y_neg).into(), tile);
|
new_grid.set((x + deltas.x_neg, y + deltas.y_neg).into(), tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*self = new_board;
|
*self = new_grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shrink(&mut self, deltas: &ResizeDeltas) {
|
pub fn shrink(&mut self, deltas: &ResizeDeltas) {
|
||||||
let new_width = self.width - deltas.x_neg - deltas.x_pos;
|
let new_width = self.width - deltas.x_neg - deltas.x_pos;
|
||||||
let new_height = self.height - deltas.y_neg - deltas.y_pos;
|
let new_height = self.height - deltas.y_neg - deltas.y_pos;
|
||||||
let mut new_board = Board::new_empty(new_width, new_height);
|
let mut new_grid = Grid::new_empty(new_width, new_height);
|
||||||
for x in 0..new_width {
|
for x in 0..new_width {
|
||||||
for y in 0..new_height {
|
for y in 0..new_height {
|
||||||
let tile = self.get_unchecked((x + deltas.x_neg, y + deltas.y_neg).into());
|
let tile = self.get_unchecked((x + deltas.x_neg, y + deltas.y_neg).into());
|
||||||
new_board.set((x, y).into(), tile);
|
new_grid.set((x, y).into(), tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*self = new_board;
|
*self = new_grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> usize {
|
pub fn width(&self) -> usize {
|
||||||
|
@ -231,3 +233,15 @@ impl Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<String> for Grid {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::parse(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Grid> for String {
|
||||||
|
fn from(val: Grid) -> String {
|
||||||
|
val.to_str()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub type PosInt = i16;
|
pub type PosInt = i16;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Pos {
|
pub struct Pos {
|
||||||
pub x: PosInt,
|
pub x: PosInt,
|
||||||
pub y: PosInt,
|
pub y: PosInt,
|
||||||
|
|
|
@ -6,14 +6,14 @@ use std::{
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{level::Level, util::userdata_dir};
|
use crate::{board::Board, level::Level, util::userdata_dir};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Solution {
|
pub struct Solution {
|
||||||
solution_id: usize,
|
solution_id: usize,
|
||||||
level_id: String,
|
level_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub board: String,
|
pub board: Board,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub score: Option<Score>,
|
pub score: Option<Score>,
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ impl Solution {
|
||||||
solution_id: id,
|
solution_id: id,
|
||||||
level_id: level.id().to_owned(),
|
level_id: level.id().to_owned(),
|
||||||
name: format!("Unnamed {id}"),
|
name: format!("Unnamed {id}"),
|
||||||
board: level.init_board().unwrap_or(String::from(" ")),
|
board: level.init_board().unwrap_or_default(),
|
||||||
score: None,
|
score: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use marble_machinations::marble_engine::{board::Board, Machine};
|
use marble_machinations::marble_engine::{grid::Grid, Machine};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn creating_marbles_cause_indirect_claim() {
|
fn creating_marbles_cause_indirect_claim() {
|
||||||
let mut eng = Machine::new_empty();
|
let mut eng = Machine::new_empty();
|
||||||
eng.set_board(Board::parse(
|
eng.set_grid(Grid::parse(
|
||||||
"
|
"
|
||||||
I
|
I
|
||||||
o 2
|
o 2
|
||||||
|
|
Loading…
Add table
Reference in a new issue