From 5b6113780ac0ac45bd69eae4e1eb925ab2102438 Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Mon, 16 Dec 2024 21:45:50 +0100 Subject: [PATCH 1/5] add undo/redo for single tile placement/erasing --- src/editor.rs | 130 ++++++++++++++++++++++++++++++------- src/marble_engine/board.rs | 36 ++++++++++ 2 files changed, 144 insertions(+), 22 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 80f4512..fcea22e 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -12,7 +12,7 @@ use crate::{ draw_scaled_texture, draw_usize, get_free_id, get_scroll, level::Level, marble_engine::{ - board::Board, + board::{Board, ResizeDeltas}, pos::{Pos, PosInt}, tile::{Claim, Comparison, Direction, MathOp, MirrorType, OpenTile, PTile, Tile, WireType}, Machine, @@ -70,6 +70,14 @@ pub struct Editor { start_time: Instant, pasting_board: Option, draw_overlay: bool, + undo_history: Vec, + undo_index: usize, +} + +#[derive(Debug, Clone)] +enum Action { + SetTile(ResizeDeltas, Pos, Tile, Tile), + SetArea(ResizeDeltas, Pos, Board, Board), } #[derive(Debug, PartialEq)] @@ -150,9 +158,66 @@ impl Editor { start_time: Instant::now(), pasting_board: None, draw_overlay: true, + undo_history: Vec::new(), + undo_index: 0, } } + fn redo(&mut self) { + if self.undo_index >= self.undo_history.len() { + return; + } + let action = self.undo_history[self.undo_index].clone(); + self.do_action(action); + self.undo_index += 1; + } + + fn push_action(&mut self, action: Action) { + self.do_action(action.clone()); + self.undo_history.truncate(self.undo_index); + self.undo_history.push(action); + self.undo_index += 1; + } + + fn do_action(&mut self, action: Action) { + match action { + Action::SetTile(resize_delta, pos, _old, new) => { + self.resize_board(resize_delta); + self.source_board.set(pos, new); + } + Action::SetArea(_, _, _, _) => todo!(), + } + } + + fn undo(&mut self) { + if self.undo_index == 0 { + return; + } + self.undo_index -= 1; + let action = self.undo_history[self.undo_index].clone(); + match action { + Action::SetTile(resize_delta, pos, old, _new) => { + self.source_board.set(pos, old); + self.undo_resize_board(resize_delta); + } + Action::SetArea(_, _, _, _) => todo!(), + } + } + + fn resize_board(&mut self, deltas: ResizeDeltas) { + self.source_board.grow(&deltas); + let tile_size = TILE_TEXTURE_SIZE * self.zoom; + self.view_offset.x -= deltas.x_neg as f32 * tile_size; + self.view_offset.y -= deltas.y_neg as f32 * tile_size; + } + + fn undo_resize_board(&mut self, deltas: ResizeDeltas) { + self.source_board.shrink(&deltas); + let tile_size = TILE_TEXTURE_SIZE * self.zoom; + self.view_offset.x += deltas.x_neg as f32 * tile_size; + self.view_offset.y += deltas.y_neg as f32 * tile_size; + } + pub fn get_exit_state(&self) -> ExitState { self.exit_state } @@ -318,22 +383,39 @@ impl Editor { } } - fn set_tile(&mut self, mut pos: Pos, tile: Tile) { - let tile_size = TILE_TEXTURE_SIZE * self.zoom; - pos.x -= BOARD_MARGIN; - pos.y -= BOARD_MARGIN; - self.grow_board_and_update_view(&mut pos); - pos.x += BOARD_MARGIN * 2; - pos.y += BOARD_MARGIN * 2; - self.grow_board_and_update_view(&mut pos); - pos.x -= BOARD_MARGIN; - pos.y -= BOARD_MARGIN; - self.source_board.set(pos, tile); - if tile.is_blank() { - let (x, y) = self.source_board.trim_size(BOARD_MARGIN as usize); - self.view_offset.x += x as f32 * tile_size; - self.view_offset.y += y as f32 * tile_size; + fn set_tile(&mut self, pos: Pos, tile: Tile) { + let old_tile = self.source_board.get_or_blank(pos); + if old_tile == tile { + return; } + let width = self.source_board.width() as PosInt; + let height = self.source_board.height() as PosInt; + let resize = ResizeDeltas { + x_pos: if (pos.x + BOARD_MARGIN + 1) > width { + pos.x + BOARD_MARGIN + 1 - width + } else { + 0 + } as usize, + x_neg: if pos.x < BOARD_MARGIN { + BOARD_MARGIN - pos.x + } else { + 0 + } as usize, + y_pos: if (pos.y + BOARD_MARGIN + 1) > height { + pos.y + BOARD_MARGIN + 1 - height + } else { + 0 + } as usize, + y_neg: if pos.y < BOARD_MARGIN { + BOARD_MARGIN - pos.y + } else { + 0 + } as usize, + }; + let mut pos = pos; + pos.x += resize.x_neg as PosInt; + pos.y += resize.y_neg as PosInt; + self.push_action(Action::SetTile(resize, pos, old_tile, tile)); } pub fn update(&mut self, rl: &RaylibHandle) { @@ -417,12 +499,16 @@ impl Editor { } if self.sim_state == SimState::Editing { - if rl.is_key_down(KeyboardKey::KEY_LEFT_CONTROL) - && rl.is_key_pressed(KeyboardKey::KEY_V) - { - if let Ok(text) = rl.get_clipboard_text() { - let b = Board::parse(&text); - self.pasting_board = Some(b); + if rl.is_key_down(KeyboardKey::KEY_LEFT_CONTROL) { + if rl.is_key_pressed(KeyboardKey::KEY_V) { + if let Ok(text) = rl.get_clipboard_text() { + let b = Board::parse(&text); + self.pasting_board = Some(b); + } + } else if rl.is_key_pressed(KeyboardKey::KEY_Z) { + self.undo() + } else if rl.is_key_pressed(KeyboardKey::KEY_Y) { + self.redo(); } } } diff --git a/src/marble_engine/board.rs b/src/marble_engine/board.rs index 1b21540..2f10308 100644 --- a/src/marble_engine/board.rs +++ b/src/marble_engine/board.rs @@ -13,6 +13,14 @@ pub struct Board { height: usize, } +#[derive(Debug, Clone)] +pub struct ResizeDeltas { + pub x_pos: usize, + pub x_neg: usize, + pub y_pos: usize, + pub y_neg: usize, +} + impl Board { pub fn parse(source: &str) -> Self { let mut rows = Vec::new(); @@ -226,6 +234,34 @@ impl Board { (offset_x, offset_y) } + pub fn grow(&mut self, deltas: &ResizeDeltas) { + // dbg!(&deltas); + let new_width = self.width + deltas.x_neg + deltas.x_pos; + let new_height = self.height + deltas.y_neg + deltas.y_pos; + let mut new_board = Board::new_empty(new_width, new_height); + for x in 0..self.width { + for y in 0..self.height { + let tile = self.rows[y][x]; + new_board.rows[y + deltas.y_neg][x + deltas.x_neg] = tile; + } + } + *self = new_board; + } + + pub fn shrink(&mut self, deltas: &ResizeDeltas) { + // dbg!(&deltas); + let new_width = self.width - deltas.x_neg - deltas.x_pos; + let new_height = self.height - deltas.y_neg - deltas.y_pos; + let mut new_board = Board::new_empty(new_width, new_height); + for x in 0..new_width { + for y in 0..new_height { + let tile = self.rows[y + deltas.y_neg][x + deltas.x_neg]; + new_board.rows[y][x] = tile; + } + } + *self = new_board; + } + pub fn width(&self) -> usize { self.width } From 6970b18e22b9db20c9605c096b6226f235330fcd Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Mon, 16 Dec 2024 22:16:58 +0100 Subject: [PATCH 2/5] add undo/redo for pasting/area operations --- src/editor.rs | 131 ++++++++++++++++++++----------------- src/marble_engine/board.rs | 39 +---------- 2 files changed, 73 insertions(+), 97 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index fcea22e..3ad00da 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -181,11 +181,16 @@ impl Editor { fn do_action(&mut self, action: Action) { match action { - Action::SetTile(resize_delta, pos, _old, new) => { - self.resize_board(resize_delta); + Action::SetTile(deltas, pos, _old, new) => { + self.shift_view(deltas.x_neg as f32, deltas.y_neg as f32); + self.source_board.grow(&deltas); self.source_board.set(pos, new); } - Action::SetArea(_, _, _, _) => todo!(), + Action::SetArea(deltas, pos, _old, new) => { + self.shift_view(deltas.x_neg as f32, deltas.y_neg as f32); + self.source_board.grow(&deltas); + self.source_board.paste_board(pos, &new); + } } } @@ -196,26 +201,39 @@ impl Editor { self.undo_index -= 1; let action = self.undo_history[self.undo_index].clone(); match action { - Action::SetTile(resize_delta, pos, old, _new) => { + Action::SetTile(deltas, pos, old, _new) => { self.source_board.set(pos, old); - self.undo_resize_board(resize_delta); + self.source_board.shrink(&deltas); + self.shift_view(-(deltas.x_neg as f32), -(deltas.y_neg as f32)); + } + Action::SetArea(deltas, pos, old, _new) => { + self.source_board.paste_board(pos, &old); + self.source_board.shrink(&deltas); + self.shift_view(-(deltas.x_neg as f32), -(deltas.y_neg as f32)); } - Action::SetArea(_, _, _, _) => todo!(), } } - fn resize_board(&mut self, deltas: ResizeDeltas) { - self.source_board.grow(&deltas); + fn shift_view(&mut self, x: f32, y: f32) { + match &mut self.active_tool { + Tool::SelectArea(Selection { + area: Some((a, b)), + is_selecting: _, + }) => { + a.x += x as PosInt; + a.y += y as PosInt; + b.x += x as PosInt; + b.y += y as PosInt; + } + Tool::Digits(Some(pos)) => { + pos.x += x as PosInt; + pos.y += y as PosInt; + } + _ => (), + } let tile_size = TILE_TEXTURE_SIZE * self.zoom; - self.view_offset.x -= deltas.x_neg as f32 * tile_size; - self.view_offset.y -= deltas.y_neg as f32 * tile_size; - } - - fn undo_resize_board(&mut self, deltas: ResizeDeltas) { - self.source_board.shrink(&deltas); - let tile_size = TILE_TEXTURE_SIZE * self.zoom; - self.view_offset.x += deltas.x_neg as f32 * tile_size; - self.view_offset.y += deltas.y_neg as f32 * tile_size; + self.view_offset.x -= x * tile_size; + self.view_offset.y -= y * tile_size; } pub fn get_exit_state(&self) -> ExitState { @@ -356,31 +374,39 @@ impl Editor { self.active_tool = Tool::Blueprint; } - fn grow_board_and_update_view(&mut self, pos: &mut Pos) { - let tile_size = TILE_TEXTURE_SIZE * self.zoom; - let (x, y) = self.source_board.grow_to_include(*pos); - if x != 0 || y != 0 { - self.view_offset.x -= x as f32 * tile_size; - self.view_offset.y -= y as f32 * tile_size; - pos.x += x; - pos.y += y; - match &mut self.active_tool { - Tool::Digits(Some(pos)) => { - pos.x += x; - pos.y += y; - } - Tool::SelectArea(Selection { - area: Some((start, end)), - is_selecting: _, - }) => { - start.x += x; - start.y += y; - end.x += x; - end.y += y; - } - _ => (), - } + fn set_area(&mut self, pos: Pos, area: Board) { + let old_area = self.source_board.get_rect(pos, area.width(), area.height()); + if area == old_area { + return; } + let width = self.source_board.width() as PosInt; + let height = self.source_board.height() as PosInt; + let resize = ResizeDeltas { + x_pos: if (pos.x + BOARD_MARGIN + area.width() as PosInt) > width { + pos.x + BOARD_MARGIN + area.width() as PosInt - width + } else { + 0 + } as usize, + x_neg: if pos.x < BOARD_MARGIN { + BOARD_MARGIN - pos.x + } else { + 0 + } as usize, + y_pos: if (pos.y + BOARD_MARGIN + area.height() as PosInt) > height { + pos.y + BOARD_MARGIN + area.height() as PosInt - height + } else { + 0 + } as usize, + y_neg: if pos.y < BOARD_MARGIN { + BOARD_MARGIN - pos.y + } else { + 0 + } as usize, + }; + let mut pos = pos; + pos.x += resize.x_neg as PosInt; + pos.y += resize.y_neg as PosInt; + self.push_action(Action::SetArea(resize, pos, old_area, area)); } fn set_tile(&mut self, pos: Pos, tile: Tile) { @@ -825,11 +851,9 @@ impl Editor { if simple_button(d, 232, y, 40, 40) { let min = selection.0.min(selection.1); 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); - } - } + let board = + Board::new_empty((max.x - min.x) as usize + 1, (max.y - min.y) as usize + 1); + self.set_area(min, board); } draw_scaled_texture(d, textures.get("eraser"), 236, y + 4, 2.); } @@ -1003,14 +1027,8 @@ impl Editor { if d.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT) { let tile_pos = (mouse_pos - self.view_offset) / tile_size; let tile_pos = Vector2::new(tile_pos.x.floor(), tile_pos.y.floor()); - let mut pos = tile_pos.into(); - let board = self.pasting_board.take().unwrap(); - self.grow_board_and_update_view(&mut pos); - self.grow_board_and_update_view( - &mut (pos + (board.width() - 1, board.height() - 1).into()), - ); - self.source_board.paste_board(pos, &board); + self.set_area(tile_pos.into(), board); } } return; @@ -1106,12 +1124,7 @@ impl Editor { if mouse_pos.x > SIDEBAR_WIDTH as f32 { if let Some(bp) = self.blueprints.get(self.selected_blueprint) { let board = bp.get_board().unwrap().clone(); - let mut pos = pos; - self.grow_board_and_update_view(&mut pos); - self.grow_board_and_update_view( - &mut (pos + (board.width() - 1, board.height() - 1).into()), - ); - self.source_board.paste_board(pos, &board); + self.set_area(pos, board); } } } diff --git a/src/marble_engine/board.rs b/src/marble_engine/board.rs index 2f10308..b7539de 100644 --- a/src/marble_engine/board.rs +++ b/src/marble_engine/board.rs @@ -6,7 +6,7 @@ use super::Pos; use super::PosInt; use raylib::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Board { rows: Vec>, width: usize, @@ -140,41 +140,6 @@ impl Board { out } - pub fn grow_to_include(&mut self, p: Pos) -> (PosInt, PosInt) { - let mut offset_x = 0; - let mut offset_y = 0; - 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]; - new_row.append(row); - *row = new_row; - } - offset_x = len; - self.width += len; - } 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); - } - 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]; - 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.height = new_height; - } - (offset_x as PosInt, offset_y as PosInt) - } - pub fn trim_size(&mut self, margin: usize) -> (usize, usize) { let (offset_x, offset_y); // top @@ -235,7 +200,6 @@ impl Board { } pub fn grow(&mut self, deltas: &ResizeDeltas) { - // dbg!(&deltas); let new_width = self.width + deltas.x_neg + deltas.x_pos; let new_height = self.height + deltas.y_neg + deltas.y_pos; let mut new_board = Board::new_empty(new_width, new_height); @@ -249,7 +213,6 @@ impl Board { } pub fn shrink(&mut self, deltas: &ResizeDeltas) { - // dbg!(&deltas); let new_width = self.width - deltas.x_neg - deltas.x_pos; let new_height = self.height - deltas.y_neg - deltas.y_pos; let mut new_board = Board::new_empty(new_width, new_height); From ba1f404250892e7fa0f416033d770c1c8069eb0c Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Mon, 16 Dec 2024 22:24:27 +0100 Subject: [PATCH 3/5] merge undo/redo logic for areas and single tiles --- README.md | 1 - src/editor.rs | 54 +++++--------------------------------- src/marble_engine/board.rs | 8 ++++++ 3 files changed, 14 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index d60eeba..439e700 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ logic mostly like https://git.crispypin.cc/CrispyPin/marble ## todo -- undo/redo - more levels - make direct power (comparator -> machine) work, (needs storing power direction in machine tiles) - cut selections, copy to system clipboard diff --git a/src/editor.rs b/src/editor.rs index 3ad00da..4e9dbbc 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -76,7 +76,6 @@ pub struct Editor { #[derive(Debug, Clone)] enum Action { - SetTile(ResizeDeltas, Pos, Tile, Tile), SetArea(ResizeDeltas, Pos, Board, Board), } @@ -181,13 +180,8 @@ impl Editor { fn do_action(&mut self, action: Action) { match action { - Action::SetTile(deltas, pos, _old, new) => { - self.shift_view(deltas.x_neg as f32, deltas.y_neg as f32); - self.source_board.grow(&deltas); - self.source_board.set(pos, new); - } Action::SetArea(deltas, pos, _old, new) => { - self.shift_view(deltas.x_neg as f32, deltas.y_neg as f32); + self.shift_world(deltas.x_neg as f32, deltas.y_neg as f32); self.source_board.grow(&deltas); self.source_board.paste_board(pos, &new); } @@ -199,22 +193,17 @@ impl Editor { return; } self.undo_index -= 1; - let action = self.undo_history[self.undo_index].clone(); + let action = &self.undo_history[self.undo_index]; match action { - Action::SetTile(deltas, pos, old, _new) => { - self.source_board.set(pos, old); - self.source_board.shrink(&deltas); - self.shift_view(-(deltas.x_neg as f32), -(deltas.y_neg as f32)); - } Action::SetArea(deltas, pos, old, _new) => { - self.source_board.paste_board(pos, &old); + self.source_board.paste_board(*pos, &old); self.source_board.shrink(&deltas); - self.shift_view(-(deltas.x_neg as f32), -(deltas.y_neg as f32)); + self.shift_world(-(deltas.x_neg as f32), -(deltas.y_neg as f32)); } } } - fn shift_view(&mut self, x: f32, y: f32) { + fn shift_world(&mut self, x: f32, y: f32) { match &mut self.active_tool { Tool::SelectArea(Selection { area: Some((a, b)), @@ -410,38 +399,7 @@ impl Editor { } fn set_tile(&mut self, pos: Pos, tile: Tile) { - let old_tile = self.source_board.get_or_blank(pos); - if old_tile == tile { - return; - } - let width = self.source_board.width() as PosInt; - let height = self.source_board.height() as PosInt; - let resize = ResizeDeltas { - x_pos: if (pos.x + BOARD_MARGIN + 1) > width { - pos.x + BOARD_MARGIN + 1 - width - } else { - 0 - } as usize, - x_neg: if pos.x < BOARD_MARGIN { - BOARD_MARGIN - pos.x - } else { - 0 - } as usize, - y_pos: if (pos.y + BOARD_MARGIN + 1) > height { - pos.y + BOARD_MARGIN + 1 - height - } else { - 0 - } as usize, - y_neg: if pos.y < BOARD_MARGIN { - BOARD_MARGIN - pos.y - } else { - 0 - } as usize, - }; - let mut pos = pos; - pos.x += resize.x_neg as PosInt; - pos.y += resize.y_neg as PosInt; - self.push_action(Action::SetTile(resize, pos, old_tile, tile)); + self.set_area(pos, Board::new_single(tile)); } pub fn update(&mut self, rl: &RaylibHandle) { diff --git a/src/marble_engine/board.rs b/src/marble_engine/board.rs index b7539de..ef70aba 100644 --- a/src/marble_engine/board.rs +++ b/src/marble_engine/board.rs @@ -61,6 +61,14 @@ impl Board { } } + pub fn new_single(tile: Tile) -> Self { + Self { + rows: vec![vec![tile]], + width: 1, + height: 1, + } + } + pub fn new(rows: Vec>) -> Self { Self { width: rows[0].len(), From 4427b4c2fc915ce97daa5c8c92e47e5b9ac5bdfd Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Mon, 16 Dec 2024 22:43:12 +0100 Subject: [PATCH 4/5] cleanup --- src/editor.rs | 18 +++++++-------- src/main.rs | 2 +- src/marble_engine/board.rs | 6 ++--- src/marble_engine/tile.rs | 46 +++++++++++++++++++------------------- src/util.rs | 2 +- 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 4e9dbbc..5c17ce4 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -196,8 +196,8 @@ impl Editor { let action = &self.undo_history[self.undo_index]; match action { Action::SetArea(deltas, pos, old, _new) => { - self.source_board.paste_board(*pos, &old); - self.source_board.shrink(&deltas); + self.source_board.paste_board(*pos, old); + self.source_board.shrink(deltas); self.shift_world(-(deltas.x_neg as f32), -(deltas.y_neg as f32)); } } @@ -356,7 +356,7 @@ impl Editor { let id = get_free_id(&self.blueprints, Blueprint::id); let mut blueprint = Blueprint::new(&board, id); if !self.new_blueprint_name.is_empty() { - blueprint.name = self.new_blueprint_name.clone(); + blueprint.name.clone_from(&self.new_blueprint_name); } blueprint.save(); self.blueprints.push(blueprint); @@ -944,7 +944,7 @@ impl Editor { { format!("{:?}", expected_byte as char) } else { - format!("{}", expected_byte) + format!("{expected_byte}") }; d.draw_text(&top_text, x + 2, y + 5, 20, Color::WHITE); } @@ -954,7 +954,7 @@ impl Editor { { format!("{:?}", real_byte as char) } else { - format!("{}", real_byte) + format!("{real_byte}") }; d.draw_text(&bottom_text, x + 2, y + 40, 20, Color::WHITE); } @@ -1057,11 +1057,10 @@ impl Editor { if d.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT) { let pos = tile_pos.into(); match self.active_tool { - Tool::None => (), - Tool::Erase => (), + Tool::None | Tool::Erase | Tool::SelectArea(_) => (), Tool::SetTile(tile) => self.set_tile(pos, tile), Tool::Math => { - self.set_tile(pos, Tile::Powerable(PTile::Math(self.tool_math), false)) + self.set_tile(pos, Tile::Powerable(PTile::Math(self.tool_math), false)); } Tool::Comparator => self.set_tile( pos, @@ -1086,13 +1085,12 @@ impl Editor { } } } - Tool::SelectArea(_) => (), } } 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::BLANK); } if let Tool::SelectArea(selection) = &mut self.active_tool { if d.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT) { diff --git a/src/main.rs b/src/main.rs index 4fb7834..b2f0f76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -302,7 +302,7 @@ fn get_solutions() -> HashMap> { .as_deref() .and_then(|s| serde_json::from_str(s).ok()); if let Some(solution) = s { - solutions.push(solution) + solutions.push(solution); } } solutions.sort_unstable_by_key(Solution::id); diff --git a/src/marble_engine/board.rs b/src/marble_engine/board.rs index ef70aba..0dd1954 100644 --- a/src/marble_engine/board.rs +++ b/src/marble_engine/board.rs @@ -45,7 +45,7 @@ impl Board { let mut out = String::new(); for row in &self.rows { for tile in row { - out.push(tile.to_char()) + out.push(tile.to_char()); } out.push('\n'); } @@ -153,7 +153,7 @@ impl Board { // top { let mut n = 0; - while n < self.height && self.rows[n].iter().all(Tile::is_blank) { + while n < self.height && self.rows[n].iter().all(|t| t.is_blank()) { n += 1; } let trim_top = n.saturating_sub(margin); @@ -166,7 +166,7 @@ impl Board { // bottom { let mut n = 0; - while n < self.height && self.rows[self.height - n - 1].iter().all(Tile::is_blank) { + while n < self.height && self.rows[self.height - n - 1].iter().all(|t| t.is_blank()) { n += 1; } let trim_bottom = n.saturating_sub(margin); diff --git a/src/marble_engine/tile.rs b/src/marble_engine/tile.rs index 808fe94..3619c19 100644 --- a/src/marble_engine/tile.rs +++ b/src/marble_engine/tile.rs @@ -155,19 +155,19 @@ impl Tile { } } - pub fn is_blank(&self) -> bool { + pub fn is_blank(self) -> bool { matches!(self, Tile::Open(OpenTile::Blank, _)) } - pub fn read_value(&self) -> MarbleValue { + pub fn read_value(self) -> MarbleValue { match self { - Tile::Marble { value, dir: _ } => *value, - Tile::Open(OpenTile::Digit(d), _) => *d as MarbleValue, + Tile::Marble { value, dir: _ } => value, + Tile::Open(OpenTile::Digit(d), _) => d as MarbleValue, _ => 0, } } - pub fn texture(&self) -> &str { + pub fn texture(self) -> &'static str { match self { Tile::Open(OpenTile::Blank, _) => "", Tile::Block => "block", @@ -188,19 +188,19 @@ impl Tile { Tile::Mirror(mirror) => mirror.texture_name(), Tile::Arrow(dir) => dir.arrow_tile_texture_name(), Tile::Button(state) => { - if *state { + if state { return "button_on"; } "button_off" } Tile::Wire(wire, state) => { - if *state { + if state { return wire.texture_name_on(); } wire.texture_name_off() } Tile::Powerable(tile, state) => { - if *state { + if state { return match tile { PTile::Comparator(comp) => comp.texture_name_on(), PTile::Math(math_op) => math_op.texture_name_on(), @@ -209,13 +209,13 @@ impl Tile { PTile::IO => "io_tile_on", }; } - return match tile { + match tile { PTile::Comparator(comp) => comp.texture_name_off(), PTile::Math(math_op) => math_op.texture_name_off(), PTile::Silo => "silo_off", PTile::Flipper => "flipper_off", PTile::IO => "io_tile_off", - }; + } } } } @@ -229,7 +229,7 @@ impl Direction { Direction::Right, ]; - pub fn opposite(&self) -> Direction { + pub fn opposite(self) -> Direction { match self { Direction::Up => Direction::Down, Direction::Down => Direction::Up, @@ -238,7 +238,7 @@ impl Direction { } } - pub fn right(&self) -> Direction { + pub fn right(self) -> Direction { match self { Direction::Up => Direction::Right, Direction::Down => Direction::Left, @@ -247,11 +247,11 @@ impl Direction { } } - pub fn left(&self) -> Direction { + pub fn left(self) -> Direction { self.right().opposite() } - pub fn step(&self, mut pos: Pos) -> Pos { + pub fn step(self, mut pos: Pos) -> Pos { match self { Direction::Up => pos.y -= 1, Direction::Down => pos.y += 1, @@ -261,7 +261,7 @@ impl Direction { pos } - pub const fn arrow_tile_texture_name(&self) -> &'static str { + pub const fn arrow_tile_texture_name(self) -> &'static str { match self { Direction::Up => "arrow_up", Direction::Down => "arrow_down", @@ -270,7 +270,7 @@ impl Direction { } } - pub const fn arrow_texture_name(&self) -> &'static str { + pub const fn arrow_texture_name(self) -> &'static str { match self { Direction::Up => "direction_up", Direction::Down => "direction_down", @@ -313,14 +313,14 @@ impl WireType { } } - pub const fn texture_name_on(&self) -> &'static str { + pub const fn texture_name_on(self) -> &'static str { match self { WireType::Vertical => "wire_vertical_on", WireType::Horizontal => "wire_horizontal_on", WireType::Cross => "wire_cross_on", } } - pub const fn texture_name_off(&self) -> &'static str { + pub const fn texture_name_off(self) -> &'static str { match self { WireType::Vertical => "wire_vertical_off", WireType::Horizontal => "wire_horizontal_off", @@ -354,7 +354,7 @@ impl MirrorType { } } - pub const fn texture_name(&self) -> &'static str { + pub const fn texture_name(self) -> &'static str { match self { MirrorType::Forward => "mirror_forward", MirrorType::Back => "mirror_back", @@ -363,7 +363,7 @@ impl MirrorType { } impl MathOp { - pub const fn texture_name_on(&self) -> &'static str { + pub const fn texture_name_on(self) -> &'static str { match self { MathOp::Add => "add_on", MathOp::Sub => "sub_on", @@ -372,7 +372,7 @@ impl MathOp { MathOp::Rem => "rem_on", } } - pub const fn texture_name_off(&self) -> &'static str { + pub const fn texture_name_off(self) -> &'static str { match self { MathOp::Add => "add_off", MathOp::Sub => "sub_off", @@ -404,7 +404,7 @@ impl MathOp { } impl Comparison { - pub const fn texture_name_on(&self) -> &'static str { + pub const fn texture_name_on(self) -> &'static str { match self { Comparison::LessThan => "lt_on", Comparison::GreaterThan => "gt_on", @@ -412,7 +412,7 @@ impl Comparison { Comparison::NotEqual => "neq_on", } } - pub const fn texture_name_off(&self) -> &'static str { + pub const fn texture_name_off(self) -> &'static str { match self { Comparison::LessThan => "lt_off", Comparison::GreaterThan => "gt_off", diff --git a/src/util.rs b/src/util.rs index 20a28d0..4a9ac2d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -278,7 +278,7 @@ pub fn slider( let percent = (mouse_pos.x - bounds.x) / bounds.width; let new_value = min + (percent * (max - min + 1) as f32) as u8; if *value != new_value { - *value = new_value + *value = new_value; } } else if d.get_mouse_wheel_move() > 0.5 && *value < max { *value += 1; From 656f567242774f7d7ce6a41cfd1c98c07b65465e Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Mon, 16 Dec 2024 22:50:51 +0100 Subject: [PATCH 5/5] select the last solution for each level --- src/main.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index b2f0f76..2d30e18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,14 +53,23 @@ impl Game { textures.load_dir("assets/tiles", rl, thread); textures.load_dir("assets/digits", rl, thread); + let levels = get_levels(); + let solutions = get_solutions(); + let mut selected_solution = 0; + + // select the last solution of the first level, if there is one + if let Some(s) = levels.first().and_then(|l| solutions.get(l.id())) { + selected_solution = s.len().saturating_sub(1); + } + Self { - levels: get_levels(), + levels, level_scroll: 0, - solutions: get_solutions(), + solutions, open_editor: None, textures, selected_level: 0, - selected_solution: 0, + selected_solution, editing_solution_name: false, } } @@ -137,9 +146,13 @@ impl Game { && bounds.check_collision_point_rec(mouse_pos) && self.selected_level != index { - self.selected_solution = 0; self.editing_solution_name = false; self.selected_level = index; + self.selected_solution = 0; + // select the last solution of the level, if there is one + if let Some(solutions) = self.solutions.get(level.id()) { + self.selected_solution = solutions.len().saturating_sub(1); + } } d.draw_rectangle_rec(bounds, widget_bg(self.selected_level == index));