From c7ccf5772391f214239186aa3746b612ada7f7ed Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sun, 20 Sep 2026 07:03:34 +0200 Subject: [PATCH] 0.3.0-0-pre.3 --- Cargo.toml | 5 +- README.md | 4 +- crates/apps/game-snake-poc-wasm/Cargo.toml | 22 ++ crates/apps/game-snake-poc-wasm/src/lib.rs | 13 ++ .../apps/game-snake-poc-wasm/src/runtime.rs | 188 ++++++++++++++++++ .../game-snake-poc-wasm/unit_tests/runtime.rs | 27 +++ deltas/0.3.0/0-pre.3.md | 118 +++++++++++ docs/000-README.md | 4 +- .../001-WORKSPACE_ARCHITECTURE.md | 18 +- docs/architecture/004-INPUT_AND_CONTROLS.md | 6 +- docs/development/009-SNAKE_WEB_WASM_BUILD.md | 53 +++++ docs/plans/001-V0_3_0_WEB_SNAKE_POC_PLAN.md | 8 +- history/0.3.0/0-pre.2.md | 38 ++++ scripts/audit_distribution_layout.py | 3 +- 14 files changed, 486 insertions(+), 21 deletions(-) create mode 100644 crates/apps/game-snake-poc-wasm/Cargo.toml create mode 100644 crates/apps/game-snake-poc-wasm/src/lib.rs create mode 100644 crates/apps/game-snake-poc-wasm/src/runtime.rs create mode 100644 crates/apps/game-snake-poc-wasm/unit_tests/runtime.rs create mode 100644 deltas/0.3.0/0-pre.3.md create mode 100644 docs/development/009-SNAKE_WEB_WASM_BUILD.md create mode 100644 history/0.3.0/0-pre.2.md diff --git a/Cargo.toml b/Cargo.toml index 982b701..794f5fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ # file: Cargo.toml -# version: 61 +# version: 62 [workspace] resolver = "3" @@ -16,10 +16,11 @@ members = [ "crates/apps/game-android-entrypoint", "crates/apps/game-reflex-poc-tauri", "crates/apps/game-reflex-poc-wasm", + "crates/apps/game-snake-poc-wasm", ] [workspace.package] -version = "0.3.0-0-pre.2" +version = "0.3.0-0-pre.3" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/games" diff --git a/README.md b/README.md index ee7dba4..8d6e6e3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ - + # games.sasedev @@ -25,7 +25,7 @@ Workspace expérimental puis productif pour des jeux multiplateformes principale Version stable de référence : `0.2.0`. -Version de développement : `0.3.0-0-pre.2`. +Version de développement : `0.3.0-0-pre.3`. Les deux premiers jeux sont des POC structurels : `game-reflex-poc` et `game-snake-poc`. Ils existent d'abord pour valider les frontières du workspace, le moteur, les assets et le packaging multiplateforme. diff --git a/crates/apps/game-snake-poc-wasm/Cargo.toml b/crates/apps/game-snake-poc-wasm/Cargo.toml new file mode 100644 index 0000000..f71134f --- /dev/null +++ b/crates/apps/game-snake-poc-wasm/Cargo.toml @@ -0,0 +1,22 @@ +# file: crates/apps/game-snake-poc-wasm/Cargo.toml +# version: 1 + +[package] +name = "game-snake-poc-wasm" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +authors.workspace = true +publish.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +engine-v1-common = { path = "../../engines/engine-v1-common" } +game-snake-poc = { path = "../../games/game-snake-poc" } +wasm-bindgen.workspace = true + +[lints] +workspace = true diff --git a/crates/apps/game-snake-poc-wasm/src/lib.rs b/crates/apps/game-snake-poc-wasm/src/lib.rs new file mode 100644 index 0000000..e0fb8af --- /dev/null +++ b/crates/apps/game-snake-poc-wasm/src/lib.rs @@ -0,0 +1,13 @@ +// file: crates/apps/game-snake-poc-wasm/src/lib.rs +// version: 1 + +//! WebAssembly adapter facade for the Snake POC. + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +mod runtime; + +/// Re-export of the Snake WebAssembly runtime adapter. +pub use self::runtime::SnakeWasmGame; diff --git a/crates/apps/game-snake-poc-wasm/src/runtime.rs b/crates/apps/game-snake-poc-wasm/src/runtime.rs new file mode 100644 index 0000000..bc29b30 --- /dev/null +++ b/crates/apps/game-snake-poc-wasm/src/runtime.rs @@ -0,0 +1,188 @@ +// file: crates/apps/game-snake-poc-wasm/src/runtime.rs +// version: 1 + +const FIXED_STEP_MILLIS: u64 = 16; + +/// WebAssembly-owned Snake game state consumed by the direct browser host. +#[wasm_bindgen::prelude::wasm_bindgen] +pub struct SnakeWasmGame { + pending_input: engine_v1_common::InputState, + runner: engine_v1_common::FixedStepRunner, + state: game_snake_poc::SnakeState, +} + +#[wasm_bindgen::prelude::wasm_bindgen] +impl SnakeWasmGame { + /// Creates a fresh Snake POC session using the shared gameplay crate. + #[wasm_bindgen::prelude::wasm_bindgen(constructor)] + pub fn new() -> Self { + return Self { + pending_input: engine_v1_common::InputState::none(), + runner: engine_v1_common::FixedStepRunner::new(std::time::Duration::from_millis(FIXED_STEP_MILLIS)), + state: game_snake_poc::SnakeState::new(), + }; + } + + /// Queues a logical left direction for the next deterministic update. + pub fn left(&mut self) { + self.queue_direction(engine_v1_common::GameAction::Left); + return; + } + + /// Queues a logical right direction for the next deterministic update. + pub fn right(&mut self) { + self.queue_direction(engine_v1_common::GameAction::Right); + return; + } + + /// Queues a logical upward direction for the next deterministic update. + pub fn up(&mut self) { + self.queue_direction(engine_v1_common::GameAction::Up); + return; + } + + /// Queues a logical downward direction for the next deterministic update. + pub fn down(&mut self) { + self.queue_direction(engine_v1_common::GameAction::Down); + return; + } + + /// Advances one deterministic update and consumes the queued direction. + pub fn tick(&mut self) { + let input = self.pending_input; + self.pending_input = engine_v1_common::InputState::none(); + self.runner.tick(&mut self.state, input); + return; + } + + /// Returns the current gameplay score. + pub fn score(&self) -> u32 { + return self.state.score().min(u32::MAX as u64) as u32; + } + + /// Returns the current Snake length. + pub fn length(&self) -> u32 { + return self.state.length().min(u32::MAX as usize) as u32; + } + + /// Returns the current scene background red channel. + pub fn background_red(&self) -> u8 { + return self.scene().background().red(); + } + + /// Returns the current scene background green channel. + pub fn background_green(&self) -> u8 { + return self.scene().background().green(); + } + + /// Returns the current scene background blue channel. + pub fn background_blue(&self) -> u8 { + return self.scene().background().blue(); + } + + /// Returns the number of occupied rectangle slots in the current scene. + pub fn rectangle_count(&self) -> u32 { + let scene = self.scene(); + let mut count = 0_u32; + for slot in scene.rectangles() { + if slot.is_some() { + count = count.saturating_add(1); + } + } + return count; + } + + /// Returns one rectangle's normalized left coordinate or `-1.0` if absent. + pub fn rectangle_x(&self, index: u32) -> f32 { + return match self.rectangle(index) { + Some(rectangle) => rectangle.rect().x(), + None => -1.0, + }; + } + + /// Returns one rectangle's normalized top coordinate or `-1.0` if absent. + pub fn rectangle_y(&self, index: u32) -> f32 { + return match self.rectangle(index) { + Some(rectangle) => rectangle.rect().y(), + None => -1.0, + }; + } + + /// Returns one rectangle's normalized width or `0.0` if absent. + pub fn rectangle_width(&self, index: u32) -> f32 { + return match self.rectangle(index) { + Some(rectangle) => rectangle.rect().width(), + None => 0.0, + }; + } + + /// Returns one rectangle's normalized height or `0.0` if absent. + pub fn rectangle_height(&self, index: u32) -> f32 { + return match self.rectangle(index) { + Some(rectangle) => rectangle.rect().height(), + None => 0.0, + }; + } + + /// Returns one rectangle's red channel or zero if absent. + pub fn rectangle_red(&self, index: u32) -> u8 { + return match self.rectangle(index) { + Some(rectangle) => rectangle.color().red(), + None => 0, + }; + } + + /// Returns one rectangle's green channel or zero if absent. + pub fn rectangle_green(&self, index: u32) -> u8 { + return match self.rectangle(index) { + Some(rectangle) => rectangle.color().green(), + None => 0, + }; + } + + /// Returns one rectangle's blue channel or zero if absent. + pub fn rectangle_blue(&self, index: u32) -> u8 { + return match self.rectangle(index) { + Some(rectangle) => rectangle.color().blue(), + None => 0, + }; + } +} + +impl Default for SnakeWasmGame { + fn default() -> Self { + return Self::new(); + } +} + +impl SnakeWasmGame { + fn queue_direction(&mut self, action: engine_v1_common::GameAction) { + self.pending_input = engine_v1_common::InputState::none().with_action(action, true); + return; + } + + fn scene(&self) -> engine_v1_common::EngineScene { + return engine_v1_common::EngineGame::scene(&self.state); + } + + fn rectangle(&self, requested_index: u32) -> Option { + let scene = self.scene(); + let mut occupied_index = 0_u32; + for slot in scene.rectangles() { + match slot { + Some(rectangle) => { + if occupied_index == requested_index { + return Some(*rectangle); + } + occupied_index = occupied_index.saturating_add(1); + }, + None => {}, + } + } + return None; + } +} + +#[cfg(test)] +#[path = "../unit_tests/runtime.rs"] +mod tests; diff --git a/crates/apps/game-snake-poc-wasm/unit_tests/runtime.rs b/crates/apps/game-snake-poc-wasm/unit_tests/runtime.rs new file mode 100644 index 0000000..6c6634e --- /dev/null +++ b/crates/apps/game-snake-poc-wasm/unit_tests/runtime.rs @@ -0,0 +1,27 @@ +// file: crates/apps/game-snake-poc-wasm/unit_tests/runtime.rs +// version: 1 + +#[test] +fn direction_is_queued_without_advancing_simulation() { + let mut game = crate::SnakeWasmGame::new(); + let before_x = game.rectangle_x(1); + let before_y = game.rectangle_y(1); + game.up(); + assert_eq!(game.rectangle_x(1), before_x); + assert_eq!(game.rectangle_y(1), before_y); + game.tick(); + assert_eq!(game.rectangle_x(1), before_x); + assert!(game.rectangle_y(1) < before_y); +} + +#[test] +fn scene_and_status_are_exposed_for_canvas_host() { + let game = crate::SnakeWasmGame::new(); + assert_eq!(game.score(), 0); + assert_eq!(game.length(), 3); + assert_eq!(game.rectangle_count(), 4); + assert!(game.rectangle_width(0) > 0.0); + assert!(game.rectangle_height(0) > 0.0); + assert_eq!(game.rectangle_x(99), -1.0); + assert_eq!(game.rectangle_y(99), -1.0); +} diff --git a/deltas/0.3.0/0-pre.3.md b/deltas/0.3.0/0-pre.3.md new file mode 100644 index 0000000..d76e445 --- /dev/null +++ b/deltas/0.3.0/0-pre.3.md @@ -0,0 +1,118 @@ + + + +# Delta 0.3.0-0-pre.3 + +## Base + +Base requise : `0.3.0-0-pre.2`. + +La validation complète de `0-pre.2` est consignée dans `history/0.3.0/0-pre.2.md`. + +## Objet + +Introduire l'adapter WASM minimal de Snake avant tout frontend navigateur : + +- ajouter `game-snake-poc-wasm` comme crate d'application dédiée ; +- réutiliser exclusivement `engine-v1-common` et `game-snake-poc` ; +- exposer à `wasm-bindgen` les directions logiques et la scène portable ; +- préserver la cadence de simulation en séparant événement d'entrée et `tick()` ; +- documenter un chemin Cargo + `wasm-bindgen` direct sans orchestrateur Python ; +- ne pas introduire Vite, Bootstrap, DOM, Tauri ou SDL3 dans cette tranche. + +## Version + +La version workspace passe de `0.3.0-0-pre.2` à `0.3.0-0-pre.3`. + +## Adapter Snake WASM + +La nouvelle crate `crates/apps/game-snake-poc-wasm` contient une façade `lib.rs` et un runtime propriétaire séparé. + +`SnakeWasmGame` possède : + +- un `SnakeState` issu de la crate de gameplay ; +- un `FixedStepRunner` à pas déterministe de 16 ms ; +- un `InputState` en attente pour la prochaine update. + +Les appels `left()`, `right()`, `up()` et `down()` ne font pas avancer la simulation. Ils remplacent la direction logique en attente. `tick()` consomme ensuite cette entrée une fois, la remet à zéro et avance le runner d'une update. La cadence reste ainsi sous la responsabilité du futur host navigateur. + +L'API expose également le score, la longueur, la couleur de fond et les rectangles normalisés de l'`EngineScene`. Le futur frontend `0-pre.4` peut donc dessiner le Canvas sans accéder à la structure interne de Snake. + +## Frontières conservées + +`game-snake-poc-wasm` ne dépend pas de : + +- `engine-v1-platform-api` ; +- `engine-v1-sdl` ; +- Tauri ; +- DOM/Canvas Rust ; +- code frontend TypeScript. + +La duplication limitée des getters de scène avec `game-reflex-poc-wasm` est volontairement conservée à ce stade. Une abstraction commune ne sera extraite que si les POC suivants démontrent un besoin partagé suffisamment stable. + +## Documentation et audits + +L'architecture du workspace mentionne désormais l'adapter Snake WASM et le contrat d'entrée précise la file directionnelle consommée par `tick()`. + +L'audit de distribution exige maintenant `crates/apps/game-snake-poc-wasm/Cargo.toml`, portant le nombre de chemins requis de 19 à 20. + +Le plan actif est réconcilié avec la livraison réelle de `0-pre.3`. `docs/development/009-SNAKE_WEB_WASM_BUILD.md` documente le build natif Cargo + `wasm-bindgen`, avec tous les artefacts générés sous `../builds/sasedev-games/`. + +## Historique + +Ajout de `history/0.3.0/0-pre.2.md` avec les résultats réellement fournis par l'utilisateur : audits, formatage, check workspace, Clippy workspace strict, tests Snake/Reflex et graphes de dépendances. + +## Fichiers ajoutés + +- `crates/apps/game-snake-poc-wasm/Cargo.toml` ; +- `crates/apps/game-snake-poc-wasm/src/lib.rs` ; +- `crates/apps/game-snake-poc-wasm/src/runtime.rs` ; +- `crates/apps/game-snake-poc-wasm/unit_tests/runtime.rs` ; +- `deltas/0.3.0/0-pre.3.md` ; +- `docs/development/009-SNAKE_WEB_WASM_BUILD.md` ; +- `history/0.3.0/0-pre.2.md`. + +## Fichiers modifiés + +- `Cargo.toml` ; +- `README.md` ; +- `docs/architecture/001-WORKSPACE_ARCHITECTURE.md` ; +- `docs/000-README.md` ; +- `docs/architecture/004-INPUT_AND_CONTROLS.md` ; +- `docs/plans/001-V0_3_0_WEB_SNAKE_POC_PLAN.md` ; +- `scripts/audit_distribution_layout.py`. + +## Suppressions + +Aucune. + +## Validations applicables + +Audits statiques de préparation : + +```bash +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android deltas history +python3 scripts/audit_distribution_layout.py +``` + +Validation utilisateur demandée : + +```bash +cargo fmt --all +cargo fmt --all -- --check +cargo check --workspace +cargo clippy -p game-snake-poc-wasm --all-targets --all-features -- -D warnings +cargo test -p game-snake-poc-wasm +cargo build -p game-snake-poc-wasm --target wasm32-unknown-unknown +wasm-bindgen ../builds/sasedev-games/target/wasm32-unknown-unknown/debug/game_snake_poc_wasm.wasm --target web --out-dir ../builds/sasedev-games/game-snake-poc-web/wasm --out-name game_snake_poc_wasm +cargo tree -p game-snake-poc-wasm --edges normal +``` + +Le `cargo tree` doit rester limité à `engine-v1-common`, `game-snake-poc`, `wasm-bindgen` et leurs dépendances nécessaires ; aucune dépendance SDL3, Tauri ou `engine-v1-platform-api` ne doit apparaître. + +La commande `wasm-bindgen` doit produire les bindings navigateur sans écrire d'artefact généré dans le dépôt. Aucun build Vite/Bootstrap et aucun smoke navigateur ne sont requis dans cette tranche, car le frontend appartient à `0-pre.4`. + +## Suite + +Après validation de `0-pre.3`, ouvrir `0-pre.4` pour le frontend Web direct Vite/TypeScript, son shell HTML Bootstrap 5, le Canvas et les contrôles clavier/tactiles. diff --git a/docs/000-README.md b/docs/000-README.md index 4cce99e..3efe0ef 100644 --- a/docs/000-README.md +++ b/docs/000-README.md @@ -1,5 +1,5 @@ - + # Documentation games.sasedev @@ -73,6 +73,8 @@ Voir [`../RULES.md`](../RULES.md), notamment [`rules/RULES_DOCUMENTATION.md`](ru - [`development/005-DESKTOP_WINDOW_POLICY.md`](development/005-DESKTOP_WINDOW_POLICY.md) — taille initiale, redimensionnement et séparation future entre fenêtre physique et résolution virtuelle. - [`development/006-ANDROID_RUST_NATIVE_BUILD.md`](development/006-ANDROID_RUST_NATIVE_BUILD.md) — build `cdylib` Rust Android, cargo-ndk, packaging `jniLibs` et symbole `SDL_main`. - [`development/007-ANDROID_JNI_BRIDGE.md`](development/007-ANDROID_JNI_BRIDGE.md) — frontière Java/JNI minimale, version de contrat et séparation avec l'input SDL3. +- [`development/008-WASM_TAURI_POC.md`](development/008-WASM_TAURI_POC.md) — baseline historique Reflex Tauri/WebAssembly et génération WASM associée. +- [`development/009-SNAKE_WEB_WASM_BUILD.md`](development/009-SNAKE_WEB_WASM_BUILD.md) — build Cargo + `wasm-bindgen` natif de l'adapter Snake pour le navigateur direct. ## Historique validé diff --git a/docs/architecture/001-WORKSPACE_ARCHITECTURE.md b/docs/architecture/001-WORKSPACE_ARCHITECTURE.md index 7db7f7f..8259cef 100644 --- a/docs/architecture/001-WORKSPACE_ARCHITECTURE.md +++ b/docs/architecture/001-WORKSPACE_ARCHITECTURE.md @@ -1,5 +1,5 @@ - + # Architecture du workspace @@ -22,7 +22,8 @@ games.sasedev/ │ ├── game-reflex-poc-desktop/ │ ├── game-snake-poc-desktop/ │ ├── game-reflex-poc-tauri/ -│ └── game-reflex-poc-wasm/ +│ ├── game-reflex-poc-wasm/ +│ └── game-snake-poc-wasm/ ├── assets/ │ ├── common/ │ ├── game-reflex-poc/ @@ -69,15 +70,12 @@ Le runtime devra conserver une distinction logique entre ressources communes et Chaque crate de jeu reste une bibliothèque. Une crate binaire Desktop séparée sous `crates/apps/` la consomme pour permettre les itérations locales rapides. Cette séparation évite d’introduire `main`, des choix de plateforme ou du code de lancement dans le gameplay réutilisable. -## Variante Tauri / WebAssembly +## Adapters WebAssembly et variante Tauri -La variante Tauri est distincte du runner SDL3 natif. +Les adapters WASM sont distincts des runners SDL3 natifs et ne déplacent aucune règle de gameplay hors des crates de jeu. -`game-reflex-poc-tauri` porte deux faces du même launcher : +`game-reflex-poc-wasm` adapte le POC Reflex à `wasm-bindgen` pour le host Tauri/WebView existant. `game-snake-poc-wasm` adapte séparément Snake pour le premier host navigateur direct de `0.3.0`. Dans les deux cas, l'adapter expose l'état et la scène portable ; il ne possède pas les règles du jeu. -- une bibliothèque `wasm32-unknown-unknown` qui adapte `game-reflex-poc` à `wasm-bindgen` ; -- un binaire natif Tauri qui héberge la WebView locale. +`game-reflex-poc-tauri` reste le binaire natif Tauri qui héberge la WebView Reflex locale. Son frontend Vite/TypeScript pilote un Canvas et appelle l'adapter Reflex WASM. La crate Tauri suit le modèle des apps Desk KSP : façade `lib.rs`, pont `tauri.rs`, modules propriétaires séparés et frontend local à la crate. -Le frontend statique sous `Tauri/game-reflex-poc/frontend/` pilote un Canvas et appelle l'adaptateur WASM. Les règles Reflex restent exclusivement dans `game-reflex-poc`. - -La crate Tauri suit le modèle des apps Desk KSP : façade `lib.rs`, pont `tauri.rs`, modules propriétaires séparés et frontend Vite/TypeScript local à la crate. +Le futur host Web Snake direct consommera `game-snake-poc-wasm` depuis son propre frontend Vite/TypeScript ; ce frontend n'est pas introduit par la tranche WASM elle-même. diff --git a/docs/architecture/004-INPUT_AND_CONTROLS.md b/docs/architecture/004-INPUT_AND_CONTROLS.md index 2424032..165082d 100644 --- a/docs/architecture/004-INPUT_AND_CONTROLS.md +++ b/docs/architecture/004-INPUT_AND_CONTROLS.md @@ -1,5 +1,5 @@ - + # Abstraction des entrées et contrôles @@ -109,7 +109,9 @@ Le contrat d'entrée Snake retenu pour le premier POC Web est volontairement min - le host traduit clavier, swipe ou boutons virtuels vers les mêmes actions directionnelles ; - le rendu reste exposé par `EngineGame::scene` sous forme d'`EngineScene`, sans dépendance au Canvas ou à SDL3. -Le futur adapter WASM de `0-pre.3` doit donc adapter ce contrat existant plutôt que créer un second modèle d'entrée ou de rendu propre au Web. Les besoins de provenance, monétisation ou autres services plateforme appartiennent au host/launcher et ne justifient pas une dépendance plateforme inutilisée dans la crate de gameplay. +L'adapter WASM de `0-pre.3` adapte ce contrat existant plutôt que créer un second modèle d'entrée ou de rendu propre au Web. Il mémorise au plus une direction logique en attente ; `tick()` la consomme pour le prochain update puis remet l'entrée en attente à zéro. Un événement clavier ou tactile ne fait donc pas avancer la simulation à lui seul. + +Le host Web reste responsable de transformer ses événements physiques ou widgets en appels `left`, `right`, `up` ou `down`, puis de piloter la cadence des `tick()`. Les besoins de provenance, monétisation ou autres services plateforme appartiennent au host/launcher et ne justifient pas une dépendance plateforme inutilisée dans la crate de gameplay. ## Requête de sortie diff --git a/docs/development/009-SNAKE_WEB_WASM_BUILD.md b/docs/development/009-SNAKE_WEB_WASM_BUILD.md new file mode 100644 index 0000000..3f5b8c0 --- /dev/null +++ b/docs/development/009-SNAKE_WEB_WASM_BUILD.md @@ -0,0 +1,53 @@ + + + +# Build WASM du POC Snake Web direct + +## Objectif + +`game-snake-poc-wasm` fournit le bridge Rust/WASM du premier host navigateur direct. Cette tranche valide uniquement la production du module WebAssembly et de ses bindings JavaScript ; le frontend Vite/TypeScript et le shell Bootstrap 5 appartiennent à `0-pre.4`. + +## Prérequis + +À installer une fois si nécessaire : + +```bash +rustup target add wasm32-unknown-unknown +cargo install wasm-bindgen-cli --locked +``` + +Le CLI `wasm-bindgen` utilisé doit rester compatible avec la version de la crate `wasm-bindgen` résolue par Cargo. + +## Build Rust + +Depuis la racine du dépôt : + +```bash +cargo build -p game-snake-poc-wasm --target wasm32-unknown-unknown +``` + +La configuration Cargo racine place l'artefact sous : + +```text +../builds/sasedev-games/target/wasm32-unknown-unknown/debug/game_snake_poc_wasm.wasm +``` + +## Génération des bindings navigateur + +Créer les bindings avec l'outil natif, sans orchestrateur Python : + +```bash +wasm-bindgen \ + ../builds/sasedev-games/target/wasm32-unknown-unknown/debug/game_snake_poc_wasm.wasm \ + --target web \ + --out-dir ../builds/sasedev-games/game-snake-poc-web/wasm \ + --out-name game_snake_poc_wasm +``` + +Les fichiers générés restent intégralement hors du dépôt sous `../builds/sasedev-games/`. Ils ne sont ni commités ni copiés dans `crates/` par cette tranche. + +## Frontière avec `0-pre.4` + +`0-pre.3` s'arrête au module WASM et à ses bindings générés. `0-pre.4` ajoutera le frontend Vite/TypeScript qui les consomme, le document HTML Bootstrap 5, le Canvas, le clavier et les contrôles tactiles. + +Le frontend reste responsable de la cadence d'appel à `tick()` et de la traduction des événements navigateur vers `left()`, `right()`, `up()` et `down()`. diff --git a/docs/plans/001-V0_3_0_WEB_SNAKE_POC_PLAN.md b/docs/plans/001-V0_3_0_WEB_SNAKE_POC_PLAN.md index e1462a1..365f05f 100644 --- a/docs/plans/001-V0_3_0_WEB_SNAKE_POC_PLAN.md +++ b/docs/plans/001-V0_3_0_WEB_SNAKE_POC_PLAN.md @@ -1,5 +1,5 @@ - + # Plan v0.3.0 — baseline Snake et premier POC Web direct @@ -59,9 +59,11 @@ Gate : audits statiques, `cargo check --workspace`, tests ciblés Snake et véri ### `0-pre.3` — adaptation Snake WASM -Introduire la crate/adaptation WASM minimale de Snake et le chemin de génération `wasm-bindgen`, sans frontend riche ni généralisation prématurée. +Introduire `game-snake-poc-wasm`, adapter WASM minimal dédié à Snake. La crate possède `SnakeState`, le `FixedStepRunner` et une direction logique en attente consommée au prochain `tick()`. Elle expose à `wasm-bindgen` les quatre directions, le score, la longueur et la scène normalisée nécessaire au futur Canvas, sans SDL3, Tauri, DOM, frontend ni `engine-v1-platform-api`. -Gate : compilation ciblée WASM et tests Rust applicables. Si cette tranche dépasse le budget normal, séparer bridge et build en deux prereleases. +Livraison candidate : la crate est membre du workspace et la distribution statique vérifie sa présence. Le bridge reste volontairement spécifique à Snake ; aucune abstraction commune avec `game-reflex-poc-wasm` n'est extraite avant observation d'une duplication réellement problématique. + +Gate : audits statiques, `cargo check --workspace`, Clippy ciblé, tests de l'adapter, build `wasm32-unknown-unknown`, génération `wasm-bindgen --target web` dans `../builds/sasedev-games/` et graphe de dépendances. Aucun build Vite/Bootstrap ni smoke navigateur n'est requis avant `0-pre.4`. ### `0-pre.4` — frontend Web/Vite, shell Bootstrap 5 et contrôles diff --git a/history/0.3.0/0-pre.2.md b/history/0.3.0/0-pre.2.md new file mode 100644 index 0000000..ef67dd6 --- /dev/null +++ b/history/0.3.0/0-pre.2.md @@ -0,0 +1,38 @@ + + + +# Historique 0.3.0-0-pre.2 + +## Statut + +Baseline Snake portable validée par l'utilisateur le 2026-09-20 avant ouverture de `0-pre.3`. + +## Validation utilisateur + +La validation a été exécutée après `cargo clean` et reformatage canonique. Les gates suivantes ont passé : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +games.sasedev workspace audit: clean +Markdown table audit: clean (5 table(s), 175 file(s)) +Distribution layout audit: clean (19 required path(s), 1 forbidden path(s) absent) +cargo fmt --all -- --check: clean +cargo check --workspace: clean +cargo clippy --workspace --all-targets --all-features -- -D warnings: clean +cargo test -p game-snake-poc: 5 passed, 0 failed +cargo test -p game-reflex-poc: 4 passed, 0 failed +``` + +Les graphes de dépendances validés sont : + +```text +game-snake-poc -> engine-v1-common +game-reflex-poc -> engine-v1-common +``` + +Aucune des deux crates de gameplay ne tire donc `engine-v1-platform-api` après le nettoyage `0-pre.2`. + +## Suite + +`0-pre.3` introduit l'adapter WASM Snake minimal sans frontend Vite/Bootstrap, conformément au plan actif. diff --git a/scripts/audit_distribution_layout.py b/scripts/audit_distribution_layout.py index b43fc01..28a37eb 100644 --- a/scripts/audit_distribution_layout.py +++ b/scripts/audit_distribution_layout.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # file: scripts/audit_distribution_layout.py -# version: 1 +# version: 2 """Audit the static distribution layout expected by the 0.1.0 beta.""" @@ -29,6 +29,7 @@ REQUIRED_PATHS = ( "crates/apps/game-reflex-poc-tauri/frontend/index.html", "crates/apps/game-reflex-poc-tauri/frontend/ts/main.ts", "crates/apps/game-reflex-poc-wasm/Cargo.toml", + "crates/apps/game-snake-poc-wasm/Cargo.toml", "Android/game-reflex-poc/build.gradle", "Android/game-snake-poc/build.gradle", "scripts/build_android_rust.py",