use owo_colors::OwoColorize;
use rand::prelude::*;
use rand::rngs::ThreadRng;
use noise::{Perlin, NoiseFn, Seedable};
use smart_default::*;
use rayon::prelude::*;
use std::fmt;
#[derive(Debug, SmartDefault)]
pub struct NoiseOptions {
#[default = 1.0]
pub frequency: f64,
#[default = 1.0]
pub redistribution: f64,
#[default = 1]
pub octaves: usize,
}
impl NoiseOptions {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Default)]
pub struct Generator {
pub map: Vec<usize>,
pub width: usize,
pub height: usize,
pub noise_options: NoiseOptions,
rooms: Vec<Room>,
seed: u32,
}
impl Generator {
pub fn new() -> Self {
let seed: u32 = rand::thread_rng().gen();
Self {
seed,
..Self::default()
}
}
fn spawn_room(&mut self, number: usize, size: &Size, rng: &mut ThreadRng) -> &mut Self {
let mut x = rng.gen_range(0, self.width);
let mut y = rng.gen_range(0, self.height);
let width = rng.gen_range(size.min_size.0, size.max_size.0);
let height = rng.gen_range(size.min_size.1, size.max_size.1);
if x + width > self.width {
x = self.width - width;
}
if y + height > self.height {
y = self.height - height;
}
let mut collides = false;
let room = Room::new(x, y, width, height);
for other_room in &self.rooms {
if room.intersects(&other_room) {
collides = true;
break;
}
}
if !collides {
for row in 0..height {
for col in 0..width {
let pos = (room.x + col, room.y + row);
self.set(pos.0, pos.1, number);
}
}
self.rooms.push(room);
}
self
}
pub fn with_seed(mut self, seed: u32) -> Self {
self.seed = seed;
self
}
pub fn with_options(mut self, options: NoiseOptions) -> Self {
self.noise_options = options;
self
}
pub fn show(&self) {
println!("{}", self);
}
pub fn with_size(mut self, width: usize, height: usize) -> Self {
self.map = vec![0; width * height];
self.width = width;
self.height = height;
self
}
pub fn spawn_perlin<F: Fn(f64) -> usize + Sync>(mut self, f: F) -> Self {
let perlin = Perlin::new().set_seed(self.seed);
let redistribution = self.noise_options.redistribution;
let freq = self.noise_options.frequency;
let octaves = self.noise_options.octaves;
let width = self.width;
self.map.par_iter_mut().enumerate().for_each(|(pos, index)| {
let x = pos % width;
let y = pos / width;
let nx = x as f64 / width as f64;
let ny = y as f64 / width as f64;
let value = (0..octaves).fold(0., |acc, n| {
let power = 2.0f64.powf(n as f64);
let modifier = 1. / power;
acc + modifier * perlin.get([nx * freq * power, ny * freq * power])
});
*index = f((value.powf(redistribution) + 1.) / 2.);
});
self
}
pub fn spawn_rooms(mut self, number: usize, rooms: usize, size: &Size) -> Self {
let mut rng = rand::thread_rng();
for _ in 0..rooms {
self.spawn_room(number, size, &mut rng);
}
self
}
pub fn get(&self, x: usize, y: usize) -> usize {
self.map[x + y * self.width]
}
pub fn set(&mut self, x: usize, y: usize, value: usize) {
self.map[x + y * self.width] = value;
}
pub fn get_2d_map(&self) -> Vec<Vec<usize>> {
self.map.chunks(self.width).fold(vec![], |mut map, chunk| {
map.push(chunk.into());
map
})
}
}
impl fmt::Display for Generator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for y in 0..self.height {
for x in 0..self.width {
let value = self.get(x, y);
let remainder = value % 7;
match remainder {
1 => write!(f, "{:?} ", value.red())?,
2 => write!(f, "{:?} ", value.green())?,
3 => write!(f, "{:?} ", value.cyan())?,
4 => write!(f, "{:?} ", value.magenta())?,
5 => write!(f, "{:?} ", value.white())?,
6 => write!(f, "{:?} ", value.yellow())?,
_ => write!(f, "{:?} ", value.blue())?,
}
}
if y < self.height - 1 {
write!(f, "\n")?
}
}
Ok(())
}
}
pub struct Size {
pub min_size: (usize, usize),
pub max_size: (usize, usize),
}
impl Size {
pub fn new(min_size: (usize, usize), max_size: (usize, usize)) -> Self {
Self { min_size, max_size }
}
}
#[derive(Debug, Default)]
struct Room {
x: usize,
y: usize,
x2: usize,
y2: usize,
width: usize,
height: usize,
}
impl Room {
fn new(x: usize, y: usize, width: usize, height: usize) -> Self {
Room {
x,
y,
x2: x + width,
y2: y + height,
width,
height,
}
}
fn intersects(&self, other: &Self) -> bool {
self.x <= other.x2 && self.x2 >= other.x && self.y <= other.y2 && self.y2 >= other.y
}
}