You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
8.2 KiB

use crate::hand_view::HandView;
use crate::remote_view::RemoteGameView;
use crate::tile_view::PlacedTileView;
use crate::types::SelectedTile;
use board_shared::board::Board;
use board_shared::deck::RngDeck;
use board_shared::expr::is_valid_guess;
use board_shared::game::Game;
use board_shared::position::Grid2d;
use board_shared::tile::Tile;
use futures::StreamExt;
use gloo_dialogs::alert;
use gloo_net::websocket::futures::WebSocket;
use std::cell::RefCell;
use std::rc::Rc;
use web_sys::HtmlInputElement;
use yew::prelude::*;
#[derive(Properties, PartialEq)]
pub struct BoardViewProps {
pub board: Board,
pub on_click: Callback<(usize, usize)>,
}
#[function_component(BoardView)]
pub fn board_view(BoardViewProps { board, on_click }: &BoardViewProps) -> Html {
html! {
<table class="board">
{ (0..board.height()).map(|y| html! {
<tr class="board-row">
{ (0..board.width()).map(|x| html! {
<PlacedTileView x={x} y={y} key={x} tile={board.get(x, y)} on_click={on_click.clone()} />
}).collect::<Html>() }
</tr>
}).collect::<Html>() }
</table>
}
}
#[function_component(GameView)]
pub fn game_view() -> Html {
let game = use_state(Game::default);
let current_game = use_state(Game::default);
let selected_tile = use_state(|| SelectedTile::None);
let on_tile_click = {
let game = current_game.clone();
let selected_tile = selected_tile.clone();
Callback::from(move |(x, y)| {
if let SelectedTile::InHand(idx) = *selected_tile {
let mut in_hand = game.in_hand.clone();
let tile = in_hand.tiles.remove(idx);
let mut board = game.board.clone();
board.set(x, y, tile);
game.set(Game { board, in_hand });
selected_tile.set(SelectedTile::None);
} else if let SelectedTile::Equals = *selected_tile {
let mut board = game.board.clone();
board.set(x, y, Tile::Equals);
game.set(Game {
board,
in_hand: game.in_hand.clone(),
});
selected_tile.set(SelectedTile::None);
}
})
};
let on_tile_select = {
let selected_tile = selected_tile.clone();
Callback::from(move |idx| {
selected_tile.set(SelectedTile::InHand(idx));
})
};
let on_equals_select = {
Callback::from(move |_| {
selected_tile.set(SelectedTile::Equals);
})
};
let on_continue_click = {
let current_game = current_game.clone();
Callback::from(move |_| {
let diff = game.board.difference(&current_game.board);
let mut deck = RngDeck::new_complete();
if let Some(true) = Board::is_contiguous(&diff) {
if let Ok(true) = is_valid_guess(&current_game.board, &diff) {
alert("Valid move!");
let mut in_hand = current_game.in_hand.clone();
if in_hand.complete(&mut deck).is_err() {
alert("No more tiles left in deck!");
}
game.set(Game {
board: current_game.board.clone(),
in_hand: in_hand.clone(),
});
current_game.set(Game {
board: current_game.board.clone(),
in_hand,
});
} else {
alert("Invalid move! (invalid expressions)");
current_game.set(Game {
board: game.board.clone(),
in_hand: game.in_hand.clone(),
});
}
} else {
if !diff.is_empty() {
alert("Invalid move! (not contiguous)");
}
let mut in_hand = game.in_hand.clone();
if in_hand.complete(&mut deck).is_err() {
alert("No more tiles left in deck!");
}
current_game.set(Game {
board: game.board.clone(),
in_hand,
});
}
})
};
html! {
<main>
<BoardView board={current_game.board.clone()} on_click={on_tile_click} />
<HandView hand={current_game.in_hand.clone()} on_select={on_tile_select} />
<div class="row">
<button onclick={on_equals_select} class="button">{"="}</button>
<button onclick={on_continue_click} class="button">{if current_game.in_hand.tiles.is_empty() { "Start" } else { "Continue" }}</button>
</div>
</main>
}
}
#[function_component(App)]
pub fn app() -> Html {
let remote = use_state(|| "ws://localhost:8081/ws".to_string());
let player_name = use_state(|| None);
let room_name = use_state(|| None);
let remote_ref = use_node_ref();
let player_name_ref = use_node_ref();
let room_name_ref = use_node_ref();
let on_create_click = {
let remote = remote.clone();
let remote_ref = remote_ref.clone();
let player_name = player_name.clone();
let player_name_ref = player_name_ref.clone();
Callback::from(move |_| {
remote.set(
remote_ref
.cast::<HtmlInputElement>()
.expect("remote_ref is not attached to a input element")
.value(),
);
player_name.set(Some(
player_name_ref
.cast::<HtmlInputElement>()
.expect("player_name_ref is not attached to a input element")
.value(),
));
})
};
let on_join_click = {
let remote = remote.clone();
let remote_ref = remote_ref.clone();
let player_name = player_name.clone();
let player_name_ref = player_name_ref.clone();
let room_name = room_name.clone();
let room_name_ref = room_name_ref.clone();
Callback::from(move |_| {
remote.set(
remote_ref
.cast::<HtmlInputElement>()
.expect("remote_ref is not attached to a input element")
.value(),
);
player_name.set(Some(
player_name_ref
.cast::<HtmlInputElement>()
.expect("player_name_ref is not attached to a input element")
.value(),
));
room_name.set(Some(
room_name_ref
.cast::<HtmlInputElement>()
.expect("room_name_ref is not attached to a input element")
.value(),
));
})
};
if let Some(player_name) = (*player_name).as_ref() {
let ws = WebSocket::open(remote.as_ref()).unwrap();
let (write, read) = ws.split();
return html! {
<div class="app">
<RemoteGameView
write={Rc::new(RefCell::new(write))}
read={Rc::new(RefCell::new(read))}
player_name={player_name.clone()}
room_name={(*room_name).clone()} />
</div>
};
}
html! {
<main>
<div>
<label>{"Server: "}</label>
<input type="text" required={true} value={"ws://localhost:8081"} ref={remote_ref} />
</div>
<div>
<label>{"Player name: "}</label>
<input type="text" required={true} value={"test"} ref={player_name_ref} />
</div>
<div style="display: grid; grid-template-columns: 2fr 2fr;">
<div>
<button onclick={on_create_click}>{"Create"}</button>
</div>
<div>
<div>
<label>{"Room name: "}</label>
<input type="text" ref={room_name_ref} />
</div>
<button onclick={on_join_click}>{"Join"}</button>
</div>
</div>
</main>
}
}