From 3ee8f9b908174b524155da6a4e8bbe3318cd8cf6 Mon Sep 17 00:00:00 2001 From: Paul Montag Date: Thu, 16 Apr 2026 16:10:44 -0500 Subject: [PATCH] Fixed the iterator --- src/grome/matrix.zig | 140 ++++++++++++++++++++++++++++--------------- src/view/draw.zig | 2 + 2 files changed, 93 insertions(+), 49 deletions(-) diff --git a/src/grome/matrix.zig b/src/grome/matrix.zig index f0915c7..d9411be 100644 --- a/src/grome/matrix.zig +++ b/src/grome/matrix.zig @@ -2,6 +2,21 @@ const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; +pub const Error = error{ + OutOfBounds, +}; + +/// Rowable is a type which can return a row at a time. +fn Rowable(comptime T: type) type { + return struct { + rowFn: *const fn (self: *@This(), line: usize) Error![]T, + + pub fn row(self: *@This(), line: usize) Error![]T { + return self.rowFn(self, line); + } + }; +} + /// Point is a location within the matrix, (x,y) pub const Point = struct { x: usize, @@ -13,6 +28,14 @@ pub const Point = struct { return (self.y * width) + self.x; } + // add two points together and return the subsiquent point + pub fn add(self: *const Point, b: Point) Point { + return .{ + .x = self.x + b.x, + .y = self.y + b.y, + }; + } + pub fn bottom_right(self: *const Point, area: Rect) Point { return .{ .x = self.x + area.width, @@ -37,21 +60,23 @@ pub fn Matrix(comptime T: type) type { /// data is the game board itself. data: []T, - /// + /// defines the size of the matrix size: Rect, + rowable: Rowable(T), + /// init creates the chunk, storing the bytes in memory. pub fn initZero(gpa: Allocator, size: Rect) !@This() { const data = try gpa.alloc(T, size.area()); @memset(data, mem.zeroes(T)); - return .{ .data = data, .size = size }; + return .{ .data = data, .size = size, .rowable = .{ .rowFn = Matrix(T).row } }; } /// init creates the chunk, storing the bytes in memory. pub fn init(gpa: Allocator, zero: T, size: Rect) !@This() { const data = try gpa.alloc(T, size.area()); @memset(data, zero); - return .{ .data = data, .size = size }; + return .{ .data = data, .size = size, .rowable = .{ .rowFn = Matrix(T).row } }; } /// deinit frees the memory utilized to create the chunk @@ -60,25 +85,44 @@ pub fn Matrix(comptime T: type) type { } // set sets the value of the specified location in memory - pub fn set(self: *@This(), loc: Point, val: T) void { + pub fn set(self: *@This(), loc: Point, val: T) !void { + if (loc.x >= self.size.width or loc.y >= self.size.height) { + return Error.OutOfBounds; + } + 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 { + pub fn get(self: *const @This(), loc: Point) !T { + if (loc.x >= self.size.width or loc.y >= self.size.height) { + return Error.OutOfBounds; + } + const index = (self.size.width * loc.y) + loc.x; return self.data[index]; } - pub fn row_iterator(self: *@This()) RowIterator(T) { - return .init(self); - } - /// submatrix returns a [SubMatrix] pub fn submatrix(self: *@This(), loc: Point, area: Rect) SubMatrix(T) { return SubMatrix(T).init(self, loc, area); } + + // TODO: you were in the middle of debugging this + pub fn row(interface: *Rowable(T), line: usize) ![]T { + var self: *@This() = @fieldParentPtr("rowable", interface); + const index = line * self.size.width; + if (index >= self.data.len) { + return Error.OutOfBounds; + } + + return self.data[index .. index + self.size.width]; + } + + pub fn row_iterator(self: *@This()) RowIterator(T) { + return .init(&self.rowable); + } }; } @@ -88,33 +132,26 @@ test "TestMatrix" { 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 })); + try mat.set(Point{ .x = 1, .y = 1 }, 100); + try testing.expectEqualSlices(u64, ([_]u64{ 0, 100 } ++ ([_]u64{0} ** 8))[0..], try mat.rowable.row(1)); + try testing.expectEqual(100, try mat.get(Point{ .x = 1, .y = 1 })); } pub fn RowIterator(comptime T: type) type { return struct { - _index: usize, + current: usize, /// data is a reference to the board game data, now we will return it as /// each row - _data: []T, + rows: *Rowable(T), - // 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 }; + fn init(rowable: *Rowable(T)) @This() { + return .{ .rows = rowable, .current = 0 }; } - /// next returns the next row until we have exhausted the slice + /// next returns the next row until we have exhausted the Rowable pub fn next(self: *@This()) ?[]T { - if (self._index >= self._data.len) { - return null; - } - - // increment a line at a time - defer self._index += self._width; - return self._data[self._index .. self._index + self._width]; + defer self.current += 1; + return self.rows.row(self.current) catch null; } }; } @@ -126,7 +163,7 @@ test "TestRowIterator" { defer chunk.deinit(testing.allocator); for (0..10) |x| { - chunk.set(Point{ .x = x, .y = x }, x); + try chunk.set(Point{ .x = x, .y = x }, x); } var iter = chunk.row_iterator(); @@ -143,34 +180,43 @@ pub fn SubMatrix(comptime T: type) type { return struct { // Data is made up of different sections of the _m: *Matrix(T), - // the width of the submatrix - _width: usize, - // _index defines where we are in the byte slice - _index: usize, - // Where we stop iterating - _endingIndex: usize, + _offset: Point, + _mask: Rect, + //_width: usize, + rowable: Rowable(T), /// init takes the underlying data from the upstream matrix and creates /// a submatrix of it pub fn init(m: *Matrix(T), loc: Point, size: Rect) @This() { - return .{ ._m = m, ._index = loc.index(m.size.width), ._endingIndex = loc.bottom_right(size).index(m.size.width), ._width = size.width }; + return .{ ._m = m, ._offset = loc, ._mask = size, .rowable = Rowable(T){ .rowFn = @This().row } }; } - // jump moves the pointer forward - pub fn skip(self: *@This(), rows: usize) void { - self._index += self._m.size.width * rows; + pub fn get(self: *const @This(), loc: Point) !T { + if (loc.x >= self._mask.width or loc.y >= self._mask.width) { + return Error.OutOfBounds; + } + + try self._m.get(self._offset.add(loc)); } - pub fn next(self: *@This()) ?[]T { - if (self._endingIndex <= self._index) { - return null; + pub fn set(self: *@This(), loc: Point, val: T) !void { + if (loc.x >= self._mask.width or loc.y >= self._mask.width) { + return Error.OutOfBounds; } - defer { - self._index += self._m.size.width; + try self._m.set(self._offset.add(loc), val); + } + + pub fn row(interface: *Rowable(T), line: usize) ![]T { + var self: *SubMatrix(T) = @fieldParentPtr("rowable", interface); + if (line >= self._mask.height) { + return Error.OutOfBounds; } - return self._m.data[self._index .. self._index + self._width]; + // TODO: This can index out of bound + // we need to checkt to make sure everything is copesedic + var r = try self.rowable.row(line + self._offset.y); + return r[self._offset.x .. self._offset.x + self._mask.width]; } }; } @@ -182,12 +228,8 @@ test "SubMatrix" { var m = try Matrix(u8).initZero(testing.allocator, .{ .height = 10, .width = 10 }); defer m.deinit(testing.allocator); var sub = m.submatrix(.{ .x = 4, .y = 4 }, .{ .height = 5, .width = 5 }); - - // set the value - if (sub.next()) |row| { - row[0] = 'a'; - } + try sub.set(.{ .x = 0, .y = 0 }, 'a'); // we should see the value updated in the Matrix - try testing.expectEqual('a', m.get(.{ .x = 4, .y = 4 })); + try testing.expectEqual('a', try m.get(.{ .x = 4, .y = 4 })); } diff --git a/src/view/draw.zig b/src/view/draw.zig index 5769ad3..d85bd85 100644 --- a/src/view/draw.zig +++ b/src/view/draw.zig @@ -22,6 +22,8 @@ pub const DoubleBuffer = struct { pub fn init(gpa: Allocator, size: Rect) !DoubleBuffer { const display = try gpa.alloc(Cell, size.area()); + errdefer gpa.free(display); + const write = try gpa.alloc(Cell, size.area()); return .{