summaryrefslogtreecommitdiff
path: root/src/sim
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2026-08-13 01:56:09 -0700
committerKai Stevenson <kai@kaistevenson.com>2026-08-13 01:56:09 -0700
commitfeefeecec6c6050635b2c016452dfa1529575987 (patch)
tree024cc36c2c75e10b6bfd68f29c1494830bec37df /src/sim
parent7ce9781f86c56f932dabc3bc84b8d21d7be8fecc (diff)
big refactor for board
Diffstat (limited to 'src/sim')
-rw-r--r--src/sim/board.rs72
-rw-r--r--src/sim/cell.rs19
-rw-r--r--src/sim/chunk.rs25
-rw-r--r--src/sim/materials/mod.rs10
-rw-r--r--src/sim/materials/sand.rs6
-rw-r--r--src/sim/materials/water.rs46
-rw-r--r--src/sim/mod.rs4
-rw-r--r--src/sim/overlay.rs67
-rw-r--r--src/sim/sim.rs208
-rw-r--r--src/sim/world.rs66
10 files changed, 341 insertions, 182 deletions
diff --git a/src/sim/board.rs b/src/sim/board.rs
deleted file mode 100644
index 7af19be..0000000
--- a/src/sim/board.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-use crate::{
- config::{PIXEL_BUFFER_HEIGHT, PIXEL_BUFFER_WIDTH},
- sim::materials::MaterialId,
-};
-
-#[derive(Clone, Copy)]
-pub struct Cell {
- pub material: MaterialId,
- pub flags: u8,
-}
-
-impl Cell {
- pub fn void() -> Cell {
- Cell {
- material: MaterialId::Void,
- flags: 0,
- }
- }
- pub fn from_material(material: MaterialId) -> Cell {
- Cell { material, flags: 0 }
- }
-}
-
-pub struct Board {
- pub size_x: u32,
- pub size_y: u32,
- pub cells: Vec<Cell>,
-}
-
-impl Board {
- pub fn index_to_position(&self, idx: usize) -> (i32, i32) {
- let y = idx / self.size_x as usize;
- let x = idx % self.size_x as usize;
- let board_x = x as i32 - self.size_x as i32 / 2;
- let board_y = y as i32 - self.size_x as i32 / 2;
- return (board_x, board_y);
- }
- pub fn set_cell_at_position(&mut self, x: i32, y: i32, c: Cell) {
- // TODO: option?
- let idx = self.position_to_index(x, y).unwrap();
- self.cells[idx] = c;
- }
- pub fn cell_at_position(&self, x: i32, y: i32) -> Option<Cell> {
- Some(self.cells[self.position_to_index(x, y)?])
- }
- pub fn position_to_index(&self, x: i32, y: i32) -> Option<usize> {
- let board_x = x + self.size_x as i32 / 2;
- let board_y = y + self.size_y as i32 / 2;
-
- let on_board = board_x >= 0
- && board_x < self.size_x as i32
- && board_y >= 0
- && board_y < self.size_y as i32;
-
- if !on_board {
- return None;
- }
-
- return Some((board_y * self.size_x as i32 + board_x) as usize);
- }
- pub fn empty() -> Board {
- let size_x = PIXEL_BUFFER_WIDTH * 2;
- let size_y = PIXEL_BUFFER_HEIGHT * 2;
- let cells: Vec<Cell> = vec![Cell::void(); (size_x * size_y) as usize];
-
- Board {
- size_x,
- size_y,
- cells,
- }
- }
-}
diff --git a/src/sim/cell.rs b/src/sim/cell.rs
new file mode 100644
index 0000000..2e26ddc
--- /dev/null
+++ b/src/sim/cell.rs
@@ -0,0 +1,19 @@
+use crate::sim::materials::MaterialId;
+
+#[derive(Clone, Copy)]
+pub struct Cell {
+ pub material: MaterialId,
+ pub flags: u8,
+}
+
+impl Cell {
+ pub fn void() -> Cell {
+ Cell {
+ material: MaterialId::Void,
+ flags: 0,
+ }
+ }
+ pub fn from_material(material: MaterialId) -> Cell {
+ Cell { material, flags: 0 }
+ }
+}
diff --git a/src/sim/chunk.rs b/src/sim/chunk.rs
new file mode 100644
index 0000000..154cf82
--- /dev/null
+++ b/src/sim/chunk.rs
@@ -0,0 +1,25 @@
+use crate::{
+ config::{CELLS_IN_CHUNK, CHUNK_SIZE},
+ sim::cell::Cell,
+};
+
+pub struct Chunk {
+ pub cells: Box<[Cell; CELLS_IN_CHUNK as usize]>,
+}
+
+impl Chunk {
+ #[inline]
+ pub fn get_cell_at_local_position(&self, x: u8, y: u8) -> Cell {
+ self.cells[x as usize + y as usize * CHUNK_SIZE as usize]
+ }
+ #[inline]
+ pub fn set_cell_at_local_position(&mut self, x: u8, y: u8, cell: Cell) {
+ self.cells[x as usize + y as usize * CHUNK_SIZE as usize] = cell;
+ }
+
+ pub fn void() -> Self {
+ Chunk {
+ cells: Box::new([Cell::void(); CELLS_IN_CHUNK]),
+ }
+ }
+}
diff --git a/src/sim/materials/mod.rs b/src/sim/materials/mod.rs
index 410236d..86ae7d5 100644
--- a/src/sim/materials/mod.rs
+++ b/src/sim/materials/mod.rs
@@ -14,7 +14,7 @@ pub enum MaterialId {
pub struct MaterialDef {
pub name: &'static str,
- pub color: [u8; 4],
+ pub color: (u8, u8, u8, u8),
pub density: u8,
pub sim_update: Option<fn(ctx: &mut UpdateCtx) -> ()>,
}
@@ -22,25 +22,25 @@ pub struct MaterialDef {
static MATERIALS: [MaterialDef; 4] = [
MaterialDef {
name: "Void",
- color: [0x00, 0x00, 0x00, 0xFF],
+ color: (0x00, 0x00, 0x00, 0xFF),
density: 0,
sim_update: None,
},
MaterialDef {
name: "Sand",
- color: [0xDE, 0xCB, 0x85, 0xFF],
+ color: (0xDE, 0xCB, 0x85, 0xFF),
density: 50,
sim_update: Some(sand::sim_update),
},
MaterialDef {
name: "Wood",
- color: [0x85, 0x56, 0x1D, 0xFF],
+ color: (0x85, 0x56, 0x1D, 0xFF),
density: 50,
sim_update: None,
},
MaterialDef {
name: "Water",
- color: [0x38, 0xA9, 0xFF, 0xFF],
+ color: (0x38, 0xA9, 0xFF, 0xFF),
density: 40,
sim_update: Some(water::sim_update),
},
diff --git a/src/sim/materials/sand.rs b/src/sim/materials/sand.rs
index 6a780f3..ac8889f 100644
--- a/src/sim/materials/sand.rs
+++ b/src/sim/materials/sand.rs
@@ -3,8 +3,8 @@ use crate::sim::sim::UpdateCtx;
#[inline]
pub fn sim_update(ctx: &mut UpdateCtx) {
ctx.candidates_swap(&[
- (ctx.self_x, ctx.self_y + 1),
- (ctx.self_x - 1 + 2 * ctx.seqno_parity as i32, ctx.self_y + 1),
- (ctx.self_x + 1 - 2 * ctx.seqno_parity as i32, ctx.self_y + 1),
+ (0, 1),
+ (-1 + 2 * ctx.seqno_parity as i32, 1),
+ (1 - 2 * ctx.seqno_parity as i32, 1),
]);
}
diff --git a/src/sim/materials/water.rs b/src/sim/materials/water.rs
index d875aee..65782dd 100644
--- a/src/sim/materials/water.rs
+++ b/src/sim/materials/water.rs
@@ -4,21 +4,21 @@ use crate::sim::sim::UpdateCtx;
pub fn sim_update(ctx: &mut UpdateCtx) {
// if the water can fall, do so
if ctx.candidates_swap(&[
- (ctx.self_x, ctx.self_y + 1),
- (ctx.self_x - 1 + 2 * ctx.seqno_parity as i32, ctx.self_y + 1),
- (ctx.self_x + 1 - 2 * ctx.seqno_parity as i32, ctx.self_y + 1),
+ (0, 1),
+ (-1 + 2 * ctx.seqno_parity as i32, 1),
+ (1 - 2 * ctx.seqno_parity as i32, 1),
]) {
return;
}
// if the water can't fall, check if we can move left or right
// these are inverted on parity so that we don't preference a direction
- let left_target = ctx.board.cell_at_position(ctx.self_x - 1, ctx.self_y);
- let can_move_left = left_target
- .is_some_and(|c| c.material.def().density < ctx.self_cell.material.def().density);
- let right_target = ctx.board.cell_at_position(ctx.self_x + 1, ctx.self_y);
- let can_move_right = right_target
- .is_some_and(|c| c.material.def().density < ctx.self_cell.material.def().density);
+ let left_target = ctx.get_cell(-1, 0);
+ let can_move_left =
+ left_target.is_some_and(|c| c.material.def().density < ctx.material.density);
+ let right_target = ctx.get_cell(1, 0);
+ let can_move_right =
+ right_target.is_some_and(|c| c.material.def().density < ctx.material.density);
// we can't move down or to other side, so we're stuck
if !can_move_left && !can_move_right {
@@ -40,39 +40,29 @@ pub fn sim_update(ctx: &mut UpdateCtx) {
}
let offset = side * (1 + i / 2);
- let hole_target = ctx
- .board
- .cell_at_position(ctx.self_x + offset, ctx.self_y + 1);
+ let hole_target = ctx.get_cell(offset, 1);
if let Some(target) = hole_target
- && target.material.def().density < ctx.self_cell.material.def().density
+ && target.material.def().density < ctx.material.density
{
// we identified a hole and we know that the space on this side is open
// move toward the hole
- let move_target = if side == 1 { right_target } else { left_target }.clone();
// new_target.flags = new_target.flags ^ 0b1;
- // safe to unwrap
- ctx.board
- .set_cell_at_position(ctx.self_x, ctx.self_y, move_target.unwrap());
- ctx.board
- .set_cell_at_position(ctx.self_x + side, ctx.self_y, ctx.self_cell);
+ ctx.candidates_swap(&[(side, 0)]);
return;
}
}
// we didn't find a hole, so just move "randomly" on the same surface
// TODO when to settle?
- let (target, target_x) = if !can_move_left {
- (right_target, 1)
+ let target_x = if !can_move_left {
+ 1
} else if !can_move_right {
- (left_target, -1)
+ -1
} else if ctx.seqno_parity % 2 == 1 {
- (right_target, 1)
+ 1
} else {
- (left_target, -1)
+ -1
};
- ctx.board
- .set_cell_at_position(ctx.self_x, ctx.self_y, target.unwrap());
- ctx.board
- .set_cell_at_position(ctx.self_x + target_x, ctx.self_y, ctx.self_cell);
+ ctx.candidates_swap(&[(target_x, 0)]);
}
diff --git a/src/sim/mod.rs b/src/sim/mod.rs
index 2b29628..7553f89 100644
--- a/src/sim/mod.rs
+++ b/src/sim/mod.rs
@@ -1,4 +1,6 @@
-pub mod board;
+pub mod cell;
+pub mod chunk;
pub mod materials;
pub mod overlay;
pub mod sim;
+pub mod world;
diff --git a/src/sim/overlay.rs b/src/sim/overlay.rs
index e0560b5..c9fdf17 100644
--- a/src/sim/overlay.rs
+++ b/src/sim/overlay.rs
@@ -1,48 +1,49 @@
-use crate::{Config, Input, sim::board::Board};
+use crate::{Config, Input, sim::world::World};
pub fn create_compute_combined_overlay_offset(
- board: &Board,
+ world: &World,
config: &Config,
input: &Input,
) -> impl Fn(i32, i32) -> (u8, u8, u8, u8) {
+ puffin::profile_function!();
// TODO fix this move?
- move |x: i32, y: i32| {
+ move |pixel_x: i32, pixel_y: i32| {
// could allow negative offsets too
let mut offset: (u8, u8, u8, u8) = (0x00, 0x00, 0x00, 0x00);
- // bounds
- // left
- let xl = -((board.size_x / 2 + 1) as i32);
- // right
- let xu = (board.size_x / 2 + 1) as i32;
- // bottom
- let yl = -((board.size_y / 2 + 1) as i32);
- // top
- let yu = (board.size_y / 2 + 1) as i32;
+ // // bounds
+ // // left
+ // let xl = -((board.get_game_width() / 2 + 1) as i32);
+ // // right
+ // let xu = (board.get_game_width() / 2 + 1) as i32;
+ // // bottom
+ // let yl = -((board.get_game_height() / 2 + 1) as i32);
+ // // top
+ // let yu = (board.get_game_height() / 2 + 1) as i32;
- if ((x == xl || x == xu) && (y <= yu && y >= yl))
- || (y == yl || y == yu) && (x <= xu && x >= xl)
- {
- offset.0 = offset.0.saturating_add(0xFF);
- offset.1 = offset.1.saturating_add(0xFF);
- offset.2 = offset.2.saturating_add(0xFF);
- }
+ // if ((pixel_x == xl || pixel_x == xu) && (pixel_y <= yu && pixel_y >= yl))
+ // || (pixel_y == yl || pixel_y == yu) && (pixel_x <= xu && pixel_x >= xl)
+ // {
+ // offset.0 = offset.0.saturating_add(0xFF);
+ // offset.1 = offset.1.saturating_add(0xFF);
+ // offset.2 = offset.2.saturating_add(0xFF);
+ // }
- // grid
- if x % 30 == 0 || y % 30 == 0 {
- offset.0 = offset.0.saturating_add(0x10);
- offset.1 = offset.1.saturating_add(0x10);
- offset.2 = offset.2.saturating_add(0x10);
- }
+ // // grid
+ // if pixel_x % 30 == 0 || pixel_y % 30 == 0 {
+ // offset.0 = offset.0.saturating_add(0x10);
+ // offset.1 = offset.1.saturating_add(0x10);
+ // offset.2 = offset.2.saturating_add(0x10);
+ // }
- // brush/selection
- if input.last_mouse_pos_on_board.is_some_and(|p| {
- ((x - p.0).pow(2) + (y - p.1).pow(2)) < (config.brush_radius as i32).pow(2)
- }) {
- offset.0 = offset.0.saturating_add(0x82);
- offset.1 = offset.1.saturating_add(0xA1);
- offset.2 = offset.2.saturating_add(0xAD);
- }
+ // // brush/selection
+ // if input.last_mouse_pos_on_board.is_some_and(|p| {
+ // ((pixel_x - p.0).pow(2) + (pixel_y - p.1).pow(2)) < (config.brush_radius as i32).pow(2)
+ // }) {
+ // offset.0 = offset.0.saturating_add(0x82);
+ // offset.1 = offset.1.saturating_add(0xA1);
+ // offset.2 = offset.2.saturating_add(0xAD);
+ // }
return offset;
}
diff --git a/src/sim/sim.rs b/src/sim/sim.rs
index 350b823..5cd065e 100644
--- a/src/sim/sim.rs
+++ b/src/sim/sim.rs
@@ -1,26 +1,100 @@
-use crate::{Board, sim::board::Cell};
+use std::marker::PhantomData;
-pub struct UpdateCtx<'a> {
- pub self_x: i32,
- pub self_y: i32,
- pub self_cell: Cell,
- pub delta_time: f32,
+use crate::{
+ config::CHUNK_SIZE,
+ sim::{cell::Cell, chunk::Chunk, materials::MaterialDef, world::World},
+};
+
+struct ChunkAccess<'a> {
+ ptr: *mut Chunk,
+ len: usize,
+ _marker: PhantomData<&'a mut [Chunk]>,
+}
+
+impl<'a> ChunkAccess<'a> {
+ pub fn new(chunks: &'a mut [Chunk]) -> Self {
+ Self {
+ ptr: chunks.as_mut_ptr(),
+ len: chunks.len(),
+ _marker: PhantomData,
+ }
+ }
+ unsafe fn get(&self, i: usize) -> &'a mut Chunk {
+ debug_assert!(i < self.len);
+ unsafe { &mut *self.ptr.add(i) }
+ }
+}
+
+pub struct UpdateCtx<'a, 'b, 'c> {
+ pub chunks: &'a mut [Option<&'b mut Chunk>; 9],
+ pub seqno: u64,
pub seqno_parity: u8,
- pub board: &'a mut Board,
+
+ pub x: i32,
+ pub y: i32,
+ pub cell: &'c mut Cell,
+ pub material: &'c MaterialDef,
+}
+
+fn get_cell(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<Cell> {
+ let dcx = x.div_euclid(CHUNK_SIZE as i32);
+ let dcy = y.div_euclid(CHUNK_SIZE as i32);
+ if dcx != 0 || dcy != 0 {
+ // in a different chunk
+ let nc_x = x.rem_euclid(CHUNK_SIZE as i32) as u8;
+ let nc_y = y.rem_euclid(CHUNK_SIZE as i32) as u8;
+
+ return if let Some(chunk) = &chunks[(dcx + 1 + (dcy + 1) * 3) as usize] {
+ Some(chunk.get_cell_at_local_position(nc_x, nc_y))
+ } else {
+ None
+ };
+ } else {
+ if let Some(target) = &chunks[4] {
+ Some(target.get_cell_at_local_position(x as u8, y as u8))
+ } else {
+ None
+ }
+ }
+}
+
+pub fn set_cell(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, cell: Cell) {
+ let dcx = x.div_euclid(CHUNK_SIZE as i32);
+ let dcy = y.div_euclid(CHUNK_SIZE as i32);
+ if dcx != 0 || dcy != 0 {
+ // in a different chunk
+ let nc_x = x.rem_euclid(CHUNK_SIZE as i32) as u8;
+ let nc_y = y.rem_euclid(CHUNK_SIZE as i32) as u8;
+
+ if let Some(chunk) = &mut chunks[(dcx + 1 + (dcy + 1) * 3) as usize] {
+ chunk.set_cell_at_local_position(nc_x, nc_y, cell);
+ }
+ } else {
+ if let Some(target) = &mut chunks[4] {
+ target.set_cell_at_local_position(x as u8, y as u8, cell);
+ }
+ }
}
-impl UpdateCtx<'_> {
+impl UpdateCtx<'_, '_, '_> {
+ pub fn get_cell(&self, dx: i32, dy: i32) -> Option<Cell> {
+ let x = self.x + dx;
+ let y = self.y + dy;
+ get_cell(self.chunks, x, y)
+ }
+
+ fn set_cell(&mut self, dx: i32, dy: i32, cell: Cell) {
+ let x = self.x + dx;
+ let y = self.y + dy;
+ set_cell(self.chunks, x, y, cell);
+ }
+
pub fn candidates_swap(&mut self, candidates: &[(i32, i32)]) -> bool {
- for candidate in candidates {
- let target = self.board.cell_at_position(candidate.0, candidate.1);
- if let Some(target) = target
- && target.material.def().density < self.self_cell.material.def().density
- {
- // swap the cells
- self.board
- .set_cell_at_position(self.self_x, self.self_y, target);
- self.board
- .set_cell_at_position(candidate.0, candidate.1, self.self_cell);
+ for &(dx, dy) in candidates {
+ let candidate_cell = self.get_cell(dx, dy);
+ if candidate_cell.is_some_and(|c| c.material.def().density < self.material.density) {
+ self.set_cell(0, 0, candidate_cell.unwrap());
+ self.set_cell(dx, dy, *self.cell);
return true;
}
}
@@ -28,36 +102,90 @@ impl UpdateCtx<'_> {
}
}
-// TODO: chunks
-pub fn sim_tick(board: &mut Board, seqno: u64, delta_time: f32) {
+pub fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) {
+ puffin::profile_function!();
// scan bottom to top to enable contiguous falling
let seqno_parity = (seqno as u8) & 0b1;
- let bx = (board.size_x / 2) as i32;
- let by = (board.size_y / 2) as i32;
- for y in (-by..by + 1).rev() {
- // invert scan order on every other frame
- for col in -bx..bx + 1 {
- let x = if seqno_parity == 0 { col } else { -col };
+ if chunks[4].is_some() {
+ for y in (0..CHUNK_SIZE as i32).rev() {
+ for i in 0..CHUNK_SIZE as i32 {
+ let x = if seqno_parity == 0 {
+ i
+ } else {
+ (CHUNK_SIZE as i32) - i - 1
+ };
- let cell = board.cell_at_position(x, y);
- if let Some(mut cur) = cell
- && cur.flags & 0b1 == seqno_parity
- {
- // flip the parity bit
- cur.flags = cur.flags ^ 0b1;
+ let mut cell = get_cell(chunks, x, y).unwrap();
+ if cell.flags & 0b1 == seqno_parity {
+ // flip the parity bit
+ // TODO if the cell doesn't move this doesn't stay
+ cell.flags = cell.flags ^ 0b1;
+ let material = cell.material.def();
- if let Some(update) = cur.material.def().sim_update {
- update(&mut UpdateCtx {
- self_x: x,
- self_y: y,
- self_cell: cur,
- board,
- delta_time,
+ let mut update_ctx = UpdateCtx {
+ chunks,
+ seqno,
seqno_parity,
- });
+
+ x,
+ y,
+ cell: &mut cell,
+ material,
+ };
+
+ if let Some(update) = material.sim_update {
+ update(&mut update_ctx);
+ }
}
}
}
}
}
+
+const NEIGHBORHOOD_OFFSETS: [(i32, i32); 9] = [
+ (-1, -1),
+ (0, -1),
+ (1, -1),
+ (-1, 0),
+ (0, 0),
+ (1, 0),
+ (-1, 1),
+ (0, 1),
+ (1, 1),
+];
+
+pub fn sim_tick(world: &mut World, seqno: u64) {
+ puffin::profile_function!();
+ let mut update_groups: [Vec<(i32, i32)>; 9] = Default::default();
+
+ // assign a color to each chunk s.t. every chunk is surrounded by <= 8 chunks of different colors
+ // --------------------
+ // | 0, 1, 2, 0, 1, 2 |
+ // | 3, 4, 5, 3, 4, 5 |
+ // | 6, 7, 8, 6, 7, 8 |
+ // | 0, 1, 2, 0, 1, 2 |
+ // | 3, 4, 5, 3, 4, 5 |
+ // | 6, 7, 8, 6, 7, 8 |
+ // --------------------
+
+ for (&(cx, cy), _) in &world.chunk_position_to_chunk_idx {
+ let color = (cx.rem_euclid(3) * 3 + cy.rem_euclid(3)) as usize;
+ update_groups[color].push((cx, cy));
+ }
+
+ for group in &update_groups {
+ let access = ChunkAccess::new(&mut world.chunks);
+ // TODO this can be parallelized since they will never share neighbours
+ for &(cx, cy) in group {
+ let mut chunks: [Option<&mut Chunk>; 9] = NEIGHBORHOOD_OFFSETS.map(|(dx, dy)| {
+ world
+ .chunk_position_to_chunk_idx
+ .get(&(cx + dx, cy + dy))
+ .map(|&idx| unsafe { access.get(idx) })
+ });
+
+ sim_tick_chunk(&mut chunks, seqno);
+ }
+ }
+}
diff --git a/src/sim/world.rs b/src/sim/world.rs
new file mode 100644
index 0000000..3c7b05b
--- /dev/null
+++ b/src/sim/world.rs
@@ -0,0 +1,66 @@
+use std::collections::HashMap;
+
+use crate::{
+ config::CHUNK_SIZE,
+ sim::{cell::Cell, chunk::Chunk},
+};
+
+pub struct World {
+ pub chunks: Vec<Chunk>,
+ // TODO FxHashMap?
+ pub chunk_position_to_chunk_idx: HashMap<(i32, i32), usize>,
+}
+
+impl World {
+ #[inline]
+ pub fn split_game_position(x: i32, y: i32) -> ((i32, i32), (u8, u8)) {
+ (
+ (
+ // TODO is this cast expensive?
+ x.div_euclid(CHUNK_SIZE as i32),
+ y.div_euclid(CHUNK_SIZE as i32),
+ ),
+ (
+ x.rem_euclid(CHUNK_SIZE as i32) as u8,
+ y.rem_euclid(CHUNK_SIZE as i32) as u8,
+ ),
+ )
+ }
+
+ // VERY EXPENSIVE
+ pub fn get_cell_from_game_position(&self, x: i32, y: i32) -> Option<Cell> {
+ let ((cx, cy), (dx, dy)) = World::split_game_position(x, y);
+ self.chunk_position_to_chunk_idx
+ .get(&(cx, cy))
+ .map(|&idx| self.chunks[idx].get_cell_at_local_position(dx, dy))
+ }
+
+ // VERY EXPENSIVE
+ pub fn set_cell_from_game_position(&mut self, x: i32, y: i32, cell: Cell) -> () {
+ let ((cx, cy), (dx, dy)) = World::split_game_position(x, y);
+ if let Some(&idx) = self.chunk_position_to_chunk_idx.get(&(cx, cy)) {
+ self.chunks[idx].set_cell_at_local_position(dx, dy, cell);
+ }
+ }
+
+ pub fn insert(&mut self, x: i32, y: i32, chunk: Chunk) -> () {
+ self.chunk_position_to_chunk_idx
+ .insert((x, y), self.chunks.len());
+ self.chunks.push(chunk);
+ }
+
+ pub fn from_default_size() -> Self {
+ let mut world = World {
+ chunks: Vec::new(),
+ chunk_position_to_chunk_idx: HashMap::new(),
+ };
+
+ for y in -10..10 {
+ for x in -10..10 {
+ world.insert(x, y, Chunk::void());
+ }
+ }
+
+ world
+ }
+}