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..e94d672 --- /dev/null +++ b/map-generation/src/lib.rs @@ -0,0 +1,15 @@ +#![no_std] + +multiversx_sc::imports!(); + +pub mod map; +pub mod random; + +#[multiversx_sc::contract] +pub trait MapGeneration: map::MapModule { + #[init] + fn init(&self) {} + + #[upgrade] + fn upgrade(&self) {} +} diff --git a/map-generation/src/map.rs b/map-generation/src/map.rs new file mode 100644 index 0000000..2f430b0 --- /dev/null +++ b/map-generation/src/map.rs @@ -0,0 +1,159 @@ +use crate::random::{Random, Seed, U64_BYTES}; + +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 { + #[inline] + pub fn new(seed: Seed) -> Self { + Self { + rng: Random::new(seed), + terrain: [0; SQUARED_SIZE], + } + } + + pub fn new_from_seed(seed: Seed) -> Self { + Self { + rng: Random::new(seed), + 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 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) { + 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); + } + + 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 { + 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) { + 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); + } + } + } + + 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) -> [u32; SQUARED_SIZE] { + 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; + map.init::(); + + 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); + + map.terrain + } + + #[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..06f1f35 --- /dev/null +++ b/map-generation/src/random.rs @@ -0,0 +1,98 @@ +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_DECODE_ERR_MSG: &[u8] = b"Failed decoding u64"; + +pub type Seed = u64; + +#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode)] +pub struct Random { + pub seed: Seed, + pub index: usize, +} + +impl Random { + #[inline] + pub fn new(rand_seed: Seed) -> Self { + Self { + seed: rand_seed, + index: 0, + } + } + + #[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 + } + + 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 > U64_BYTES { + self.xor_shift_seed(); + } + + 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 + } + + // 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); + } +} 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..c1c114c --- /dev/null +++ b/map-generation/wasm/src/lib.rs @@ -0,0 +1,28 @@ +// Code generated by the multiversx-sc build system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Upgrade: 1 +// Endpoints: 2 +// Async Callback (empty): 1 +// Total number of exported functions: 5 + +#![no_std] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + map_generation + ( + init => init + upgrade => upgrade + generateNewMap => generate_new_map + getCurrentMapSeed => current_map_seed + ) +} + +multiversx_sc_wasm_adapter::async_callback_empty! {}