195 lines
5 KiB
Rust
195 lines
5 KiB
Rust
use rand::seq::SliceRandom;
|
|
use sycamore::prelude::*;
|
|
use wasm_bindgen::{prelude::*, JsCast};
|
|
use web_sys::{Element, Event};
|
|
|
|
macro_rules! wasm_import {
|
|
($($tt:tt)*) => {
|
|
#[wasm_bindgen]
|
|
extern "C" {
|
|
#[wasm_bindgen]
|
|
pub fn $($tt)*;
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! wasm_import_with_ns {
|
|
($ns: ident, $($tt:tt)*) => {
|
|
#[wasm_bindgen]
|
|
extern "C" {
|
|
#[wasm_bindgen(js_namespace = $ns)]
|
|
pub fn $($tt)*;
|
|
}
|
|
};
|
|
}
|
|
|
|
wasm_import!(reset_cards());
|
|
wasm_import!(alert(s: &str));
|
|
wasm_import_with_ns!(console, log(s: &str));
|
|
wasm_import_with_ns!(Date, now() -> u32);
|
|
|
|
wasm_import!(setTimeout(closure: &Closure<dyn Fn()>, time: u32));
|
|
|
|
enum AppMode {
|
|
StartScreen,
|
|
GameScreen,
|
|
}
|
|
|
|
#[derive(Prop)]
|
|
struct ModeProp<'a> {
|
|
mode: &'a Signal<AppMode>,
|
|
}
|
|
|
|
fn main() {
|
|
sycamore::render(|ctx| {
|
|
console_error_panic_hook::set_once();
|
|
|
|
let mode = ctx.create_signal(AppMode::StartScreen);
|
|
|
|
view! {ctx,
|
|
div(class="wrapper") {
|
|
h1(class="text-align-center") { "RemembeRS" }
|
|
(match *mode.get() {
|
|
AppMode::StartScreen => view! {ctx,
|
|
StartComponent {
|
|
mode: mode
|
|
}
|
|
},
|
|
AppMode::GameScreen => view! {ctx,
|
|
GameComponent {
|
|
mode: mode
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
#[component]
|
|
fn StartComponent<'a, G: Html>(ctx: ScopeRef<'a>, props: ModeProp<'a>) -> View<G> {
|
|
let on_click = |_| {
|
|
props.mode.set(AppMode::GameScreen);
|
|
};
|
|
|
|
view! {ctx,
|
|
div(class="start-button-container") {
|
|
button(on:click=on_click) {
|
|
"Start"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
fn GameComponent<'a, G: Html>(ctx: ScopeRef<'a>, _props: ModeProp<'a>) -> View<G> {
|
|
let mut cards = ["burger", "fries", "hotdog", "soda", "nachos", "tacos"]
|
|
.into_iter()
|
|
.cycle()
|
|
.take(12)
|
|
// .map(|s| s.to_string())
|
|
.collect::<Vec<_>>();
|
|
|
|
cards.shuffle(&mut rand::rngs::OsRng);
|
|
|
|
let v = ctx.create_signal(cards);
|
|
|
|
let first: &Signal<Option<Element>> = ctx.create_signal(None);
|
|
let matches = ctx.create_signal(0u8);
|
|
|
|
let start_time = ctx.create_signal(now() / 1000);
|
|
|
|
let on_click = |event: Event| {
|
|
let elem = event
|
|
.current_target()
|
|
.unwrap()
|
|
.dyn_ref::<Element>()
|
|
.unwrap()
|
|
.clone();
|
|
|
|
if elem.class_list().contains("disabled") {
|
|
return;
|
|
}
|
|
|
|
elem.class_list().toggle("flip").unwrap();
|
|
|
|
if let Some(ref felem) = *first.get() {
|
|
if elem == *felem {
|
|
first.set(None);
|
|
return;
|
|
}
|
|
let attr1 = felem.get_attribute("data_value");
|
|
let attr2 = elem.get_attribute("data_value");
|
|
|
|
if attr1 == attr2 {
|
|
// these are all ok to unwrap, because it should never ever fail
|
|
felem.class_list().toggle("disabled").unwrap();
|
|
elem.class_list().toggle("disabled").unwrap();
|
|
matches.set(*matches.get() + 1);
|
|
} else {
|
|
let felem = felem.clone();
|
|
let cb = Closure::wrap(Box::new(move || {
|
|
felem.class_list().toggle("flip").unwrap();
|
|
elem.class_list().toggle("flip").unwrap();
|
|
}) as Box<dyn Fn()>);
|
|
|
|
setTimeout(&cb, 750);
|
|
|
|
cb.forget();
|
|
}
|
|
|
|
first.set(None);
|
|
} else {
|
|
first.set(Some(elem));
|
|
}
|
|
|
|
if (*matches.get()) == 6 {
|
|
let curr_time = now() / 1000;
|
|
let diff = curr_time - *start_time.get();
|
|
log(&format_time(diff));
|
|
alert(&format!(
|
|
"You won! Took {}. Press OK to restart",
|
|
format_time(diff)
|
|
));
|
|
reset_cards();
|
|
matches.set(0);
|
|
let mut c = (*v.get()).clone();
|
|
c.shuffle(&mut rand::rngs::OsRng);
|
|
v.set(c);
|
|
start_time.set(now() / 1000);
|
|
}
|
|
};
|
|
|
|
view! {ctx,
|
|
section(id="game") {
|
|
Keyed {
|
|
iterable: v,
|
|
view: move |ctx, i| view! {ctx,
|
|
div(class="card", on:click=on_click, data_value=i.clone()) {
|
|
h2(class="back-face") {
|
|
(i)
|
|
}
|
|
div(class="front-face")
|
|
}
|
|
},
|
|
key: |x| x.clone()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn add_leading_zeroes(num: u32) -> String {
|
|
if num < 10 {
|
|
format!("0{}", num)
|
|
} else {
|
|
format!("{}", num)
|
|
}
|
|
}
|
|
|
|
fn format_time(inp: u32) -> String {
|
|
let minutes = add_leading_zeroes((inp / 60) % 60);
|
|
let seconds = add_leading_zeroes(inp % 60);
|
|
|
|
format!("{}:{}", minutes, seconds)
|
|
}
|