aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjjanzen <jjanzen@jjanzen.ca>2025-01-22 14:22:12 -0600
committerjjanzen <jjanzen@jjanzen.ca>2025-01-22 14:22:12 -0600
commit14c2530817f20986b51f611ef637d09f43aac23c (patch)
treecc948750173fd7c0aba570f462b65ad1fe405121
parent8ecefa4b3fd56fc87f822f3dbefa1ce1ef6601d8 (diff)
fix memory leak and implement stub front end
-rw-r--r--src/frontend/ncurses.zig4
-rw-r--r--src/frontend/stub.zig57
-rw-r--r--src/main.zig43
-rw-r--r--src/root.zig2
-rw-r--r--src/run.zig93
5 files changed, 168 insertions, 31 deletions
diff --git a/src/frontend/ncurses.zig b/src/frontend/ncurses.zig
index fd58cf1..d9b99fe 100644
--- a/src/frontend/ncurses.zig
+++ b/src/frontend/ncurses.zig
@@ -239,8 +239,7 @@ pub const IO = struct {
pub fn init(allocator: std.mem.Allocator) !IOInterface {
_ = locale.setlocale(locale.LC_ALL, "");
- const io_ptr = try allocator.alloc(IO, 1);
- var io = &io_ptr[0];
+ var io = try allocator.create(IO);
io.allocator = allocator;
io.inst = null;
io.msgs = null;
@@ -292,5 +291,6 @@ pub const IO = struct {
pub fn deinit(self: *IO) void {
self.deleteWindows();
_ = ncurses.endwin();
+ self.allocator.destroy(self);
}
};
diff --git a/src/frontend/stub.zig b/src/frontend/stub.zig
new file mode 100644
index 0000000..d6e7fc8
--- /dev/null
+++ b/src/frontend/stub.zig
@@ -0,0 +1,57 @@
+const std = @import("std");
+const Action = @import("../actions.zig").Action;
+const IOInterface = @import("io_interface.zig").IOInterface;
+
+pub const ActionStack = struct {
+ stream: std.ArrayList(Action),
+
+ pub fn init(allocator: std.mem.Allocator) ActionStack {
+ return ActionStack{
+ .stream = std.ArrayList(Action).init(allocator),
+ };
+ }
+
+ pub fn deinit(self: *ActionStack) void {
+ self.stream.deinit();
+ }
+
+ pub fn scheduleAction(self: *ActionStack, action: Action) !void {
+ try self.stream.append(action);
+ }
+
+ pub fn next(self: *ActionStack) ?Action {
+ return self.stream.popOrNull();
+ }
+};
+
+pub const IO = struct {
+ allocator: std.mem.Allocator,
+ as: ActionStack,
+ out_as: ActionStack,
+
+ pub fn displayMessage(self: *IO, str: []const u8) anyerror!void {
+ _ = self;
+ std.log.info("{s}\n", .{str});
+ }
+
+ pub fn waitForTick(self: *IO) anyerror!Action {
+ const a = self.as.next() orelse return error.NoAvailableAction;
+
+ try self.out_as.scheduleAction(a);
+
+ return a;
+ }
+
+ pub fn init(allocator: std.mem.Allocator, as: ActionStack) !IOInterface {
+ var io = try allocator.create(IO);
+ io.allocator = allocator;
+ io.as = as;
+ io.out_as = ActionStack.init(allocator);
+ return IOInterface.init(io);
+ }
+
+ pub fn deinit(self: *IO) void {
+ self.out_as.deinit();
+ self.allocator.destroy(self);
+ }
+};
diff --git a/src/main.zig b/src/main.zig
index f55935d..9584038 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,38 +1,23 @@
const std = @import("std");
const IO = @import("frontend/ncurses.zig").IO;
-const Action = @import("actions.zig").Action;
+const run = @import("run.zig");
-fn run(allocator: std.mem.Allocator) !void {
- var io = try IO.init(allocator);
- defer {
- io.deinit();
- }
-
- try io.displayMessage("Initialized");
-
- var action = Action.illegal;
- var tick_count: usize = 0;
- while (action != Action.exit) {
- const str = try std.fmt.allocPrint(allocator, "{}", .{tick_count});
- defer allocator.free(str);
- try io.displayMessage(str);
+pub fn main() u8 {
+ var status: u8 = 0;
- action = try io.waitForTick();
- switch (action) {
- Action.tick => {
- tick_count += 1;
- },
- else => {},
+ var gpa = std.heap.GeneralPurposeAllocator(.{}){};
+ const allocator = gpa.allocator();
+ defer {
+ const deinit_status = gpa.deinit();
+ if (deinit_status == .leak) {
+ status = 1;
}
}
-}
-pub fn main() u8 {
- var gpa = std.heap.GeneralPurposeAllocator(.{}){};
- const allocator = gpa.allocator();
- run(allocator) catch {
- return 1;
- };
+ var io = IO.init(allocator) catch return 1;
+ defer io.deinit();
+
+ run.run(allocator, &io) catch return 1;
- return 0;
+ return status;
}
diff --git a/src/root.zig b/src/root.zig
index 330b6ba..60a4df4 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -3,8 +3,10 @@ const testing = std.testing;
const component = @import("ecs/component.zig");
const ecs = @import("ecs/ecs.zig");
+const run = @import("run.zig");
test {
_ = component;
_ = ecs;
+ _ = run;
}
diff --git a/src/run.zig b/src/run.zig
new file mode 100644
index 0000000..e20d9b5
--- /dev/null
+++ b/src/run.zig
@@ -0,0 +1,93 @@
+const std = @import("std");
+const stub = @import("frontend/stub.zig");
+const IO = @import("frontend/io_interface.zig").IOInterface;
+const Action = @import("actions.zig").Action;
+
+pub fn run(allocator: std.mem.Allocator, io: *IO) !void {
+ try io.displayMessage("Initialized");
+
+ var action = Action.illegal;
+ var tick_count: usize = 0;
+ while (action != Action.exit) {
+ const str = try std.fmt.allocPrint(allocator, "{}", .{tick_count});
+ defer allocator.free(str);
+ try io.displayMessage(str);
+
+ action = try io.waitForTick();
+ switch (action) {
+ Action.tick => {
+ tick_count += 1;
+ },
+ else => {},
+ }
+ }
+}
+
+test "can run game" {
+ const allocator = std.testing.allocator;
+
+ var as = stub.ActionStack.init(allocator);
+ defer as.deinit();
+
+ var io = try stub.IO.init(allocator, as);
+ defer io.deinit();
+
+ var res: ?anyerror = null;
+ run(allocator, &io) catch |err| {
+ res = err;
+ };
+ try std.testing.expect(res != null);
+ try std.testing.expectEqual(error.NoAvailableAction, res.?);
+}
+
+test "game exits on Action.exit" {
+ const allocator = std.testing.allocator;
+
+ var as = stub.ActionStack.init(allocator);
+ defer as.deinit();
+ try as.scheduleAction(Action.exit);
+
+ var io = try stub.IO.init(allocator, as);
+ defer io.deinit();
+
+ try run(allocator, &io);
+ const io_obj: *const stub.IO = @ptrCast(@alignCast(io.ptr));
+ var actions = io_obj.out_as;
+
+ const act = actions.next().?;
+ try std.testing.expectEqual(Action.exit, act);
+}
+
+test "game processes stream of actions" {
+ const allocator = std.testing.allocator;
+
+ var as = stub.ActionStack.init(allocator);
+ defer as.deinit();
+
+ try as.scheduleAction(Action.exit);
+ try as.scheduleAction(Action.move_right);
+ try as.scheduleAction(Action.move_left);
+ try as.scheduleAction(Action.move_down);
+ try as.scheduleAction(Action.move_up);
+ try as.scheduleAction(Action.tick);
+
+ var io = try stub.IO.init(allocator, as);
+ defer io.deinit();
+
+ try run(allocator, &io);
+ const io_obj: *const stub.IO = @ptrCast(@alignCast(io.ptr));
+ var actions = io_obj.out_as;
+
+ var act = actions.next().?;
+ try std.testing.expectEqual(Action.exit, act);
+ act = actions.next().?;
+ try std.testing.expectEqual(Action.move_right, act);
+ act = actions.next().?;
+ try std.testing.expectEqual(Action.move_left, act);
+ act = actions.next().?;
+ try std.testing.expectEqual(Action.move_down, act);
+ act = actions.next().?;
+ try std.testing.expectEqual(Action.move_up, act);
+ act = actions.next().?;
+ try std.testing.expectEqual(Action.tick, act);
+}