diff --git a/fish-webworker/Cargo.lock b/fish-webworker/Cargo.lock index 310626b..0b6c328 100644 --- a/fish-webworker/Cargo.lock +++ b/fish-webworker/Cargo.lock @@ -61,6 +61,7 @@ name = "fish-webworker" version = "0.1.0" dependencies = [ "fish", + "mino", "wasm-bindgen", "wasm-bindgen-macro", "wee_alloc", diff --git a/fish-webworker/Cargo.toml b/fish-webworker/Cargo.toml index 4017507..2d5eb4a 100644 --- a/fish-webworker/Cargo.toml +++ b/fish-webworker/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib"] [dependencies] fish = { path = "../fish" } +mino = { path = "../mino" } wasm-bindgen = "0.2" wasm-bindgen-macro = "0.2" diff --git a/fish-webworker/src/lib.rs b/fish-webworker/src/lib.rs index d81041f..e12819f 100644 --- a/fish-webworker/src/lib.rs +++ b/fish-webworker/src/lib.rs @@ -1,7 +1,9 @@ use std::{cell::RefCell, mem::MaybeUninit}; use wasm_bindgen_macro::wasm_bindgen; -use fish::Weights; +use fish::{Bot, Weights}; +use mino::srs::{PieceType, Queue}; +use mino::{MatBuf, Rot}; #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; @@ -13,18 +15,22 @@ struct State { static mut STATE: MaybeUninit> = MaybeUninit::uninit(); -fn state() -> &'static RefCell { - unsafe { STATE.assume_init_ref() } +impl State { + fn new() -> RefCell { + RefCell::new(State { + weights: Weights::default(), + max_iters: 10_000, + }) + } + + fn get() -> &'static RefCell { + unsafe { STATE.assume_init_ref() } + } } #[wasm_bindgen(start)] fn start() { - let init_state = State { - weights: Weights::default(), - max_iters: 10_000, - }; - - unsafe { STATE.write(RefCell::new(init_state)) }; + unsafe { STATE.write(State::new()) }; } #[wasm_bindgen] @@ -34,8 +40,71 @@ pub fn get_version() -> String { #[wasm_bindgen] pub fn get_config() -> Vec { - let state = state().borrow(); + let state = State::get().borrow(); let mut config = Vec::from(state.weights.0); config.push(state.max_iters as i32); config } + +#[wasm_bindgen] +pub fn suggest(matrix: String, hold: String, next: String) -> Result { + let matrix = parse_matrix(&matrix); + let hold = parse_hold(&hold)?; + let next = parse_queue(&next)?; + let queue = Queue::new(hold, &next); + + let state = State::get().borrow(); + + let res = { + let mut bot = Bot::new(&state.weights, &matrix, queue); + bot.think(state.max_iters); + bot.suggest().ok_or("No suggestion found")? + }; + + Ok(format!( + "{x},{y},{r},{ty}", + x = res.loc.x, + y = res.loc.y, + r = rot(res.loc.r), + ty = res.ty + )) +} + +fn rot(r: Rot) -> &'static str { + match r { + Rot::N => "spawn", + Rot::E => "right", + Rot::S => "reverse", + Rot::W => "left", + } +} + +fn parse_matrix(s: &str) -> MatBuf { + s.bytes() + .take(400) + .enumerate() + .filter(|(_, c)| *c != b' ') + .map(|(i, _)| ((i % 10) as i16, (i / 10) as i16)) + .fold(MatBuf::new(), |mut mat, (x, y)| { + mat.set(x, y); + mat + }) +} + +fn parse_hold(s: &str) -> Result, &'static str> { + match s.len() { + 0 => Ok(None), + 1 => { + let c = s.as_bytes()[0]; + let ty = PieceType::try_from(c as char).map_err(|_| "Unrecognized hold piece")?; + Ok(Some(ty)) + } + _ => Err("Expected hold piece to be a single character"), + } +} + +fn parse_queue(s: &str) -> Result, &'static str> { + s.bytes() + .map(|c| PieceType::try_from(c as char).map_err(|_| "Unrecognized next piece")) + .collect() +} diff --git a/fish-webworker/src/worker.js b/fish-webworker/src/worker.js index ceab301..7ec8729 100644 --- a/fish-webworker/src/worker.js +++ b/fish-webworker/src/worker.js @@ -2,6 +2,7 @@ import { default as init, get_version as getVersion, get_config as getConfig, + suggest, } from '../build/fish_webworker.js' let initPromise = init('/blockfish.wasm'); @@ -16,6 +17,14 @@ async function handle(msg) { return { version, config: { weights, maxIters } }; } + case 'suggest': + { + let res = suggest(msg.state.matrix, msg.state.hold, msg.state.next); + let [x, y, r, type] = res.split(','); + x = +x, y = +y; + return { moves: [{ x, y, r, type }] }; + } + default: throw `Unrecognized message "${msg.type}"`; }