Cellular automata generator
This commit is contained in:
parent
8770d8ab77
commit
3805372cf2
|
@ -11,4 +11,4 @@ documentation = "https://docs.rs/mapgen"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = { version = "0.4.8", features = ["serde"] }
|
||||
rand = "0.7"
|
||||
|
|
|
@ -5,6 +5,7 @@ authors = ["Krzysztof Langner <klangner@gmail.com>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.7"
|
||||
amethyst = {version = "0.15", features = ["tiles", "no-slow-safety-checks"]}
|
||||
log = { version = "0.4.8", features = ["serde"] }
|
||||
mapgen = {path=".."}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use rand::prelude::*;
|
||||
use amethyst::{
|
||||
assets::{AssetStorage, Loader},
|
||||
core::{
|
||||
|
@ -20,7 +21,9 @@ use amethyst::{
|
|||
winit,
|
||||
};
|
||||
use mapgen::dungeon::{
|
||||
MapGenerator,
|
||||
map::{Map, TileType},
|
||||
cellular_automata::CellularAutomataGen,
|
||||
};
|
||||
|
||||
|
||||
|
@ -63,16 +66,22 @@ fn init_camera(world: &mut World, transform: Transform, camera: Camera) -> Entit
|
|||
.build()
|
||||
}
|
||||
|
||||
fn init_map(world: &mut World) {
|
||||
let gen = CellularAutomataGen::new(80, 50);
|
||||
let mut rng = StdRng::seed_from_u64(0);
|
||||
let map = gen.generate_map(&mut rng);
|
||||
world.insert(map);
|
||||
}
|
||||
|
||||
|
||||
struct PlayState;
|
||||
impl SimpleState for PlayState {
|
||||
fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) {
|
||||
|
||||
let world = data.world;
|
||||
let mut world = data.world;
|
||||
|
||||
// Create map
|
||||
let map = Map::new(80, 50);
|
||||
world.insert(map);
|
||||
init_map(&mut world);
|
||||
|
||||
let map_sprite_sheet_handle =
|
||||
load_tiles_sprite_sheet(world, "texture/cp437_20x20.png", "texture/cp437_20x20.ron");
|
||||
|
|
103
src/dungeon/cellular_automata.rs
Normal file
103
src/dungeon/cellular_automata.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
use rand::prelude::*;
|
||||
use super::{MapGenerator, MapModifier};
|
||||
use super::map::{Map, TileType};
|
||||
|
||||
|
||||
pub struct CellularAutomataGen {
|
||||
width: usize,
|
||||
height: usize
|
||||
}
|
||||
|
||||
impl MapGenerator for CellularAutomataGen {
|
||||
fn generate_map(&self, rng : &mut StdRng) -> Map {
|
||||
self.build(rng)
|
||||
}
|
||||
}
|
||||
|
||||
impl CellularAutomataGen {
|
||||
pub fn new(width: usize, height: usize) -> CellularAutomataGen {
|
||||
CellularAutomataGen {width, height}
|
||||
}
|
||||
|
||||
/// Generate map
|
||||
fn build(&self, rng : &mut StdRng) -> Map {
|
||||
let mut map = Map::new(self.width, self.height);
|
||||
// First we completely randomize the map, setting 55% of it to be floor.
|
||||
for y in 1..self.height-1 {
|
||||
for x in 1..self.width-1 {
|
||||
let roll = rng.next_u32() % 100;
|
||||
if roll > 55 { map.set_tile(x, y, TileType::Floor) }
|
||||
else { map.set_tile(x, y, TileType::Wall) }
|
||||
}
|
||||
}
|
||||
|
||||
// Now we iteratively apply cellular automata rules
|
||||
for _ in 0..15 {
|
||||
map = apply_iteration(&map);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl MapModifier for CellularAutomataGen {
|
||||
fn modify_map(&self, _rng: &mut StdRng, map : &Map) -> Map {
|
||||
apply_iteration(map)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn apply_iteration(map: &Map) -> Map {
|
||||
let mut new_map = map.clone();
|
||||
|
||||
for y in 1..map.height-1 {
|
||||
for x in 1..map.width-1 {
|
||||
let idxs = [
|
||||
(x-1, y-1), (x, y-1), (x+1, y-1),
|
||||
(x-1, y), (x+1, y),
|
||||
(x-1, y+1), (x, y+1), (x+1, y+1)];
|
||||
let neighbors = idxs.iter()
|
||||
.filter(|(x, y)| map.at(*x, *y) == TileType::Wall)
|
||||
.count();
|
||||
|
||||
if neighbors > 4 || neighbors == 0 {
|
||||
new_map.set_tile(x, y, TileType::Wall)
|
||||
}
|
||||
else {
|
||||
new_map.set_tile(x, y, TileType::Floor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_map
|
||||
}
|
||||
|
||||
/// ------------------------------------------------------------------------------------------------
|
||||
/// Module unit tests
|
||||
/// ------------------------------------------------------------------------------------------------
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_iteration_wal() {
|
||||
let map = Map::new(3, 3);
|
||||
let new_map = apply_iteration(&map);
|
||||
assert_eq!(new_map.at(1, 1), TileType::Wall);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_iteration_floor() {
|
||||
let mut map = Map::new(3, 3);
|
||||
for i in 0..3 {
|
||||
for j in 0..2 {
|
||||
map.set_tile(i, j, TileType::Floor);
|
||||
}
|
||||
}
|
||||
let new_map = apply_iteration(&map);
|
||||
assert_eq!(new_map.at(1, 1), TileType::Floor);
|
||||
}
|
||||
|
||||
}
|
|
@ -22,11 +22,17 @@ impl Map {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get TileType at the given location
|
||||
pub fn at(&self, x: usize, y: usize) -> TileType {
|
||||
let idx = y * self.width + x;
|
||||
self.tiles[idx]
|
||||
}
|
||||
|
||||
/// Modify tile at the given location
|
||||
pub fn set_tile(&mut self, x: usize, y: usize, tile: TileType) {
|
||||
let idx = y * self.width + x;
|
||||
self.tiles[idx] = tile;
|
||||
}
|
||||
}
|
||||
|
||||
/// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -1 +1,46 @@
|
|||
pub mod map;
|
||||
pub mod cellular_automata;
|
||||
|
||||
use rand::prelude::*;
|
||||
use map::Map;
|
||||
|
||||
|
||||
pub trait MapGenerator {
|
||||
fn generate_map(&self, rng: &mut StdRng) -> Map;
|
||||
}
|
||||
|
||||
pub trait MapModifier {
|
||||
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map;
|
||||
}
|
||||
|
||||
pub struct MapBuilder {
|
||||
generator: Box<dyn MapGenerator>,
|
||||
modifiers: Vec<Box<dyn MapModifier>>,
|
||||
rng: StdRng,
|
||||
}
|
||||
|
||||
impl MapBuilder {
|
||||
pub fn new(generator : Box<dyn MapGenerator>) -> MapBuilder {
|
||||
MapBuilder {
|
||||
generator,
|
||||
modifiers: Vec::new(),
|
||||
rng: StdRng::seed_from_u64(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with(&mut self, modifier : Box<dyn MapModifier>) {
|
||||
self.modifiers.push(modifier);
|
||||
}
|
||||
|
||||
pub fn build_map(&mut self) -> Map {
|
||||
let mut map = self.generator.generate_map(&mut self.rng);
|
||||
|
||||
// Build additional layers in turn
|
||||
for modifier in self.modifiers.iter() {
|
||||
modifier.modify_map(&mut self.rng, &mut map);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user