diff --git a/Cargo.toml b/Cargo.toml index b477739..1c0feea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,8 @@ crate-type = ["cdylib"] csv = "1.2.2" rand = {version = "0.8.5", features = ["small_rng"]} getrandom = {version = "0.2", features = ["js"]} -wasm-bindgen = "0.2.87" \ No newline at end of file +wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } +serde_json = "1.0" +serde = { version = "1.0.181", features = ["derive"] } +serde-wasm-bindgen = "0.4" + diff --git a/src/board.rs b/src/board.rs index 05752b2..f4d06cd 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,6 +1,7 @@ use std::collections::HashSet; use std::fmt; use std::fmt::{Formatter, Write}; +use serde::{Deserialize, Serialize}; use crate::constants::{ALL_LETTERS_BONUS, GRID_LENGTH, TRAY_LENGTH}; use crate::dictionary::DictionaryImpl; @@ -48,7 +49,7 @@ impl Coordinates { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct Letter { pub text: char, pub points: u32, diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..a3b0beb --- /dev/null +++ b/src/game.rs @@ -0,0 +1,67 @@ +use rand::rngs::SmallRng; +use rand::SeedableRng; +use wasm_bindgen::prelude::wasm_bindgen; +use crate::board::Letter; +use crate::constants::{standard_tile_pool, TRAY_LENGTH}; +use crate::player_interaction::ai::Difficulty; +use crate::player_interaction::Tray; + +pub enum Player { + Human(String), + AI{ + name: String, + difficulty: Difficulty, + } +} + +pub struct PlayerState { + player: Player, + score: u32, + tray: Tray +} + +pub struct Game { + tiles: Vec, + player: PlayerState, +} + +// Problem - I want to provide a UI to the player +// Ideally they would get some kind of 'tray' object with methods to use and run +// However I want the main game state to live in Rust, not in JS. +// Does this mean I provide Rc>? + +// Other option - what if I just have one Game object that exposes all methods. +// At no point do they get a Tray reference that auto-updates, they need to handle that +// I just provide read-only JSON of everything, and they call the methods for updates +// This will later work out well when I build it out as an API for multiplayer. + +impl Game { + pub fn new(seed: u64) -> Self { + let mut rng = SmallRng::seed_from_u64(seed); + + let mut letters = standard_tile_pool(Some(&mut rng)); + let mut tray = Tray::new(TRAY_LENGTH); + tray.fill(&mut letters); + + + let player = PlayerState { + player: Player::Human("Joel".to_string()), + score: 0, + tray, + }; + + Game { + tiles: letters, + player, + } + } + + pub fn get_tray(&self) -> &Tray { + &self.player.tray + } + + pub fn get_tray_mut(&mut self) -> &mut Tray { + &mut self.player.tray + } + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 148d94b..4ee28f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ pub mod constants; pub mod board; pub mod dictionary; pub mod player_interaction; +pub mod game; +pub mod wasm; #[wasm_bindgen] @@ -16,3 +18,4 @@ extern { pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); } + diff --git a/src/player_interaction.rs b/src/player_interaction.rs index dd0d825..3685158 100644 --- a/src/player_interaction.rs +++ b/src/player_interaction.rs @@ -1,8 +1,10 @@ +use serde::{Deserialize, Serialize}; use crate::board::Letter; + pub mod ai; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct Tray { pub letters: Vec> } diff --git a/src/player_interaction/ai.rs b/src/player_interaction/ai.rs index e69de29..38177e8 100644 --- a/src/player_interaction/ai.rs +++ b/src/player_interaction/ai.rs @@ -0,0 +1,5 @@ + +pub struct Difficulty { + proportion: f64, + randomness: f64, +} \ No newline at end of file diff --git a/src/wasm.rs b/src/wasm.rs new file mode 100644 index 0000000..0f8571c --- /dev/null +++ b/src/wasm.rs @@ -0,0 +1,22 @@ +use serde_wasm_bindgen::Error; +use wasm_bindgen::JsValue; +use wasm_bindgen::prelude::wasm_bindgen; +use crate::game::Game; + +#[wasm_bindgen] +pub struct GameWasm(Game); + +#[wasm_bindgen] +impl GameWasm { + + #[wasm_bindgen(constructor)] + pub fn new(seed: u64) -> GameWasm { + GameWasm(Game::new(seed)) + } + + pub fn get_tray(&self) -> Result { + let tray = self.0.get_tray(); + + serde_wasm_bindgen::to_value(tray) + } +} \ No newline at end of file diff --git a/ui/package-lock.json b/ui/package-lock.json index 1acc88c..40ea153 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -5,14 +5,15 @@ "packages": { "": { "dependencies": { - "@types/react": "^18.2.18", - "@types/react-dom": "^18.2.7", "react": "^18.2.0", "react-dom": "^18.2.0", "word_grid": "file:../pkg" }, "devDependencies": { - "parcel": "^2.9.3" + "@types/react": "^18.2.18", + "@types/react-dom": "^18.2.7", + "parcel": "^2.9.3", + "process": "^0.11.10" } }, "../pkg": { @@ -1961,12 +1962,14 @@ "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true }, "node_modules/@types/react": { "version": "18.2.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.18.tgz", "integrity": "sha512-da4NTSeBv/P34xoZPhtcLkmZuJ+oYaCxHmyHzwaDQo9RQPBeXV+06gEk2FpqEcsX9XrnNLvRpVh6bdavDSjtiQ==", + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -1977,6 +1980,7 @@ "version": "18.2.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -1984,7 +1988,8 @@ "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true }, "node_modules/abortcontroller-polyfill": { "version": "1.7.5", @@ -2330,7 +2335,8 @@ "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true }, "node_modules/detect-libc": { "version": "1.0.3", @@ -3182,6 +3188,15 @@ "node": ">=12" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", diff --git a/ui/package.json b/ui/package.json index f7595d5..7829fdb 100644 --- a/ui/package.json +++ b/ui/package.json @@ -5,8 +5,9 @@ "word_grid": "file:../pkg" }, "devDependencies": { - "parcel": "^2.9.3", "@types/react": "^18.2.18", - "@types/react-dom": "^18.2.7" + "@types/react-dom": "^18.2.7", + "parcel": "^2.9.3", + "process": "^0.11.10" } } diff --git a/ui/src/index.html b/ui/src/index.html index 7ab2d36..fccece6 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -6,6 +6,7 @@ + diff --git a/ui/src/index.js b/ui/src/index.js index b1331d0..8f4415e 100644 --- a/ui/src/index.js +++ b/ui/src/index.js @@ -5,7 +5,7 @@ // will "boot" the module and make it ready to use. Currently browsers // don't support natively imported WebAssembly as an ES module, but // eventually the manual initialization won't be required! - import init, { greet } from '../node_modules/word_grid/word_grid.js'; + import init, { greet, GameWasm } from '../node_modules/word_grid/word_grid.js'; async function run() { // First up we need to actually load the wasm file, so we use the @@ -36,8 +36,15 @@ // modes await init(); + greet("Heyo!"); + + let game = new GameWasm(1234n); + let tray = game.get_tray(); + + console.log({tray}); + // And afterwards we can use all the functionality defined in wasm. - greet("Heyo!"); + } run();