Skip to content

Commit

Permalink
Board Creator show/hide
Browse files Browse the repository at this point in the history
  • Loading branch information
randallard committed Nov 29, 2024
1 parent f5c2efc commit d204f01
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 1 deletion.
141 changes: 141 additions & 0 deletions src/components/board.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use leptos::*;
use leptos::prelude::*;
use serde::{Serialize, Deserialize};

#[derive(Clone, Serialize, Deserialize)]
pub enum CellContent {
Empty,
Player,
Trap,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct Board {
grid: Vec<Vec<CellContent>>,
size: usize,
}

impl Board {
pub fn new(size: usize) -> Self {
Board {
grid: vec![vec![CellContent::Empty; size]; size],
size,
}
}
}

#[component]
pub fn BoardCreator(
#[prop(into)] on_cancel: Callback<()>,
) -> impl IntoView {
let board = RwSignal::new(Board::new(2));
let current_turn = RwSignal::new(0);
let finished = RwSignal::new(false);

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;
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(&current_board);
if let Some((player_row, player_col)) = player_pos {
if is_adjacent(player_row, player_col, row, col) {
if row == 0 {
// Reached top row - game can be finished
current_board.grid[row][col] = CellContent::Player;
board.set(current_board);
finished.set(true);
} else {
current_board.grid[row][col] = CellContent::Player;
current_board.grid[player_row][player_col] = CellContent::Empty;
board.set(current_board);
current_turn.update(|t| *t += 1);
}
}
}
}
};

let rows = move || (0..2).collect::<Vec<_>>();
let cols = move || (0..2).collect::<Vec<_>>();

view! {
<div class="flex flex-col gap-4">
<div class="grid grid-cols-2 gap-1 bg-slate-800 p-2 rounded">
<For
each=rows
key=|row| *row
children=move |row| {
view! {
<For
each=cols
key=|col| *col
children=move |col| {
let cell_content = move || {
match board.get().grid[row][col] {
CellContent::Empty => "·",
CellContent::Player => "○",
CellContent::Trap => "×",
}
};

view! {
<button
class="w-16 h-16 flex items-center justify-center bg-slate-700 hover:bg-slate-600 text-2xl"
on:click=move |_| handle_cell_click(row, col)
>
{cell_content()}
</button>
}
}
/>
}
}
/>
</div>
<div class="text-gray-300">
{move || if current_turn.get() == 0 {
"Place your piece in the bottom row"
} else if finished.get() {
"Board complete!"
} else {
"Move your piece or place a trap in adjacent squares"
}}
</div>
<div class="flex gap-2">
<button
class="px-4 py-2 bg-gray-600 rounded hover:bg-gray-700"
on:click=move |_| on_cancel.run(())
>
"Cancel"
</button>
{move || finished.get().then(|| view! {
<button class="px-4 py-2 bg-blue-600 rounded hover:bg-blue-700">
"Save Board"
</button>
})}
</div>
</div>
}
}

fn find_player(board: &Board) -> Option<(usize, usize)> {
for i in 0..board.size {
for j in 0..board.size {
if matches!(board.grid[i][j], CellContent::Player) {
return Some((i, j));
}
}
}
None
}

fn is_adjacent(x1: usize, y1: usize, x2: usize, y2: usize) -> bool {
let dx = if x1 >= x2 { x1 - x2 } else { x2 - x1 };
let dy = if y1 >= y2 { y1 - y2 } else { y2 - y1 };
(dx == 1 && dy == 0) || (dx == 0 && dy == 1)
}
1 change: 1 addition & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod board;
20 changes: 19 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use leptos::prelude::*;
use web_sys::{MouseEvent, Storage, window};
use serde::{Serialize, Deserialize};

mod components;
use components::board::BoardCreator;

#[derive(Serialize, Deserialize)]
struct UserData {
name: String,
Expand Down Expand Up @@ -36,6 +39,7 @@ fn App() -> impl IntoView {
let (name, set_name) = signal(String::new());
let (greeting, set_greeting) = signal(String::new());
let (show_form, set_show_form) = signal(true);
let (show_board_creator, set_show_board_creator) = signal(false);

if let Some(data) = load_user_data() {
set_name.set(data.name);
Expand Down Expand Up @@ -97,7 +101,21 @@ fn App() -> impl IntoView {
</div>
<div>
<h2 class="text-2xl font-bold mb-4">"Boards"</h2>
<a href="#" class="text-blue-400 hover:text-blue-300 block mb-2">"+ Create New Board"</a>
<a
href="#"
class="text-blue-400 hover:text-blue-300 block mb-2"
on:click=move |ev| {
ev.prevent_default();
set_show_board_creator.set(true);
}
>
"+ Create New Board"
</a>
{move || show_board_creator.get().then(|| view! {
<BoardCreator
on_cancel=move || set_show_board_creator.set(false)
/>
})}
</div>
</div>
})}
Expand Down

0 comments on commit d204f01

Please sign in to comment.