diff --git a/src/components/board.rs b/src/components/board.rs index 8182728..01d08d6 100644 --- a/src/components/board.rs +++ b/src/components/board.rs @@ -1,20 +1,24 @@ +use std::time::Duration; + use leptos::*; use leptos::prelude::*; use serde::{Serialize, Deserialize}; -use crate::components::utils::generate_thumbnail; +use crate::components::utils::{generate_thumbnail, save_board}; + -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, PartialEq)] pub enum CellContent { Empty, Player, Trap, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, PartialEq)] pub struct Board { pub grid: Vec>, pub size: usize, + pub moves: Vec<(usize,usize)> } impl Board { @@ -22,8 +26,43 @@ impl Board { Board { grid: vec![vec![CellContent::Empty; size]; size], size, + moves: Vec::new(), + } + } +} + +#[derive(Clone, Serialize, Deserialize, PartialEq)] +pub struct SavedBoard { + pub board: Board, + pub thumbnail: String, +} + +fn reset_board( + board: &RwSignal, + current_turn: &RwSignal, + finished: &RwSignal +) { + board.set(Board::new(2)); + current_turn.set(0); + finished.set(false); +} + +fn has_valid_moves(board: &Board) -> bool { + if let Some((player_row, player_col)) = find_player(board) { + if player_row == 0 { + return true; + } + for i in 0..board.size { + for j in 0..board.size { + if matches!(board.grid[i][j], CellContent::Empty) + && is_adjacent(player_row, player_col, i, j) + && i <= player_row { + return true; + } + } } } + false } #[component] @@ -37,20 +76,25 @@ pub fn BoardCreator( let handle_cell_click = move |row: usize, col: usize| { let mut current_board = board.get(); if current_turn.get() == 0 && row == current_board.size - 1 { - // First turn - only allow placing player in bottom row current_board.grid[row][col] = CellContent::Player; + current_board.moves.push((row,col)); board.set(current_board); current_turn.set(1); } else if !finished.get() { - // Subsequent turns - allow placing trap or moving player let player_pos = find_player(¤t_board); if let Some((player_row, player_col)) = player_pos { if player_row == 0 || is_adjacent(player_row, player_col, row, col) { if row == usize::MAX { // Special case for final move finished.set(true); + let current_board = board.get(); + let _ = save_board(current_board); + set_timeout(move || { + reset_board(&board, ¤t_turn, &finished); + }, Duration::from_millis(333)); } else { current_board.grid[player_row][player_col] = CellContent::Empty; current_board.grid[row][col] = CellContent::Player; + current_board.moves.push((row,col)); board.set(current_board); current_turn.update(|t| *t += 1); } @@ -66,8 +110,18 @@ pub fn BoardCreator(
{move || { let player_pos = find_player(&board.get()); - if let Some((row, _)) = player_pos { - if row == 0 { + if let Some((row, _col)) = player_pos { + let has_valid_moves = has_valid_moves(&board.get()); + if !has_valid_moves { + view! { + + }.into_any() + } else if row == 0 { view! { +
+ } + } + /> } } \ No newline at end of file diff --git a/src/components/utils.rs b/src/components/utils.rs index 9d11e08..b45b674 100644 --- a/src/components/utils.rs +++ b/src/components/utils.rs @@ -1,37 +1,79 @@ -use super::board::{Board, CellContent}; +use web_sys::window; + +use std::fmt::Write; +use super::board::{Board, CellContent, SavedBoard}; pub fn generate_thumbnail(board: &Board) -> String { - let svg = format!( - r#" + let mut svg = String::from(r#" - {} - "#, - board.grid.iter().enumerate().map(|(i, row)| { - row.iter().enumerate().map(|(j, cell)| { - let x = j as f32 * 45.0; - let y = i as f32 * 45.0; - match cell { - CellContent::Empty => format!( - r#""#, - x, y - ), - CellContent::Player => format!( - r#" - "#, - x, y, x + 20.0, y + 20.0 - ), - CellContent::Trap => format!( - r#" - "#, - x, y, x + 5.0, y + 5.0 - ), - } - }).collect::() - }).collect::() - ); - - format!( - r#"data:image/svg+xml,{}"#, - urlencoding::encode(&svg) - ) + "#); + + // Draw grid and traps + for (i, row) in board.grid.iter().enumerate() { + for (j, cell) in row.iter().enumerate() { + let x = j as f32 * 45.0; + let y = i as f32 * 45.0; + let _ = match cell { + CellContent::Empty | CellContent::Player => write!( + svg, + r#""#, + x, y + ), + CellContent::Trap => write!( + svg, + r#" + "#, + x, y, x + 5.0, y + 5.0 + ), + }; + } + } + + // Draw numbered circles for moves + for (idx, &(i, j)) in board.moves.iter().enumerate() { + let x = j as f32 * 45.0; + let y = i as f32 * 45.0; + let _ = write!( + svg, + r#" + {}"#, + x + 20.0, y + 20.0, x + 20.0, y + 20.0, idx + 1 + ); + } + + svg.push_str(""); + format!(r#"data:image/svg+xml,{}"#, urlencoding::encode(&svg)) +} + +pub fn save_board(board: Board) -> Result<(), serde_json::Error> { + let storage = window().unwrap().local_storage().unwrap().unwrap(); + let thumbnail = generate_thumbnail(&board); + let saved_board = SavedBoard { board, thumbnail }; + + // Load existing boards first + let mut saved_boards = load_saved_boards().unwrap_or_default(); + web_sys::console::log_1(&format!("Current boards: {}", saved_boards.len()).into()); + + saved_boards.push(saved_board); + web_sys::console::log_1(&format!("After adding: {}", saved_boards.len()).into()); + + let json = serde_json::to_string(&saved_boards)?; + storage.set_item("saved_boards", &json).unwrap(); + Ok(()) } + +pub fn load_saved_boards() -> Option> { + let storage = window().unwrap().local_storage().unwrap().unwrap(); + let data = storage.get_item("saved_boards").ok()??; + serde_json::from_str(&data).ok() +} + +// In utils.rs, add this function: +pub fn delete_board(index: usize) -> Result<(), serde_json::Error> { + let storage = window().unwrap().local_storage().unwrap().unwrap(); + let mut saved_boards = load_saved_boards().unwrap_or_default(); + saved_boards.remove(index); + let json = serde_json::to_string(&saved_boards)?; + storage.set_item("saved_boards", &json).unwrap(); + Ok(()) +} \ No newline at end of file