From 0c2d24174527a4f812ac253e9f5df07cd19d3e14 Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Sun, 6 Oct 2024 16:29:45 +0200 Subject: [PATCH] solution saving and loading --- .gitignore | 1 + README.md | 1 - src/editor.rs | 13 +++++-------- src/main.rs | 50 +++++++++++++++++++++++++++++++++++++++++-------- src/solution.rs | 22 +++++++++++++++++++--- src/util.rs | 6 +++++- 6 files changed, 72 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c4bf..f7b9ff7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/user diff --git a/README.md b/README.md index c19f078..f9cf146 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ logic mostly like https://git.crispypin.cc/CrispyPin/marble ## todo -solution saving & loading cleanup: unpowered texture names x_off -> x input/output display grow grid automatically while editing diff --git a/src/editor.rs b/src/editor.rs index c2a4f45..25710e7 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -64,6 +64,7 @@ enum SimState { pub enum ExitState { Dont, ExitAndSave, + Save, ExitNoSave, } @@ -100,18 +101,10 @@ impl Editor { &self.level.id() } - pub fn save(&mut self) { - todo!() - } - pub fn source_board(&self) -> &Board { &self.source_board } - pub fn load_board(&mut self, board: Board) { - self.source_board = board; - } - fn start_sim(&mut self) { self.machine.reset(); self.machine.set_board(self.source_board.clone()); @@ -278,6 +271,10 @@ impl Editor { self.exit_menu = true; } d.draw_text("exit", 10, 10, 20, Color::WHITE); + if simple_button(d, 90, 5, 80, 30) { + self.exit_state = ExitState::Save; + } + d.draw_text("save", 95, 10, 20, Color::WHITE); } } diff --git a/src/main.rs b/src/main.rs index 9f3d421..dc8f21f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,6 @@ mod util; use editor::{Editor, ExitState}; use level::Level; -use marble_engine::board::Board; use solution::Solution; use util::*; @@ -40,8 +39,6 @@ fn main() { let mut game = Game::new(&mut rl, &thread); game.run(&mut rl, &thread); - // let board = Board::parse(&read_to_string("boards/adder.mbl").unwrap()); - // game.load_board(board); } impl Game { @@ -52,7 +49,7 @@ impl Game { Self { levels: get_levels(), - solutions: HashMap::new(), + solutions: get_solutions(), open_editor: None, textures, selected_level: 0, @@ -69,13 +66,20 @@ impl Game { editor.draw(&mut d, &self.textures); match editor.get_exit_state() { ExitState::Dont => (), - ExitState::ExitNoSave => self.open_editor = None, ExitState::ExitAndSave => { - self.solutions.get_mut(editor.level_id()).unwrap() - [self.selected_solution] - .board = editor.source_board().to_string(); + let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap() + [self.selected_solution]; + solution.board = editor.source_board().to_string(); + solution.save(); self.open_editor = None; } + ExitState::Save => { + let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap() + [self.selected_solution]; + solution.board = editor.source_board().to_string(); + solution.save(); + } + ExitState::ExitNoSave => self.open_editor = None, } } else { self.draw(&mut d); @@ -227,3 +231,33 @@ fn get_levels() -> Vec { levels.sort_by(|a, b| a.id().cmp(b.id())); levels } + +fn get_solutions() -> HashMap> { + let mut levels = HashMap::new(); + let solution_dir = userdata_dir().join("solutions"); + if let Ok(dir_contents) = read_dir(solution_dir) { + for dir in dir_contents.flatten() { + if dir.path().is_dir() { + let level_name = dir.file_name().to_string_lossy().to_string(); + let mut solutions = Vec::new(); + + if let Ok(files) = read_dir(dir.path()) { + for file in files.flatten() { + let s = read_to_string(file.path()) + .ok() + .as_deref() + .map(|s| serde_json::from_str(s).ok()) + .flatten(); + if let Some(solution) = s { + solutions.push(solution) + } + } + + levels.insert(level_name, solutions); + } + } + } + } + + levels +} diff --git a/src/solution.rs b/src/solution.rs index 7b4de77..c91aebd 100644 --- a/src/solution.rs +++ b/src/solution.rs @@ -1,13 +1,19 @@ +use std::{ + fs::{self, File}, + io::Write, +}; + use serde::{Deserialize, Serialize}; -use crate::level::Level; +use crate::{level::Level, userdata_dir}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Solution { - // solution_id: String, + solution_id: String, level_id: String, // redundant? pub name: String, pub board: String, + #[serde(default)] pub score: Option, } @@ -21,7 +27,7 @@ pub struct Score { impl Solution { pub fn new(level: &Level, number: usize) -> Self { Self { - // solution_id: format!("solution_{number}"), + solution_id: format!("solution_{number}"), level_id: level.id().to_owned(), name: format!("Unnamed {number}"), board: level @@ -32,6 +38,16 @@ impl Solution { } } + pub fn save(&self) { + let dir = userdata_dir().join("solutions").join(&self.level_id); + fs::create_dir_all(&dir).unwrap(); + let path = dir.join(&format!("{}.json", &self.solution_id)); + + let json = serde_json::to_string_pretty(self).unwrap(); + let mut file = File::create(path).unwrap(); + file.write_all(json.as_bytes()).unwrap(); + } + pub fn level_id(&self) -> &str { &self.level_id } diff --git a/src/util.rs b/src/util.rs index e67cd3e..b21f229 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fs::read_dir}; +use std::{collections::HashMap, fs::read_dir, path::PathBuf}; use raylib::prelude::*; @@ -188,3 +188,7 @@ pub fn shrink_rec(rec: Rectangle, a: f32) -> Rectangle { height: rec.height - a * 2., } } + +pub fn userdata_dir()->PathBuf{ + PathBuf::from("user") +} \ No newline at end of file