So I’m learning Zig and trying to write a Arkanoid game in Zig.
I have to iterate through 2D array of bricks, check collision and update each brick status.
This is easy to do with high level languages. With C++ vector, we can use range-based for loop like this.
vector<vector<int>> items;
for (auto & row : items) {
for (auto &item: row) {
item += 1;
}
}
With C, we have to do this old fashion way by accessing each brick by i
and j
indexes.
Zig does support range-based for loop, so I want to use that instead of traditional i
and j
.
From Zig reference, we can modify 1D array’s elements like this.
test "for reference" {
var items = [_]i32{ 3, 4, 2 };
// Iterate over the slice by reference by
// specifying that the capture value is a pointer.
for (&items) |*value| {
value.* += 1;
}
try expect(items[0] == 4);
try expect(items[1] == 5);
try expect(items[2] == 3);
}
For 2D array, we also have that fancy utility. There’s a catch here is that we need to
reference item at both outer and inner loop, like C++. And because item of outer loop has
been referenced, we should not use &
a gain in the inner loop.
test "for reference 2d" {
var items = [_][3]i32{[_]i32{ 3, 4, 2 }};
// Iterate over the slice by reference by
// specifying that the capture value is a pointer.
for (&items) |*row| {
// value is pointer so we don't need to reference
for (row) |*item| {
item.* += 1;
}
}
try expect(items[0][0] == 4);
try expect(items[0][1] == 5);
try expect(items[0][2] == 3);
}
Let’s put C++ and Zig code side by side to see the difference.
for (auto & row : items) {
for (auto &item: row) {
item += 1;
}
}
for (&items) |*row| {
for (row) |*item| {
item.* += 1;
}
}
Here’s a bonus, to modify struct elements in 2D array.
const Point = struct {
x: f64,
y: f64,
};
var items = [_][3]Point{[_]Point{ .{ .x = 3, .y = 3 }, .{ .x = 4, .y = 4 }, .{ .x = 2, .y = 2 } }};
for (&items) |*value| {
for (value) |*item| {
item.x += 1;
item.y += 1;
}
}