3个版本

新版本 0.2.2 2024年8月24日
0.2.1 2024年8月24日
0.2.0 2024年8月24日

#260 in 算法

Download history 185/week @ 2024-08-19

185 每月下载量

MIT 许可证

175KB
3K SLoC

滚动网格的实现。一种存储在二维(或三维)网格中的值的类型,并且可以将网格转换到新的位置。这对于想要存储可以移动的区域的缓冲区的伪无限世界来说很理想。移动区域(并设置新值)的操作是O(n),其中n是移动操作期间将改变位置的单元格数量。移动操作将调用一个回调函数,传递旧位置、新位置、旧值,并期望回调函数返回新值。此功能允许您移动网格并保存正在卸载的任何单元格,同时加载新单元格。移动操作可以被视为二维(或三维)环形缓冲区。如果是1D,移动操作可能看起来像这样

Offset: 1
   old: [0, 1, 2, 3, 4]
   new: [1, 2, 3, 4, 0]

如您所见,0被移动到了缓冲区的末尾。由于0被移动到了缓冲区的末尾,因此需要重新加载,所以移动操作将调用reload

reload(0, 5, Some(0))

假设您有一个这样的网格

0123
4567
89AB
CDEF

如果您要将其平移偏移量 (1, 1),结果将是这个网格(在重新定位期间不修改值)

5674
9AB8
DEFC
1230

这不是这个库的优点。在重新定位网格时,它根本不会移动任何元素。相反,它跟踪移动偏移量,并允许重新定位操作非常快,因为它实际上并没有在内存中移动任何东西。它计算偏移量和包装偏移量((0, 0)开始的偏移量,所有之前的都包装到末尾)。然后算法计算哪些单元格应该更改,并调用提供的回调函数来处理这些单元格。

以下是一些实际应用的例子

let mut grid = RollGrid2D::new_with_init(4, 4, (0, 0), |pos: (i32, i32)| {
    Some(pos)
});
println!("Initial grid:");
print_grid(&grid);
let mut iterations = 0;
let mut changes = vec![];
grid.reposition((1, 2), |old, new, old_value| {
    iterations += 1;
    changes.push((old, new));
    Some(new)
});
println!("Changes:");
for (old, new) in changes {
    println!("{old:?} moved to {new:?}");
}
println!("Grid repositioned to (1, 2) with {iterations} iterations:");
print_grid(&grid);
println!("Cell at (4, 5): {:?}", grid.get_copy((4, 5)).unwrap());
println!("Cell at (0, 0): {:?}", grid.get_copy((0, 0)));

输出

Initial grid:
[
    [( 0,  0), ( 1,  0), ( 2,  0), ( 3,  0)]
    [( 0,  1), ( 1,  1), ( 2,  1), ( 3,  1)]
    [( 0,  2), ( 1,  2), ( 2,  2), ( 3,  2)]
    [( 0,  3), ( 1,  3), ( 2,  3), ( 3,  3)]
]
Changes:
(0, 2) moved to (4, 2)
(0, 3) moved to (4, 3)
(1, 0) moved to (1, 4)
(2, 0) moved to (2, 4)
(3, 0) moved to (3, 4)
(1, 1) moved to (1, 5)
(2, 1) moved to (2, 5)
(3, 1) moved to (3, 5)
(0, 0) moved to (4, 4)
(0, 1) moved to (4, 5)
Grid repositioned to (1, 2) with 10 iterations:
[
    [( 1,  2), ( 2,  2), ( 3,  2), ( 4,  2)]
    [( 1,  3), ( 2,  3), ( 3,  3), ( 4,  3)]
    [( 1,  4), ( 2,  4), ( 3,  4), ( 4,  4)]
    [( 1,  5), ( 2,  5), ( 3,  5), ( 4,  5)]
]
Cell at (4, 5): (4, 5)
Cell at (0, 0): None

另一个更高级的例子

chunks.reposition((chunk_x, chunk_z), |old_pos, (x, z), chunk| {
    let mut chunk = chunk.expect("Chunk was None");
    self.unload_chunk(&mut chunk);
    chunk.block_offset = Coord::new(x * 16, WORLD_BOTTOM, z * 16);
    if let Some(region) = regions.get_mut((x >> 5, z >> 5)) {
        let result = region.read((x & 31, z & 31), |reader| {
            chunk.read_from(reader, self)
        });
        match result {
            Err(Error::ChunkNotFound) => (/* Do nothing, that just means it's an empty chunk */),
            Err(err) => {
                panic!("Error: {err}");
            }
            _ => (),
        }
        chunk.edit_time = region.get_timestamp((x & 31, z & 31));
    }
    chunk.block_offset.x = x * 16;
    chunk.block_offset.z = z * 16;
    Some(chunk)
});

这个 reposition 方法适用于rollgrid的2D和3D变体。您可以将此代码修改为适应您的目的。

没有运行时依赖项