From cfb944151610aacb248190ab85dcf5c2836fe1e8 Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Mon, 26 Aug 2024 09:18:18 +0300 Subject: [PATCH 01/10] contract from template --- Cargo.lock | 17 +++ Cargo.toml | 4 +- map-generation/Cargo.toml | 18 +++ map-generation/meta/Cargo.toml | 12 ++ map-generation/meta/src/main.rs | 3 + map-generation/multiversx.json | 3 + map-generation/src/lib.rs | 12 ++ map-generation/wasm/Cargo.lock | 188 ++++++++++++++++++++++++++++++++ map-generation/wasm/Cargo.toml | 34 ++++++ map-generation/wasm/src/lib.rs | 26 +++++ 10 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 map-generation/Cargo.toml create mode 100644 map-generation/meta/Cargo.toml create mode 100644 map-generation/meta/src/main.rs create mode 100644 map-generation/multiversx.json create mode 100644 map-generation/src/lib.rs create mode 100644 map-generation/wasm/Cargo.lock create mode 100644 map-generation/wasm/Cargo.toml create mode 100644 map-generation/wasm/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 284e62d..0173d18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -424,6 +424,23 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "map-generation" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-scenario", + "num-bigint", +] + +[[package]] +name = "map-generation-meta" +version = "0.0.0" +dependencies = [ + "map-generation", + "multiversx-sc-meta-lib", +] + [[package]] name = "memchr" version = "2.7.4" diff --git a/Cargo.toml b/Cargo.toml index 43fa56e..6f4736d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,7 @@ members = [ "gas-burner-contracts/gas-burner", "gas-burner-contracts/gas-burner/meta", "gas-burner-contracts/owner-sc", - "gas-burner-contracts/owner-sc/meta" + "gas-burner-contracts/owner-sc/meta", + "map-generation", + "map-generation/meta", ] diff --git a/map-generation/Cargo.toml b/map-generation/Cargo.toml new file mode 100644 index 0000000..cb70c17 --- /dev/null +++ b/map-generation/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "map-generation" +version = "0.0.0" +authors = ["you"] +edition = "2021" +publish = false + +[lib] +path = "src/lib.rs" + +[dependencies.multiversx-sc] +version = "=0.52.3" + +[dev-dependencies] +num-bigint = "0.4" + +[dev-dependencies.multiversx-sc-scenario] +version = "=0.52.3" diff --git a/map-generation/meta/Cargo.toml b/map-generation/meta/Cargo.toml new file mode 100644 index 0000000..030d6d1 --- /dev/null +++ b/map-generation/meta/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "map-generation-meta" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies.map-generation] +path = ".." + +[dependencies.multiversx-sc-meta-lib] +version = "=0.52.3" +default-features = false diff --git a/map-generation/meta/src/main.rs b/map-generation/meta/src/main.rs new file mode 100644 index 0000000..7b9269c --- /dev/null +++ b/map-generation/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta_lib::cli_main::(); +} diff --git a/map-generation/multiversx.json b/map-generation/multiversx.json new file mode 100644 index 0000000..7365539 --- /dev/null +++ b/map-generation/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} \ No newline at end of file diff --git a/map-generation/src/lib.rs b/map-generation/src/lib.rs new file mode 100644 index 0000000..e368096 --- /dev/null +++ b/map-generation/src/lib.rs @@ -0,0 +1,12 @@ +#![no_std] + +multiversx_sc::imports!(); + +#[multiversx_sc::contract] +pub trait MapGeneration { + #[init] + fn init(&self) {} + + #[upgrade] + fn upgrade(&self) {} +} diff --git a/map-generation/wasm/Cargo.lock b/map-generation/wasm/Cargo.lock new file mode 100644 index 0000000..a0eaf61 --- /dev/null +++ b/map-generation/wasm/Cargo.lock @@ -0,0 +1,188 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "map-generation" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "map-generation-wasm" +version = "0.0.0" +dependencies = [ + "map-generation", + "multiversx-sc-wasm-adapter", +] + +[[package]] +name = "multiversx-sc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "526760b1d6236c011285b264a70a0a9dd3b3dbc53c3b5f76932f4bcfd3a8910c" +dependencies = [ + "bitflags", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", + "unwrap-infallible", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad4f318427761faecf26c1f3115a3beeb5f61858845a60547d9763aa981ddd2d" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", + "unwrap-infallible", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476501462b0c2654b64f9dec6f2c480e24b4e9b7133ec10b7167e64acda35d04" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3557f2f12640a8a07fa6af66cc2a13b188c5b61bed72db22fe631fb3a60c3e96" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed13aaca9cbdbc6911174cd3029e750a7563d85dd3daaa1107b1fd31c7f17245" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unwrap-infallible" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb" diff --git a/map-generation/wasm/Cargo.toml b/map-generation/wasm/Cargo.toml new file mode 100644 index 0000000..c801604 --- /dev/null +++ b/map-generation/wasm/Cargo.toml @@ -0,0 +1,34 @@ +# Code generated by the multiversx-sc build system. DO NOT EDIT. + +# ########################################## +# ############## AUTO-GENERATED ############# +# ########################################## + +[package] +name = "map-generation-wasm" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +overflow-checks = false + +[profile.dev] +panic = "abort" + +[dependencies.map-generation] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "=0.52.3" + +[workspace] +members = ["."] diff --git a/map-generation/wasm/src/lib.rs b/map-generation/wasm/src/lib.rs new file mode 100644 index 0000000..e5e6834 --- /dev/null +++ b/map-generation/wasm/src/lib.rs @@ -0,0 +1,26 @@ +// Code generated by the multiversx-sc build system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Upgrade: 1 +// Endpoints: 0 +// Async Callback (empty): 1 +// Total number of exported functions: 3 + +#![no_std] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + map_generation + ( + init => init + upgrade => upgrade + ) +} + +multiversx_sc_wasm_adapter::async_callback_empty! {} From 682867c693706daf11c12b018b65a595058d529a Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Mon, 26 Aug 2024 10:47:57 +0300 Subject: [PATCH 02/10] base generation --- map-generation/src/lib.rs | 5 +- map-generation/src/map.rs | 155 +++++++++++++++++++++++++++++++++++ map-generation/src/random.rs | 90 ++++++++++++++++++++ 3 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 map-generation/src/map.rs create mode 100644 map-generation/src/random.rs diff --git a/map-generation/src/lib.rs b/map-generation/src/lib.rs index e368096..e94d672 100644 --- a/map-generation/src/lib.rs +++ b/map-generation/src/lib.rs @@ -2,8 +2,11 @@ multiversx_sc::imports!(); +pub mod map; +pub mod random; + #[multiversx_sc::contract] -pub trait MapGeneration { +pub trait MapGeneration: map::MapModule { #[init] fn init(&self) {} diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs new file mode 100644 index 0000000..af88699 --- /dev/null +++ b/map-generation/src/map.rs @@ -0,0 +1,155 @@ +use multiversx_sc::api::CryptoApi; + +use crate::random::{Hash, Random}; + +multiversx_sc::imports!(); + +const WIGGLE: i16 = 32; +const TILES: u32 = 512; +const SIZE: u32 = TILES * 2 + 1; +const SQUARED_SIZE: usize = (SIZE * SIZE) as usize; + +pub struct Map { + rng: Random, + terrain: [u32; SQUARED_SIZE], +} + +impl Map { + pub fn new() -> Self { + Self { + rng: Random::new(), + terrain: [0; SQUARED_SIZE], + } + } + + pub fn new_from_seed(seed: Hash) -> Self { + Self { + rng: Random::from_hash(seed, 0), + terrain: [0; SQUARED_SIZE], // TODO: Generate the terrain + } + } + + pub fn get(&self, x: u32, y: u32) -> u32 { + self.terrain[(x * SIZE + y) as usize] + } + + pub fn set(&mut self, x: u32, y: u32, value: u32) { + self.terrain[(x * SIZE + y) as usize] = value; + } + + pub fn init(&mut self) { + let a = self.rng.next_u8() as u32; + let b = self.rng.next_u8() as u32; + let c = self.rng.next_u8() as u32; + let d = self.rng.next_u8() as u32; + + self.set(0, 0, a); + self.set(SIZE - 1, 0, b); + self.set(0, SIZE - 1, c); + self.set(SIZE - 1, SIZE - 1, d); + } + + pub fn square(&mut self, x: u32, y: u32, radius: u32) { + let height = self.wiggle( + (self.get(x - radius, y - radius) + + self.get(x - radius, y + radius) + + self.get(x + radius, y - radius) + + self.get(x + radius, y + radius)) + / 4, + WIGGLE, + ); + + self.set(x, y, height); + } + + pub fn diamond(&mut self, x: u32, y: u32, radius: u32) { + let mut spread = 0; + let mut t = 0; + + if radius <= x { + spread += 1; + t += self.get(x - radius, y); + } + + if x + radius < SIZE { + spread += 1; + t += self.get(x + radius, y); + } + + if radius <= y { + spread += 1; + t += self.get(x, y - radius); + } + + if y + radius < SIZE { + spread += 1; + t += self.get(x, y + radius); + } + + let height = self.wiggle(t / spread, WIGGLE); + self.set(x, y, height); + } + + pub fn squares(&mut self, step: u32) { + let step2 = step * 2; + for x in 0..step { + for y in 0..step { + self.square( + SIZE / step2 + (x * SIZE / step), + SIZE / step2 + (y * SIZE / step), + SIZE / step2, + ); + } + } + } + + pub fn diamonds(&mut self, radius: u32) { + for x in (0..SIZE).step_by(radius as usize) { + let y_start: u32 = if (x / (radius)) % 2 == 0 { radius } else { 0 }; + + for y in (y_start..SIZE).step_by(radius as usize * 2) { + self.diamond(x, y, radius); + } + } + } + + pub fn wiggle(&mut self, value: u32, range: i16) -> u32 { + let min = if value < range as u32 { + value as i16 + } else { + range + }; + + (value as i16 + self.rng.gen_range(-min, range)) as u32 + } +} + +#[multiversx_sc::module] +pub trait MapModule { + #[only_owner] + #[endpoint(generateNewMap)] + fn generate_new_map(&self) -> Hash { + let mut map = Map::::new(); + let initial_seed = Hash::from_raw_handle(map.rng.seed.get_raw_handle()).clone(); + + let steps = TILES.trailing_zeros() + 1; + for s in 0..steps { + map.squares(1 << s); + map.diamonds(1 << (steps - s - 1)); + } + + for _x in 0..SIZE { + for _y in 0..SIZE { + // TODO: Do... something... ? + } + } + + self.current_map_seed().set(&initial_seed); + + initial_seed + } + + #[view(getCurrentMapSeed)] + #[storage_mapper("currentMapSeed")] + fn current_map_seed(&self) -> SingleValueMapper>; +} diff --git a/map-generation/src/random.rs b/map-generation/src/random.rs new file mode 100644 index 0000000..3b24b9a --- /dev/null +++ b/map-generation/src/random.rs @@ -0,0 +1,90 @@ +// Stolen (and adapted) from launchpad: https://github.com/multiversx/mx-launchpad-sc/blob/main/launchpad-common/src/random.rs + +use multiversx_sc::api::{CryptoApi, CryptoApiImpl}; + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +const U8_BYTES: usize = 1; +const I16_BYTES: usize = 2; +pub const HASH_LEN: usize = 32; +static FAILED_COPY_ERR_MSG: &[u8] = b"Failed copy to/from managed buffer"; + +pub type Hash = ManagedByteArray; + +#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode)] +pub struct Random { + pub seed: ManagedBuffer, + pub index: usize, +} + +impl Default for Random { + fn default() -> Self { + Self { + seed: ManagedBuffer::new_random(HASH_LEN), + index: 0, + } + } +} + +impl Random { + #[inline] + pub fn new() -> Self { + Self::default() + } + + pub fn from_hash(hash: Hash, index: usize) -> Self { + Self { + seed: ManagedBuffer::from_raw_handle(hash.get_raw_handle()), + index, + } + } + + #[inline] + pub fn next_u8(&mut self) -> u8 { + let value = self.next_value(U8_BYTES); + + value as u8 + } + + #[inline] + pub fn next_i16(&mut self) -> i16 { + let value = self.next_value(I16_BYTES); + + value as i16 + } + + // TODO: Test if this actually works properly with negative numbers + pub fn gen_range(&mut self, min: i16, max: i16) -> i16 { + let rand = self.next_i16(); + + if min >= max { + min + } else { + min + rand % (max - min) + } + } + + fn next_value(&mut self, size: usize) -> u64 { + if self.index + size > HASH_LEN { + self.hash_seed(); + } + + let raw_buffer = match self.seed.copy_slice(self.index, size) { + Some(buffer) => buffer, + None => M::error_api_impl().signal_error(FAILED_COPY_ERR_MSG), + }; + let rand = u64::top_decode(raw_buffer).unwrap_or_default(); + + self.index += size; + + rand + } + + fn hash_seed(&mut self) { + let handle = self.seed.get_raw_handle(); + M::crypto_api_impl().sha256_managed(handle.into(), handle.into()); + + self.index = 0; + } +} From 0949f846beca0a75d9a23d09f2a32bceec3ad4ea Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Mon, 26 Aug 2024 10:50:03 +0300 Subject: [PATCH 03/10] evil clippy --- map-generation/src/map.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs index af88699..fe306a0 100644 --- a/map-generation/src/map.rs +++ b/map-generation/src/map.rs @@ -14,13 +14,21 @@ pub struct Map { terrain: [u32; SQUARED_SIZE], } -impl Map { - pub fn new() -> Self { +impl Default for Map { + #[inline] + fn default() -> Self { Self { rng: Random::new(), terrain: [0; SQUARED_SIZE], } } +} + +impl Map { + #[inline] + pub fn new() -> Self { + Self::default() + } pub fn new_from_seed(seed: Hash) -> Self { Self { From 897ef4e34e566ef50a2103ec585a1211eebd0241 Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Mon, 26 Aug 2024 10:59:21 +0300 Subject: [PATCH 04/10] small refactor --- map-generation/src/map.rs | 29 ++++++++++++++--------------- map-generation/wasm/src/lib.rs | 6 ++++-- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs index fe306a0..4f6a6d0 100644 --- a/map-generation/src/map.rs +++ b/map-generation/src/map.rs @@ -58,14 +58,12 @@ impl Map { } pub fn square(&mut self, x: u32, y: u32, radius: u32) { - let height = self.wiggle( - (self.get(x - radius, y - radius) - + self.get(x - radius, y + radius) - + self.get(x + radius, y - radius) - + self.get(x + radius, y + radius)) - / 4, - WIGGLE, - ); + let top_left = self.get(x - radius, y - radius); + let bottom_left = self.get(x - radius, y + radius); + let top_right = self.get(x + radius, y - radius); + let bottom_right = self.get(x + radius, y + radius); + let average = (top_left + bottom_left + top_right + bottom_right) / 4; + let height = self.wiggle(average, WIGGLE); self.set(x, y, height); } @@ -102,20 +100,21 @@ impl Map { let step2 = step * 2; for x in 0..step { for y in 0..step { - self.square( - SIZE / step2 + (x * SIZE / step), - SIZE / step2 + (y * SIZE / step), - SIZE / step2, - ); + let square_x = SIZE / step2 + (x * SIZE / step); + let square_y = SIZE / step2 + (y * SIZE / step); + let radius = SIZE / step2; + + self.square(square_x, square_y, radius); } } } pub fn diamonds(&mut self, radius: u32) { - for x in (0..SIZE).step_by(radius as usize) { + let radius_usize = radius as usize; + for x in (0..SIZE).step_by(radius_usize) { let y_start: u32 = if (x / (radius)) % 2 == 0 { radius } else { 0 }; - for y in (y_start..SIZE).step_by(radius as usize * 2) { + for y in (y_start..SIZE).step_by(radius_usize * 2) { self.diamond(x, y, radius); } } diff --git a/map-generation/wasm/src/lib.rs b/map-generation/wasm/src/lib.rs index e5e6834..c1c114c 100644 --- a/map-generation/wasm/src/lib.rs +++ b/map-generation/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 0 +// Endpoints: 2 // Async Callback (empty): 1 -// Total number of exported functions: 3 +// Total number of exported functions: 5 #![no_std] @@ -20,6 +20,8 @@ multiversx_sc_wasm_adapter::endpoints! { ( init => init upgrade => upgrade + generateNewMap => generate_new_map + getCurrentMapSeed => current_map_seed ) } From e16ab8c6964e869592dc20acc95569c79680f1db Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Mon, 26 Aug 2024 11:00:22 +0300 Subject: [PATCH 05/10] rename --- map-generation/src/map.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs index 4f6a6d0..86942b3 100644 --- a/map-generation/src/map.rs +++ b/map-generation/src/map.rs @@ -46,15 +46,15 @@ impl Map { } pub fn init(&mut self) { - let a = self.rng.next_u8() as u32; - let b = self.rng.next_u8() as u32; - let c = self.rng.next_u8() as u32; - let d = self.rng.next_u8() as u32; - - self.set(0, 0, a); - self.set(SIZE - 1, 0, b); - self.set(0, SIZE - 1, c); - self.set(SIZE - 1, SIZE - 1, d); + let top_left = self.rng.next_u8() as u32; + let bottom_left = self.rng.next_u8() as u32; + let top_right = self.rng.next_u8() as u32; + let bottom_right = self.rng.next_u8() as u32; + + self.set(0, 0, top_left); + self.set(SIZE - 1, 0, bottom_left); + self.set(0, SIZE - 1, top_right); + self.set(SIZE - 1, SIZE - 1, bottom_right); } pub fn square(&mut self, x: u32, y: u32, radius: u32) { From 17dea06cab749caa415be29e5761fa5a768421a5 Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Mon, 26 Aug 2024 11:03:24 +0300 Subject: [PATCH 06/10] return whole map --- map-generation/src/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs index 86942b3..52d026d 100644 --- a/map-generation/src/map.rs +++ b/map-generation/src/map.rs @@ -135,7 +135,7 @@ impl Map { pub trait MapModule { #[only_owner] #[endpoint(generateNewMap)] - fn generate_new_map(&self) -> Hash { + fn generate_new_map(&self) -> [u32; SQUARED_SIZE] { let mut map = Map::::new(); let initial_seed = Hash::from_raw_handle(map.rng.seed.get_raw_handle()).clone(); @@ -153,7 +153,7 @@ pub trait MapModule { self.current_map_seed().set(&initial_seed); - initial_seed + map.terrain } #[view(getCurrentMapSeed)] From 9a272ae7cd522280ae1fbce8b4650bcb0bd42802 Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Mon, 26 Aug 2024 11:04:33 +0300 Subject: [PATCH 07/10] comment code --- map-generation/src/map.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs index 52d026d..c82d98c 100644 --- a/map-generation/src/map.rs +++ b/map-generation/src/map.rs @@ -145,11 +145,11 @@ pub trait MapModule { map.diamonds(1 << (steps - s - 1)); } - for _x in 0..SIZE { - for _y in 0..SIZE { - // TODO: Do... something... ? - } - } + // for _x in 0..SIZE { + // for _y in 0..SIZE { + // // TODO: Do... something... ? + // } + // } self.current_map_seed().set(&initial_seed); From 7dee244dc41a58939f5e0f44473ee90aa08c17b7 Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Tue, 27 Aug 2024 10:31:23 +0300 Subject: [PATCH 08/10] optimize rng --- map-generation/src/map.rs | 70 +++++++++++++------------- map-generation/src/random.rs | 96 +++++++++++++++++++----------------- 2 files changed, 85 insertions(+), 81 deletions(-) diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs index c82d98c..8575fb1 100644 --- a/map-generation/src/map.rs +++ b/map-generation/src/map.rs @@ -1,6 +1,4 @@ -use multiversx_sc::api::CryptoApi; - -use crate::random::{Hash, Random}; +use crate::random::{Random, Seed, U64_BYTES}; multiversx_sc::imports!(); @@ -9,30 +7,23 @@ const TILES: u32 = 512; const SIZE: u32 = TILES * 2 + 1; const SQUARED_SIZE: usize = (SIZE * SIZE) as usize; -pub struct Map { - rng: Random, +pub struct Map { + rng: Random, terrain: [u32; SQUARED_SIZE], } -impl Default for Map { +impl Map { #[inline] - fn default() -> Self { + pub fn new(seed: Seed) -> Self { Self { - rng: Random::new(), + rng: Random::new(seed), terrain: [0; SQUARED_SIZE], } } -} - -impl Map { - #[inline] - pub fn new() -> Self { - Self::default() - } - pub fn new_from_seed(seed: Hash) -> Self { + pub fn new_from_seed(seed: Seed) -> Self { Self { - rng: Random::from_hash(seed, 0), + rng: Random::new(seed), terrain: [0; SQUARED_SIZE], // TODO: Generate the terrain } } @@ -45,11 +36,11 @@ impl Map { self.terrain[(x * SIZE + y) as usize] = value; } - pub fn init(&mut self) { - let top_left = self.rng.next_u8() as u32; - let bottom_left = self.rng.next_u8() as u32; - let top_right = self.rng.next_u8() as u32; - let bottom_right = self.rng.next_u8() as u32; + pub fn init(&mut self) { + let top_left = self.rng.next_u8::() as u32; + let bottom_left = self.rng.next_u8::() as u32; + let top_right = self.rng.next_u8::() as u32; + let bottom_right = self.rng.next_u8::() as u32; self.set(0, 0, top_left); self.set(SIZE - 1, 0, bottom_left); @@ -57,18 +48,18 @@ impl Map { self.set(SIZE - 1, SIZE - 1, bottom_right); } - pub fn square(&mut self, x: u32, y: u32, radius: u32) { + pub fn square(&mut self, x: u32, y: u32, radius: u32) { let top_left = self.get(x - radius, y - radius); let bottom_left = self.get(x - radius, y + radius); let top_right = self.get(x + radius, y - radius); let bottom_right = self.get(x + radius, y + radius); let average = (top_left + bottom_left + top_right + bottom_right) / 4; - let height = self.wiggle(average, WIGGLE); + let height = self.wiggle::(average, WIGGLE); self.set(x, y, height); } - pub fn diamond(&mut self, x: u32, y: u32, radius: u32) { + pub fn diamond(&mut self, x: u32, y: u32, radius: u32) { let mut spread = 0; let mut t = 0; @@ -92,11 +83,11 @@ impl Map { t += self.get(x, y + radius); } - let height = self.wiggle(t / spread, WIGGLE); + let height = self.wiggle::(t / spread, WIGGLE); self.set(x, y, height); } - pub fn squares(&mut self, step: u32) { + pub fn squares(&mut self, step: u32) { let step2 = step * 2; for x in 0..step { for y in 0..step { @@ -104,30 +95,30 @@ impl Map { let square_y = SIZE / step2 + (y * SIZE / step); let radius = SIZE / step2; - self.square(square_x, square_y, radius); + self.square::(square_x, square_y, radius); } } } - pub fn diamonds(&mut self, radius: u32) { + pub fn diamonds(&mut self, radius: u32) { let radius_usize = radius as usize; for x in (0..SIZE).step_by(radius_usize) { let y_start: u32 = if (x / (radius)) % 2 == 0 { radius } else { 0 }; for y in (y_start..SIZE).step_by(radius_usize * 2) { - self.diamond(x, y, radius); + self.diamond::(x, y, radius); } } } - pub fn wiggle(&mut self, value: u32, range: i16) -> u32 { + pub fn wiggle(&mut self, value: u32, range: i16) -> u32 { let min = if value < range as u32 { value as i16 } else { range }; - (value as i16 + self.rng.gen_range(-min, range)) as u32 + (value as i16 + self.rng.gen_range::(-min, range)) as u32 } } @@ -136,13 +127,18 @@ pub trait MapModule { #[only_owner] #[endpoint(generateNewMap)] fn generate_new_map(&self) -> [u32; SQUARED_SIZE] { - let mut map = Map::::new(); - let initial_seed = Hash::from_raw_handle(map.rng.seed.get_raw_handle()).clone(); + let buffer = ManagedBuffer::new_random(U64_BYTES); + let seed_result = u64::top_decode(buffer); + require!(seed_result.is_ok(), "Failed decoding random seed"); + + let seed = unsafe { seed_result.unwrap_unchecked() }; + let mut map = Map::new(seed); + let initial_seed = map.rng.seed; let steps = TILES.trailing_zeros() + 1; for s in 0..steps { - map.squares(1 << s); - map.diamonds(1 << (steps - s - 1)); + map.squares::(1 << s); + map.diamonds::(1 << (steps - s - 1)); } // for _x in 0..SIZE { @@ -158,5 +154,5 @@ pub trait MapModule { #[view(getCurrentMapSeed)] #[storage_mapper("currentMapSeed")] - fn current_map_seed(&self) -> SingleValueMapper>; + fn current_map_seed(&self) -> SingleValueMapper; } diff --git a/map-generation/src/random.rs b/map-generation/src/random.rs index 3b24b9a..06f1f35 100644 --- a/map-generation/src/random.rs +++ b/map-generation/src/random.rs @@ -1,62 +1,45 @@ -// Stolen (and adapted) from launchpad: https://github.com/multiversx/mx-launchpad-sc/blob/main/launchpad-common/src/random.rs - -use multiversx_sc::api::{CryptoApi, CryptoApiImpl}; - multiversx_sc::imports!(); multiversx_sc::derive_imports!(); const U8_BYTES: usize = 1; const I16_BYTES: usize = 2; +pub const U64_BYTES: usize = 8; pub const HASH_LEN: usize = 32; -static FAILED_COPY_ERR_MSG: &[u8] = b"Failed copy to/from managed buffer"; +static FAILED_DECODE_ERR_MSG: &[u8] = b"Failed decoding u64"; -pub type Hash = ManagedByteArray; +pub type Seed = u64; #[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode)] -pub struct Random { - pub seed: ManagedBuffer, +pub struct Random { + pub seed: Seed, pub index: usize, } -impl Default for Random { - fn default() -> Self { - Self { - seed: ManagedBuffer::new_random(HASH_LEN), - index: 0, - } - } -} - -impl Random { +impl Random { #[inline] - pub fn new() -> Self { - Self::default() - } - - pub fn from_hash(hash: Hash, index: usize) -> Self { + pub fn new(rand_seed: Seed) -> Self { Self { - seed: ManagedBuffer::from_raw_handle(hash.get_raw_handle()), - index, + seed: rand_seed, + index: 0, } } #[inline] - pub fn next_u8(&mut self) -> u8 { - let value = self.next_value(U8_BYTES); + pub fn next_u8(&mut self) -> u8 { + let value = self.next_value::(U8_BYTES); value as u8 } #[inline] - pub fn next_i16(&mut self) -> i16 { - let value = self.next_value(I16_BYTES); + pub fn next_i16(&mut self) -> i16 { + let value = self.next_value::(I16_BYTES); value as i16 } - // TODO: Test if this actually works properly with negative numbers - pub fn gen_range(&mut self, min: i16, max: i16) -> i16 { - let rand = self.next_i16(); + pub fn gen_range(&mut self, min: i16, max: i16) -> i16 { + let rand = self.next_i16::(); if min >= max { min @@ -65,26 +48,51 @@ impl Random { } } - fn next_value(&mut self, size: usize) -> u64 { - if self.index + size > HASH_LEN { - self.hash_seed(); + fn next_value(&mut self, size: usize) -> u64 { + if self.index + size > U64_BYTES { + self.xor_shift_seed(); } - let raw_buffer = match self.seed.copy_slice(self.index, size) { - Some(buffer) => buffer, - None => M::error_api_impl().signal_error(FAILED_COPY_ERR_MSG), - }; - let rand = u64::top_decode(raw_buffer).unwrap_or_default(); + let seed_bytes = self.seed.to_be_bytes(); + let val_range = &seed_bytes[self.index..self.index + size]; + let decode_result = u64::top_decode(val_range); + if decode_result.is_err() { + Api::error_api_impl().signal_error(FAILED_DECODE_ERR_MSG); + } + let rand = unsafe { decode_result.unwrap_unchecked() }; self.index += size; rand } - fn hash_seed(&mut self) { - let handle = self.seed.get_raw_handle(); - M::crypto_api_impl().sha256_managed(handle.into(), handle.into()); - + // Stolen from: https://en.wikipedia.org/wiki/Xorshift + fn xor_shift_seed(&mut self) { + let mut new_seed = self.seed; + new_seed ^= new_seed << 13; + new_seed ^= new_seed >> 7; + new_seed ^= new_seed << 17; + self.seed = new_seed; self.index = 0; } } + +#[cfg(test)] +mod randomness_tests { + use multiversx_sc_scenario::DebugApi; + + use super::*; + + #[test] + fn gen_range_test() { + let mut random = Random::new(0); + let rand_nr = random.gen_range::(-20, -10); + assert_eq!(rand_nr, -20); + + let rand_nr = random.gen_range::(10, 20); + assert_eq!(rand_nr, 10); + + let rand_nr = random.gen_range::(-20, 20); + assert_eq!(rand_nr, -20); + } +} From 94c475ca6b935882b60902b9d290d26b3423d5b3 Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Tue, 27 Aug 2024 10:32:16 +0300 Subject: [PATCH 09/10] evil clippy --- map-generation/src/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs index 8575fb1..ea62b0d 100644 --- a/map-generation/src/map.rs +++ b/map-generation/src/map.rs @@ -147,7 +147,7 @@ pub trait MapModule { // } // } - self.current_map_seed().set(&initial_seed); + self.current_map_seed().set(initial_seed); map.terrain } From 4a669617390758ebb515b0081e028445b41ec49a Mon Sep 17 00:00:00 2001 From: Dorin Marian Iancu Date: Tue, 27 Aug 2024 10:34:16 +0300 Subject: [PATCH 10/10] call init --- map-generation/src/map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs index ea62b0d..2f430b0 100644 --- a/map-generation/src/map.rs +++ b/map-generation/src/map.rs @@ -134,6 +134,7 @@ pub trait MapModule { let seed = unsafe { seed_result.unwrap_unchecked() }; let mut map = Map::new(seed); let initial_seed = map.rng.seed; + map.init::(); let steps = TILES.trailing_zeros() + 1; for s in 0..steps {