Initial commit
This commit is contained in:
77
src/app.rs
Normal file
77
src/app.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
// use serde_wasm_bindgen::to_value;
|
||||
use wasm_bindgen::prelude::*;
|
||||
// use web_sys::EventTarget;
|
||||
// use wasm_bindgen_futures::spawn_local;
|
||||
use yew::{prelude::*, html::Scope};
|
||||
|
||||
use log::info;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use minesweeper_ui::components::{button::Button, board::BoardComponent};
|
||||
|
||||
use minesweeper_ui::minesweeper::{cell::Cell, board::Board, Game};
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "tauri"])]
|
||||
async fn invoke(cmd: &str, args: JsValue) -> JsValue;
|
||||
}
|
||||
|
||||
pub enum Msg {
|
||||
Discover{cell: Cell}
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
link: Scope<Self>,
|
||||
game: Game
|
||||
}
|
||||
|
||||
|
||||
// #[function_component(App)]
|
||||
// pub fn app() -> Html {
|
||||
// html! {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Component for App {
|
||||
type Message = Msg;
|
||||
|
||||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
let mut game = Game::new(25, 40, 50);
|
||||
game.start_board();
|
||||
|
||||
info!("\n{}", game.get_board().to_string());
|
||||
|
||||
Self {
|
||||
link: ctx.link().clone(),
|
||||
game
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let b = self.game.get_board().clone();
|
||||
|
||||
html!{
|
||||
<main class="container">
|
||||
<BoardComponent onsignal={self.link.callback(|cell| Msg::Discover{cell})} board={b}/>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::Discover {cell} => {
|
||||
// info!("Pos (from App): {}", format!("{:?}", cell.get_pos()));
|
||||
self.game.show(cell.get_pos());
|
||||
|
||||
// info!("celhid (from App): {}", format!("{:?}", self.game.get_cell(cell.get_pos()).is_hidden()));
|
||||
info!("\n{}", self.game.get_board().to_string())
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
77
src/components/board.rs
Normal file
77
src/components/board.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use yew::{prelude::*, html::Scope};
|
||||
|
||||
use crate::minesweeper::{board::Board, cell::Cell};
|
||||
|
||||
use crate::components::button::Button;
|
||||
|
||||
use log::info;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
pub struct BoardComponent {
|
||||
link: Scope<Self>,
|
||||
board: Board,
|
||||
onsignal: Callback<Cell>
|
||||
}
|
||||
|
||||
pub enum Msg {
|
||||
Discover{ cell: Cell },
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
pub board: Board,
|
||||
pub onsignal: Callback<Cell>,
|
||||
}
|
||||
|
||||
impl Component for BoardComponent {
|
||||
type Message = Msg;
|
||||
|
||||
type Properties = Props;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
link: ctx.link().clone(),
|
||||
board: ctx.props().board.clone(),
|
||||
onsignal: ctx.props().onsignal.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
|
||||
let b = ctx.props().board.get_board().to_owned();
|
||||
|
||||
html! {
|
||||
<div class="board">
|
||||
{b.into_iter().map( |row| {
|
||||
html! {
|
||||
<>
|
||||
{row.into_iter().map(|c| {
|
||||
html! {
|
||||
<Button onsignal={self.link.callback(move |_| Msg::Discover{cell: c})} cell={c}/>
|
||||
}
|
||||
}).collect::<Html>()}
|
||||
<br/>
|
||||
</>
|
||||
}
|
||||
}).collect::<Html>()}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::Discover {cell} => {
|
||||
// info!("Pos (from board): {}", format!("{:?}", cell.get_pos()));
|
||||
self.onsignal.emit(cell)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||
self.board = ctx.props().board.clone();
|
||||
self.onsignal = ctx.props().onsignal.clone();
|
||||
true
|
||||
}
|
||||
|
||||
}
|
||||
61
src/components/button.rs
Normal file
61
src/components/button.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
use yew::{prelude::*, html::Scope};
|
||||
|
||||
use crate::minesweeper::cell::Cell;
|
||||
|
||||
use log::info;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
pub struct Button {
|
||||
link: Scope<Self>,
|
||||
cell: Cell,
|
||||
onsignal: Callback<Cell>,
|
||||
}
|
||||
|
||||
pub enum Msg {
|
||||
Clicked,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
pub cell: Cell,
|
||||
pub onsignal: Callback<Cell>,
|
||||
}
|
||||
|
||||
impl Component for Button {
|
||||
type Message = Msg;
|
||||
type Properties = Props;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
link: ctx.link().clone(),
|
||||
cell: ctx.props().cell.clone(),
|
||||
onsignal: ctx.props().onsignal.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
|
||||
html!{
|
||||
<button class={if self.cell.is_hidden() {"button-hidden"} else {"button-shown"} } onclick={self.link.callback(|_| Msg::Clicked)}>
|
||||
{ &self.cell.to_string() }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::Clicked => {
|
||||
// info!("hid (from cell): {}", format!("{:?}", self.cell.is_hidden()));
|
||||
self.onsignal.emit(self.cell)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||
self.cell = ctx.props().cell.clone();
|
||||
self.onsignal = ctx.props().onsignal.clone();
|
||||
true
|
||||
}
|
||||
}
|
||||
2
src/components/mod.rs
Normal file
2
src/components/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod button;
|
||||
pub mod board;
|
||||
2
src/lib.rs
Normal file
2
src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod minesweeper;
|
||||
pub mod components;
|
||||
8
src/main.rs
Normal file
8
src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
mod app;
|
||||
|
||||
use app::App;
|
||||
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::default());
|
||||
yew::Renderer::<App>::new().render();
|
||||
}
|
||||
107
src/minesweeper/board.rs
Normal file
107
src/minesweeper/board.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
use super::cell::Cell;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Board {
|
||||
board: Vec<Vec<Cell>>,
|
||||
height: usize,
|
||||
width: usize,
|
||||
num_mines: usize
|
||||
}
|
||||
|
||||
impl Board {
|
||||
pub fn new(height: usize, width: usize, num_mines: usize) -> Self {
|
||||
let mut t: Vec<Vec<Cell>> = Vec::new();
|
||||
for i in 0..height {
|
||||
t.push(Vec::new());
|
||||
for j in 0..width {
|
||||
t[i].push(Cell::new((i, j)))
|
||||
}
|
||||
}
|
||||
|
||||
return Self {
|
||||
board: t,
|
||||
height,
|
||||
width,
|
||||
num_mines
|
||||
};
|
||||
}
|
||||
|
||||
pub fn calculate_value(&self, pos: (usize, usize)) -> usize {
|
||||
let mut value: usize = 0;
|
||||
for i in -1..=1 {
|
||||
for j in -1..=1 {
|
||||
let new_pos: (isize, isize) = ((pos.0 as isize) + i, (pos.1 as isize) + j);
|
||||
if new_pos.0 >= 0 && new_pos.0 < (self.height as isize) && new_pos.1 >= 0 && new_pos.1 < (self.width as isize) {
|
||||
value += self.is_mine((new_pos.0 as usize, new_pos.1 as usize)) as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
pub fn get_board(&self) -> &Vec<Vec<Cell>> {
|
||||
return &self.board;
|
||||
}
|
||||
|
||||
pub fn set_mine(&mut self, pos: (usize, usize), value: bool) {
|
||||
self.board[pos.0][pos.1].set_mine(value);
|
||||
}
|
||||
|
||||
pub fn get_height(&self) -> usize {
|
||||
return self.height;
|
||||
}
|
||||
|
||||
pub fn get_width(&self) -> usize {
|
||||
return self.width;
|
||||
}
|
||||
|
||||
pub fn get_num_mines(&self) -> usize {
|
||||
return self.num_mines;
|
||||
}
|
||||
|
||||
pub fn is_mine(&self, pos: (usize, usize)) -> bool {
|
||||
return self.board[pos.0][pos.1].is_mine();
|
||||
}
|
||||
|
||||
pub fn get_value(&self, pos: (usize, usize)) -> usize {
|
||||
return self.board[pos.0][pos.1].get_value();
|
||||
}
|
||||
|
||||
pub fn get_cell(&self, pos: (usize, usize)) -> Cell {
|
||||
return self.board[pos.0][pos.1];
|
||||
}
|
||||
|
||||
pub fn set_value(&mut self, pos: (usize, usize), new_value: usize) {
|
||||
return self.board[pos.0][pos.1].set_value(new_value)
|
||||
}
|
||||
|
||||
pub fn show_cell(&mut self, pos: (usize, usize)) {
|
||||
self.board[pos.0][pos.1].show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl ToString for Board {
|
||||
fn to_string(&self) -> String {
|
||||
let mut result = String::new();
|
||||
result.push_str(" ");
|
||||
for i in 0..self.width {
|
||||
// result.push_str(" ");
|
||||
result.push_str(i.to_string().as_str());
|
||||
}
|
||||
result.push_str("\n");
|
||||
|
||||
for i in 0..self.height {
|
||||
result.push_str(i.to_string().as_str());
|
||||
result.push_str(" ");
|
||||
|
||||
for j in 0..self.width {
|
||||
result.push_str(self.get_cell((i, j)).to_string().as_str());
|
||||
}
|
||||
result.push_str("\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
72
src/minesweeper/cell.rs
Normal file
72
src/minesweeper/cell.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct Cell {
|
||||
pos: (usize, usize), // (height, width)
|
||||
mine: bool,
|
||||
value: usize,
|
||||
hidden: bool,
|
||||
flagged: bool
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
pub fn new(pos: (usize, usize)) -> Self {
|
||||
return Self {
|
||||
pos,
|
||||
mine: false,
|
||||
value: 0,
|
||||
hidden: true,
|
||||
flagged: false
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_pos(&self) -> (usize, usize) {
|
||||
return self.pos;
|
||||
}
|
||||
|
||||
pub fn is_mine(&self) -> bool {
|
||||
return self.mine;
|
||||
}
|
||||
|
||||
pub fn set_mine(&mut self, new_mine: bool) {
|
||||
self.mine = new_mine;
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> usize {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
pub fn set_value(&mut self, new_value: usize) {
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
pub fn show(&mut self) {
|
||||
self.hidden = false;
|
||||
}
|
||||
|
||||
pub fn is_hidden(&self) -> bool {
|
||||
return self.hidden;
|
||||
}
|
||||
|
||||
pub fn is_flagged(&self) -> bool {
|
||||
return self.flagged;
|
||||
}
|
||||
|
||||
pub fn set_flag(&mut self, new_flag: bool) {
|
||||
self.flagged = new_flag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl ToString for Cell {
|
||||
fn to_string(&self) -> String {
|
||||
if self.hidden {
|
||||
return " ".to_string();
|
||||
}
|
||||
if self.is_mine() {
|
||||
return "*".to_string();
|
||||
}
|
||||
if self.value == 0 {
|
||||
return " ".to_string();
|
||||
}
|
||||
return self.value.to_string();
|
||||
}
|
||||
}
|
||||
74
src/minesweeper/mod.rs
Normal file
74
src/minesweeper/mod.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
pub mod cell;
|
||||
pub mod board;
|
||||
|
||||
use board::Board;
|
||||
use cell::Cell;
|
||||
use rand::Rng;
|
||||
// use getrandom::getrandom;
|
||||
|
||||
pub struct Game {
|
||||
board: Board,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new(height: usize, width: usize, num_mines: usize) -> Self {
|
||||
Self {
|
||||
board: Board::new(height, width, num_mines),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_board(&mut self){
|
||||
// TODO: make a better implementation
|
||||
let mut added_mines = 0;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
while added_mines < self.board.get_num_mines() {
|
||||
let pos = (rng.gen_range(0..self.board.get_height()), rng.gen_range(0..self.board.get_width()));
|
||||
if !self.board.is_mine(pos) {
|
||||
self.board.set_mine(pos, true);
|
||||
added_mines += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set values
|
||||
for i in 0..self.board.get_height() {
|
||||
for j in 0..self.board.get_width() {
|
||||
self.board.set_value((i, j), self.board.calculate_value((i, j)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show(&mut self, pos: (usize, usize)) {
|
||||
self.board.show_cell(pos);
|
||||
if self.board.get_value(pos) == 0 {
|
||||
for i in -1..=1 {
|
||||
for j in -1..=1 {
|
||||
if pos.0 as isize + i >= 0 &&
|
||||
pos.0 as isize + i < self.get_height() as isize &&
|
||||
pos.1 as isize + j >= 0 &&
|
||||
pos.1 as isize + j < self.get_width() as isize &&
|
||||
self.board.get_cell(((pos.0 as isize + i) as usize , (pos.1 as isize + j) as usize)).is_hidden() {
|
||||
self.show(((pos.0 as isize + i) as usize , (pos.1 as isize + j) as usize))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_height(&self) -> usize {
|
||||
return self.board.get_height();
|
||||
}
|
||||
|
||||
pub fn get_width(&self) -> usize {
|
||||
return self.board.get_width();
|
||||
}
|
||||
|
||||
pub fn get_board(&self) -> &Board {
|
||||
return &self.board;
|
||||
}
|
||||
|
||||
pub fn get_cell(&self,pos: (usize, usize)) -> Cell {
|
||||
self.board.get_cell(pos)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user