From feefeecec6c6050635b2c016452dfa1529575987 Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Thu, 13 Aug 2026 01:56:09 -0700 Subject: big refactor for board --- src/sim/sim.rs | 212 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 170 insertions(+), 42 deletions(-) (limited to 'src/sim/sim.rs') 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 { + 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 { + 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 }; - - 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; - - if let Some(update) = cur.material.def().sim_update { - update(&mut UpdateCtx { - self_x: x, - self_y: y, - self_cell: cur, - board, - delta_time, + 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 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(); + + 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); + } + } +} -- cgit v1.3.1