#![warn(missing_docs)]
use divrem::DivFloor;
use std::{array, collections::HashMap};
const CHUNK_SIZE: usize = 32;
const CHUNK_SIZE_I32: i32 = CHUNK_SIZE as i32;
type Chunk<T> = [[T; CHUNK_SIZE]; CHUNK_SIZE];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct ChunkCoordinate {
x: i32,
y: i32,
}
impl ChunkCoordinate {
fn get_from_coordinates(x: i32, y: i32) -> ChunkCoordinate {
ChunkCoordinate {
x: DivFloor::div_floor(x, CHUNK_SIZE_I32) * CHUNK_SIZE_I32,
y: DivFloor::div_floor(y, CHUNK_SIZE_I32) * CHUNK_SIZE_I32,
}
}
fn x_offset(&self, x: i32) -> usize {
let offset = x - self.x;
if (0..CHUNK_SIZE_I32).contains(&offset) {
return offset as usize;
}
panic!("Cannot find x_offset within this chunk!")
}
fn y_offset(&self, y: i32) -> usize {
let offset = y - self.y;
if (0..CHUNK_SIZE_I32).contains(&offset) {
return offset as usize;
}
panic!("Cannot find y_offset within this chunk!")
}
}
#[derive(Debug, Clone)]
pub struct ChunkMap<T> {
map: HashMap<ChunkCoordinate, Chunk<Option<T>>>,
}
impl<T: Clone> ChunkMap<T> {
pub fn new() -> ChunkMap<T> {
ChunkMap {
map: HashMap::new(),
}
}
fn get_slot(&mut self, x: i32, y: i32) -> &mut Option<T> {
let coord = ChunkCoordinate::get_from_coordinates(x, y);
&mut self.map.entry(coord).or_insert_with(
|| Self::empty_chunk()
)[coord.x_offset(x)][coord.y_offset(y)]
}
fn empty_chunk() -> [[Option<T>; CHUNK_SIZE]; CHUNK_SIZE] {
array::from_fn(|_| array::from_fn(|_| None))
}
pub fn get(&self, x: i32, y: i32) -> Option<&T> {
let coord = ChunkCoordinate::get_from_coordinates(x, y);
self.map.get(&coord)?[coord.x_offset(x)][coord.y_offset(y)].as_ref()
}
pub fn remove(&mut self, x: i32, y: i32) -> Option<T> {
let coord = ChunkCoordinate::get_from_coordinates(x, y);
let chunk = self.map.get_mut(&coord)?;
let value = &mut chunk[coord.x_offset(x)][coord.y_offset(y)];
value.take()
}
pub fn insert(&mut self, x: i32, y: i32, val: T) {
*self.get_slot(x, y) = Some(val);
}
}
#[cfg(test)]
mod tests {
use super::ChunkCoordinate;
use super::ChunkMap;
#[test]
fn check_chunk_coordinate() {
let c = ChunkCoordinate::get_from_coordinates(3, 3);
assert!(c.x == 0);
assert!(c.y == 0);
let c = ChunkCoordinate::get_from_coordinates(72, 3);
assert!(c.x == 64);
assert!(c.y == 0);
}
#[test]
fn get_none_chunkmap() {
let c = ChunkMap::<i32>::new();
assert_eq!(c.get(0, 0), None);
}
#[test]
fn get_none_string_chunkmap() {
let c = ChunkMap::<String>::new();
assert_eq!(c.get(0, 0), None);
}
#[test]
fn get_some_chunkmap() {
let mut c = ChunkMap::<i32>::new();
c.insert(3, 3, 7);
assert_eq!(c.get(3, 3), Some(&7));
c.insert(103, 103, 7);
assert_eq!(c.get(103, 103), Some(&7));
}
#[test]
fn get_some_string_chunkmap() {
let mut c = ChunkMap::<String>::new();
c.insert(
3,
3,
String::from("It is hardware that makes a machine fast"),
);
assert_eq!(
c.get(3, 3),
Some(&String::from("It is hardware that makes a machine fast"))
);
c.insert(
103,
103,
String::from("It's software that makes a fast machine slow"),
);
assert_eq!(
c.get(103, 103),
Some(&String::from(
"It's software that makes a fast machine slow"
))
);
}
#[test]
fn check_chunkmap_default() {
let mut c = ChunkMap::<i32>::new();
c.insert(4, 3, 0);
assert_eq!(c.get(4, 3), Some(&0));
assert_eq!(c.get(5, 3), None);
assert_eq!(c.get(65, 3), None);
}
}