From 4bf8af7de5d19409d62b8e137c4f169c66a0901c Mon Sep 17 00:00:00 2001 From: Paul Montag Date: Wed, 15 Apr 2026 06:29:44 -0500 Subject: [PATCH] SubMatrixes --- src/grome/matrix.zig | 116 +++++++++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 27 deletions(-) diff --git a/src/grome/matrix.zig b/src/grome/matrix.zig index 2019571..2ee7588 100644 --- a/src/grome/matrix.zig +++ b/src/grome/matrix.zig @@ -6,6 +6,12 @@ const Allocator = mem.Allocator; pub const Point = struct { x: usize, y: usize, + + /// index returns the index in a flat array of the point givent he + /// provided width. + pub fn index(self: *const Point, width: usize) usize { + return (self.y * width) + self.x; + } }; /// Rect is a rectangle @@ -18,32 +24,27 @@ pub const Rect = struct { } }; -/// Area is the area -pub const Area = struct { - location: Point, - size: Rect, -}; - /// Matrix is a part of the game board -pub fn Matrix(comptime T: type, size: Rect) type { - const MatrixRowIterator = RowIterator(T, size.width); - +pub fn Matrix(comptime T: type) type { return struct { /// data is the game board itself. data: []T, + /// + size: Rect, + /// init creates the chunk, storing the bytes in memory. - pub fn initZero(gpa: Allocator) !@This() { + pub fn initZero(gpa: Allocator, size: Rect) !@This() { const data = try gpa.alloc(T, size.area()); @memset(data, mem.zeroes(T)); - return .{ .data = data }; + return .{ .data = data, .size = size }; } /// init creates the chunk, storing the bytes in memory. - pub fn init(gpa: Allocator, zero: T) !@This() { + pub fn init(gpa: Allocator, zero: T, size: Rect) !@This() { const data = try gpa.alloc(T, size.area()); @memset(data, zero); - return .{ .data = data }; + return .{ .data = data, .size = size }; } /// deinit frees the memory utilized to create the chunk @@ -53,18 +54,23 @@ pub fn Matrix(comptime T: type, size: Rect) type { // set sets the value of the specified location in memory pub fn set(self: *@This(), loc: Point, val: T) void { - const index = (size.width * loc.y) + loc.x; + const index = (self.size.width * loc.y) + loc.x; self.data[index] = val; } // set sets the value of the specified location in memory pub fn get(self: *const @This(), loc: Point) T { - const index = (size.width * loc.y) + loc.x; + const index = (self.size.width * loc.y) + loc.x; return self.data[index]; } - pub fn row_iterator(self: *@This()) MatrixRowIterator { - return .init(self.data); + pub fn row_iterator(self: *@This()) RowIterator(T) { + return .init(self); + } + + /// submatrix returns a [SubMatrix] + pub fn submatrix(self: *@This(), gpa: Allocator, loc: Point, area: Rect) !SubMatrix(T) { + return SubMatrix(T).init(gpa, self, loc, area); } }; } @@ -72,25 +78,25 @@ pub fn Matrix(comptime T: type, size: Rect) type { test "TestMatrix" { const testing = std.testing; - var mat = try Matrix(u64, Rect{ .width = 10, .height = 10 }).initZero(testing.allocator); + var mat = try Matrix(u64).initZero(testing.allocator, Rect{ .width = 10, .height = 10 }); defer mat.deinit(testing.allocator); mat.set(Point{ .x = 1, .y = 1 }, 100); try testing.expectEqual(100, mat.get(Point{ .x = 1, .y = 1 })); } -pub fn RowIterator(comptime T: type, width: usize) type { +pub fn RowIterator(comptime T: type) type { return struct { _index: usize, /// data is a reference to the board game data, now we will return it as /// each row _data: []T, - fn init(data: []T) @This() { - return .{ - ._index = 0, - ._data = data, - }; + // The width of each row of data + _width: usize, + + fn init(m: *Matrix(T)) @This() { + return .{ ._index = 0, ._data = m.data, ._width = m.size.width }; } /// next returns the next row until we have exhausted the slice @@ -100,8 +106,8 @@ pub fn RowIterator(comptime T: type, width: usize) type { } // increment a line at a time - defer self._index += width; - return self._data[self._index .. self._index + width]; + defer self._index += self._width; + return self._data[self._index .. self._index + self._width]; } }; } @@ -109,7 +115,7 @@ pub fn RowIterator(comptime T: type, width: usize) type { test "TestRowIterator" { const testing = std.testing; - var chunk = try Matrix(usize, Rect{ .height = 10, .width = 10 }).initZero(testing.allocator); + var chunk = try Matrix(usize).initZero(testing.allocator, Rect{ .height = 10, .width = 10 }); defer chunk.deinit(testing.allocator); for (0..10) |x| { @@ -124,3 +130,59 @@ test "TestRowIterator" { x += 1; } } + +// SubMatrix is a subset of a matrix which you can traverse to +pub fn SubMatrix(comptime T: type) type { + return struct { + // Data is made up of different sections of the + _data: [][]T, + + /// init takes the underlying data from the upstream matrix and creates + /// a submatrix of it + pub fn init(gpa: Allocator, m: *Matrix(T), loc: Point, size: Rect) !@This() { + var subData = try gpa.alloc([]T, size.height); + var index = loc.index(m.size.width); + + for (0..size.height) |x| { + const ending_index = index + size.width; + subData[x] = m.data[index..ending_index]; + index += m.size.width; + } + + return .{ + ._data = subData, + }; + } + + pub fn deinit(self: *@This(), gpa: Allocator) void { + gpa.free(self._data); + } + + /// get returns the type in the specified point + pub fn get(self: *const @This(), loc: Point) T { + return self._data[loc.y][loc.x]; + } + + /// set sets the value of the specified point + pub fn set(self: *@This(), loc: Point, val: T) void { + self._data[loc.y][loc.x] = val; + } + }; +} + +test "SubMatrix" { + const testing = std.testing; + + // create our matrix and submatrix + var m = try Matrix(u64).initZero(testing.allocator, .{ .height = 10, .width = 10 }); + defer m.deinit(testing.allocator); + var sub = try m.submatrix(testing.allocator, .{ .x = 4, .y = 4 }, .{ .height = 5, .width = 5 }); + defer sub.deinit(testing.allocator); + + // set the value + sub.set(.{ .x = 0, .y = 0 }, 100); + + // it should be set in both locations + try testing.expectEqual(100, sub.get(.{ .x = 0, .y = 0 })); + try testing.expectEqual(100, m.get(.{ .x = 4, .y = 4 })); +}