# Files changed · Pull request \#1 · versecafe/asteroids

[View on GitCafe](https://git.cafe/versecafe/asteroids/pull/1/diffs)

Repository: [versecafe/asteroids](https://git.cafe/versecafe/asteroids)

Visibility: public

[Upgrade the game from Zig 0.14 to Zig 0.16\.](https://git.cafe/versecafe/asteroids/pull/1.md)

Version: 1

Head commit: f81655efc6f438a61134392880bd48f424b6ffeb

Base commit: 3fbf37e5c9cc85147559645bc733471ac8242f8d

Comparison base: 3fbf37e5c9cc85147559645bc733471ac8242f8d

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## \.gitignore

Status: modified; +1; −0

```diff
@@ -1,5 +1,6 @@
 .zig-cache
 zig-out
+zig-pkg
 
 # macos
 .DS_Store
```

## build.zig

Status: modified; +28; −49

```diff
@@ -1,84 +1,63 @@
 const std = @import("std");
 
-// Although this function looks imperative, note that its job is to
-// declaratively construct a build graph that will be executed by an external
-// runner.
 pub fn build(b: *std.Build) void {
-    // Standard target options allows the person running `zig build` to choose
-    // what target to build for. Here we do not override the defaults, which
-    // means any target is allowed, and the default is native. Other options
-    // for restricting supported target set are available.
     const target = b.standardTargetOptions(.{});
-
-    // Standard optimization options allow the person running `zig build` to select
-    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
-    // set a preferred release mode, allowing the user to decide how to optimize.
     const optimize = b.standardOptimizeOption(.{});
 
+    const raylib_dep = b.dependency("raylib_zig", .{
-    const exe = b.addExecutable(.{
-        .name = "asteroids",
-        .root_source_file = b.path("src/main.zig"),
         .target = target,
         .optimize = optimize,
     });
 
+    const raylib = raylib_dep.module("raylib");
+    const raygui = raylib_dep.module("raygui");
+    const raylib_artifact = raylib_dep.artifact("raylib");
+
+    const exe_mod = b.createModule(.{
-    // Raylib setup
+        .root_source_file = b.path("src/main.zig"),
-    const raylib_dep = b.dependency("raylib_zig", .{
         .target = target,
         .optimize = optimize,
+        .imports = &.{
+            .{ .name = "raylib", .module = raylib },
+            .{ .name = "raygui", .module = raygui },
+        },
     });
+    exe_mod.linkLibrary(raylib_artifact);
 
+    const exe = b.addExecutable(.{
+        .name = "asteroids",
+        .root_module = exe_mod,
+        // LLD treats system .so files packed into libraylib.a as a hard error
+        // on Linux release links (raylib-zig#219).
+        .use_lld = if (target.result.os.tag == .linux) false else null,
+    });
-    const raylib = raylib_dep.module("raylib"); // main raylib module
-    const raygui = raylib_dep.module("raygui"); // raygui module
-    const raylib_artifact = raylib_dep.artifact("raylib"); // raylib C library
-
-    exe.linkLibrary(raylib_artifact); // link the raylib library
-    exe.root_module.addImport("raylib", raylib);
-    exe.root_module.addImport("raygui", raygui);
-
-    // This declares intent for the executable to be installed into the
-    // standard location when the user invokes the "install" step (the default
-    // step when running `zig build`).
     b.installArtifact(exe);
 
-    // This *creates* a Run step in the build graph, to be executed when another
-    // step is evaluated that depends on it. The next line below will establish
-    // such a dependency.
     const run_cmd = b.addRunArtifact(exe);
-
-    // By making the run step depend on the install step, it will be run from the
-    // installation directory rather than directly from within the cache directory.
-    // This is not necessary, however, if the application depends on other installed
-    // files, this ensures they will be present and in the expected location.
     run_cmd.step.dependOn(b.getInstallStep());
-
-    // This allows the user to pass arguments to the application in the build
-    // command itself, like this: `zig build run -- arg1 arg2 etc`
     if (b.args) |args| {
         run_cmd.addArgs(args);
     }
 
-    // This creates a build step. It will be visible in the `zig build --help` menu,
-    // and can be selected like this: `zig build run`
-    // This will evaluate the `run` step rather than the default, which is "install".
     const run_step = b.step("run", "Run the app");
     run_step.dependOn(&run_cmd.step);
 
+    const test_mod = b.createModule(.{
-    const exe_unit_tests = b.addTest(.{
         .root_source_file = b.path("src/main.zig"),
         .target = target,
         .optimize = optimize,
+        .imports = &.{
+            .{ .name = "raylib", .module = raylib },
+            .{ .name = "raygui", .module = raygui },
+        },
     });
+    test_mod.linkLibrary(raylib_artifact);
 
+    const exe_unit_tests = b.addTest(.{
+        .root_module = test_mod,
+    });
-    exe_unit_tests.linkLibrary(raylib_artifact); // link the raylib library
-    exe_unit_tests.root_module.addImport("raylib", raylib);
-    exe_unit_tests.root_module.addImport("raygui", raygui);
-
     const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
 
-    // Similar to creating the run step earlier, this exposes a `test` step to
-    // the `zig build --help` menu, providing a way for the user to request
-    // running the unit tests.
     const test_step = b.step("test", "Run unit tests");
     test_step.dependOn(&run_exe_unit_tests.step);
 }
```

## build.zig.zon

Status: modified; +3; −3

```diff
@@ -2,12 +2,12 @@
     .name = .asteroids,
     .fingerprint = 0xa01c579bf06b97ed,
     .version = "0.0.0",
-    .minimum_zig_version = "0.14.0",
+    .minimum_zig_version = "0.16.0",
 
     .dependencies = .{
         .raylib_zig = .{
-            .url = "https://github.com/Not-Nik/raylib-zig/archive/devel.tar.gz",
+            .url = "git+https://github.com/raylib-zig/raylib-zig?ref=devel#8758f4cd3a5ae0df6941c4715257223d34c5ad3e",
+            .hash = "raylib_zig-6.0.0-KE8RECZ8BQDm-txuospkpZHbJ6DNpacUF7D88RWQ_qAe",
-            .hash = "raylib_zig-5.6.0-dev-KE8REGMrBQCs5X69dptNzjw9Z7MYM1fgdaKrnuKf8zyr",
         },
     },
     .paths = .{
```

## readme.md

Status: modified; +1; −1

```diff
@@ -4,7 +4,7 @@
 
 ## Building
 
-To build the game you need to have zig installed. You can get it from
+To build the game you need Zig 0.16. You can get it from
 https://ziglang.org/download/
 
 Once you have zig installed you can build the game by running:
```

## src/constants.zig

Status: modified; +6; −8

```diff
@@ -1,5 +1,4 @@
 const rl = @import("raylib");
-const rlm = rl.math;
 const std = @import("std");
 
 // these are mutable just to allow config.txt to override them
@@ -16,16 +15,15 @@
 pub var MAX_ASTEROIDS: u32 = 15;
 pub var SEED: u64 = 0; // if undefined uses timstamp bitcast
 
-pub fn parseConfig() !void {
+pub fn parseConfig(io: std.Io) !void {
+    SEED = @bitCast(std.Io.Timestamp.now(io, .real).toSeconds());
-    SEED = @bitCast(std.time.timestamp()); // set default seed based off timestamp
-    var file = try std.fs.cwd().openFile("config.txt", .{});
-    defer file.close();
 
-    var buf_reader = std.io.bufferedReader(file.reader());
-    var in_stream = buf_reader.reader();
+    const file = try std.Io.Dir.cwd().openFile(io, "config.txt", .{});
+    defer file.close(io);
 
     var buf: [1024]u8 = undefined;
+    var reader = file.reader(io, &buf);
+    while (try reader.interface.takeDelimiter('\n')) |line| {
-    while (in_stream.readUntilDelimiterOrEof(&buf, '\n') catch "") |line| {
         var parts = std.mem.splitAny(u8, line, "=");
         if (parts.next()) |key| {
             if (parts.next()) |value| {
```

## src/logic.zig

Status: modified; +25; −27

```diff
@@ -13,6 +13,7 @@
     for (0..5) |_| {
         const angle = std.math.tau * state.random.float(f32);
         try state.particles.append(
+            state.allocator,
             .{ .position = rlm.vector2Add(
                 position,
                 rl.Vector2.init(
@@ -40,23 +41,21 @@
     if (state.ship.last_shot < state.now - 0.25) {
         state.ship.last_shot = state.now;
         const angle = state.ship.rotation;
-        try state.projectiles.append(
-            .{
+        try state.projectiles.append(state.allocator, .{
-                .position = state.ship.position,
-                .velocity = rlm.vector2Add(
-                    state.ship.velocity,
-                    rlm.vector2Scale(
-                        rl.Vector2.init(
-                            -std.math.sin(angle),
-                            std.math.cos(angle),
-                        ),
-                        3.0,
+            .position = state.ship.position,
+            .velocity = rlm.vector2Add(
+                state.ship.velocity,
+                rlm.vector2Scale(
+                    rl.Vector2.init(
+                        -std.math.sin(angle),
+                        std.math.cos(angle),
                     ),
+                    3.0,
                 ),
-                .rotation = state.ship.rotation,
-                .ttl = 2.5,
-            },
+            ),
-        );
+            .rotation = state.ship.rotation,
+            .ttl = 2.5,
+        });
     }
 }
 
@@ -68,19 +67,19 @@
             state.random.float(f32) * c.WINDOW_SIZE.x,
             state.random.float(f32) * c.WINDOW_SIZE.y,
         );
+        var points: [16]rl.Vector2 = undefined;
-        var points = try std.BoundedArray(rl.Vector2, 16).init(0);
         const n = state.random.intRangeAtMost(i32, 8, 16);
 
         for (0..@intCast(n)) |index| {
             const radius = 0.9 + (0.35 * state.random.float(f32));
             const point_angle = (@as(f32, @floatFromInt(index)) * (std.math.tau / @as(f32, @floatFromInt(n)))) + (std.math.pi * 0.1 * state.random.float(f32));
+            points[index] = rlm.vector2Scale(
-            try points.append(rlm.vector2Scale(
                 rl.Vector2.init(std.math.cos(point_angle), std.math.sin(point_angle)),
                 radius,
-            ));
+            );
         }
         if (!(rlm.vector2Distance(position, state.ship.position) < (size.size() + (c.SPAWN_RADIUS * c.SCALE)))) {
-            try state.asteroids.append(.{
+            try state.asteroids.append(state.allocator, .{
                 .position = position,
                 .velocity = rlm.vector2Scale(
                     rl.Vector2.init(std.math.cos(angle), std.math.sin(angle)),
@@ -89,6 +88,7 @@
                 .size = size,
                 .health = size.health(),
                 .points = points,
+                .point_count = @intCast(n),
             });
         }
     }
@@ -102,17 +102,15 @@
     state.ship = .{
         .position = rlm.vector2Scale(c.WINDOW_SIZE, 0.5),
     };
-    try state.asteroids.resize(0);
-    try state.particles.resize(0);
+    state.asteroids.clearRetainingCapacity();
+    state.particles.clearRetainingCapacity();
+    state.projectiles.clearRetainingCapacity();
-    try state.projectiles.resize(0);
 
     try initAsteroids(state);
 }
 
+fn updateScoreText(state: *types.State) void {
+    state.score_text = std.fmt.bufPrintSentinel(state.score_buf[0..], "{d}", .{state.score}, 0) catch unreachable;
-pub fn copyIntStr(n: i32) []const u8 {
-    var buffer: [4096]u8 = undefined;
-    const result = std.fmt.bufPrintZ(buffer[0..], "{d}", .{n}) catch unreachable;
-    return @as([]const u8, result);
 }
 
 pub fn update(state: *types.State) !void {
@@ -215,7 +213,7 @@
         try shootProjectile(state);
     }
 
+    updateScoreText(state);
-    state.score_text = copyIntStr(state.score);
 
     if (!state.ship.alive and (state.now - state.ship.death_time) > 2.0) {
         try reset(state);
```

## src/main.zig

Status: modified; +15; −25

```diff
@@ -1,36 +1,31 @@
 const std = @import("std");
 const rl = @import("raylib");
-const rg = @import("raygui");
 const types = @import("types.zig");
 const render = @import("render.zig");
 const logic = @import("logic.zig");
 const c = @import("constants.zig");
 
+pub fn main(init: std.process.Init) !void {
+    try c.parseConfig(init.io);
-pub fn main() !void {
-    try c.parseConfig();
 
     rl.initWindow(@as(i32, @intFromFloat(c.WINDOW_SIZE.x)), @as(i32, @intFromFloat(c.WINDOW_SIZE.y)), "Asteroids!");
+    defer rl.closeWindow();
     rl.setWindowPosition(300, 100);
     rl.setTargetFPS(120);
-
-    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
-    const allocator = gpa.allocator();
-    defer std.debug.assert(gpa.deinit() == .ok);
 
+    const allocator = init.gpa;
     var prng = std.Random.Xoshiro256.init(c.SEED);
 
     var state: types.State = .{
+        .allocator = allocator,
         .random = prng.random(),
         .ship = .{
             .position = rl.math.vector2Scale(c.WINDOW_SIZE, 0.5),
         },
-        .asteroids = std.ArrayList(types.Asteroid).init(allocator),
-        .particles = std.ArrayList(types.Particle).init(allocator),
-        .projectiles = std.ArrayList(types.Projectile).init(allocator),
     };
+    defer state.asteroids.deinit(allocator);
+    defer state.particles.deinit(allocator);
-    defer state.asteroids.deinit();
-    defer state.particles.deinit();
-    defer state.projectiles.deinit();
+    defer state.projectiles.deinit(allocator);
 
     try logic.initAsteroids(&state); // crate the initial asteroids
 
@@ -42,26 +37,21 @@
 
 // test a basic state setup, run a update, and confirm no memory leaks after deinit
 test "init without graphics" {
-    try c.parseConfig();
-
-    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
-    const allocator = gpa.allocator();
-    defer std.debug.assert(gpa.deinit() == .ok);
+    try c.parseConfig(std.testing.io);
 
+    const allocator = std.testing.allocator;
-    var prng = std.Random.Xoshiro256.init(@bitCast(std.time.timestamp())); // seed
+    var prng = std.Random.Xoshiro256.init(@bitCast(std.Io.Timestamp.now(std.testing.io, .real).toSeconds()));
 
     var state: types.State = .{
+        .allocator = allocator,
         .random = prng.random(),
         .ship = .{
             .position = rl.math.vector2Scale(c.WINDOW_SIZE, 0.5),
         },
-        .asteroids = std.ArrayList(types.Asteroid).init(allocator),
-        .particles = std.ArrayList(types.Particle).init(allocator),
-        .projectiles = std.ArrayList(types.Projectile).init(allocator),
     };
-    defer state.asteroids.deinit();
+    defer state.asteroids.deinit(allocator);
+    defer state.particles.deinit(allocator);
+    defer state.projectiles.deinit(allocator);
-    defer state.particles.deinit();
-    defer state.projectiles.deinit();
 
     try logic.initAsteroids(&state);
 
```

## src/render.zig

Status: modified; +2; −60

```diff
@@ -1,15 +1,8 @@
-const std = @import("std");
 const rl = @import("raylib");
 const rlm = rl.math;
-const rg = @import("raygui");
 const types = @import("types.zig");
 const c = @import("constants.zig");
 
-fn screenWrapPosition(p: rl.Vector2) rl.Vector2 {
-    // wrap after origin crosses the screen + 1.5 to allow smooth out of site instead of jumping
-    return rl.Vector2.init(@mod(p.x, c.WINDOW_SIZE.x + (1.5 * c.SCALE)), @mod(p.y, c.WINDOW_SIZE.y + (1.5 * c.SCALE)));
-}
-
 fn drawLines(origin: rl.Vector2, scale: f32, rotation: f32, points: []const rl.Vector2) void {
     const Transformer = struct {
         origin: rl.Vector2,
@@ -37,7 +30,7 @@
 }
 
 fn drawAsteroid(asteroid: types.Asteroid) void {
-    drawLines(asteroid.position, asteroid.size.size(), 0.0, asteroid.points.slice());
+    drawLines(asteroid.position, asteroid.size.size(), 0.0, asteroid.points[0..asteroid.point_count]);
 
     if (c.DEBUG) {
         rl.drawCircleLinesV(asteroid.position, asteroid.size.size(), rl.Color.red);
@@ -102,64 +95,13 @@
     }
 }
 
-fn spawnDeathParticles(position: rl.Vector2, state: *types.State) !void {
-    for (0..5) |_| {
-        const angle = std.math.tau * state.random.float(f32);
-        try state.particles.append(
-            .{ .position = rlm.vctor2Add(
-                position,
-                rl.Vector2.init(
-                    state.random.float(f32) * 2,
-                    state.random.float(f32) * 2,
-                ),
-            ), .velocity = rlm.vector2Scale(
-                rl.Vector2.init(
-                    std.math.cos(angle),
-                    std.math.sin(angle),
-                ),
-                2.0 * std.math.sin(state.random.float(f32)),
-            ), .ttl = 1 + (2 * state.random.float(f32)), .type = .{
-                .LINE = .{
-                    .rotation = std.math.tau * state.random.float(f32),
-                    .length = c.SCALE * (0.5 + (0.5 * state.random.float(f32))),
-                },
-            } },
-        );
-    }
-}
-
-fn shootProjectile(state: *types.State) !void {
-    // throttle to one shot per 0.25 seconds
-    if (state.ship.last_shot < state.now - 0.25) {
-        state.ship.last_shot = state.now;
-        const angle = state.ship.rotation;
-        try state.projectiles.append(
-            .{
-                .position = state.ship.position,
-                .velocity = rlm.vector2Add(
-                    state.ship.velocity,
-                    rlm.vector2Scale(
-                        rl.Vector2.init(
-                            -std.math.sin(angle),
-                            std.math.cos(angle),
-                        ),
-                        3.0,
-                    ),
-                ),
-                .rotation = state.ship.rotation,
-                .ttl = 2.5,
-            },
-        );
-    }
-}
-
 pub fn paint(state: *types.State) !void {
     rl.beginDrawing();
     defer rl.endDrawing();
 
     rl.clearBackground(rl.Color.black);
 
+    rl.drawText(state.score_text, 10, 10, 10, rl.Color.white);
-    rl.drawText(@ptrCast(state.score_text), 10, 10, 10, rl.Color.white);
 
     for (state.asteroids.items) |asteroid| {
         drawAsteroid(asteroid);
```

## src/types.zig

Status: modified; +8; −6

```diff
@@ -1,6 +1,5 @@
 const std = @import("std");
 const rl = @import("raylib");
-const rlm = rl.math;
 const c = @import("constants.zig");
 
 pub const Ship = struct {
@@ -57,7 +56,8 @@
     velocity: rl.Vector2,
     size: AsteroidSize,
     health: i32,
-    points: std.BoundedArray(rl.Vector2, 16),
+    points: [16]rl.Vector2,
+    point_count: usize,
 };
 
 pub const ParticleType = enum {
@@ -88,13 +88,15 @@
 };
 
 pub const State = struct {
+    allocator: std.mem.Allocator,
     now: f32 = 0.0,
     delta: f32 = 0.0,
     random: std.Random,
     ship: Ship,
     score: i32 = 0,
-    score_text: []const u8 = "0",
-    asteroids: std.ArrayList(Asteroid),
+    score_buf: [32]u8 = undefined,
+    score_text: [:0]const u8 = "0",
+    asteroids: std.ArrayList(Asteroid) = .empty,
+    particles: std.ArrayList(Particle) = .empty,
-    particles: std.ArrayList(Particle),
+    projectiles: std.ArrayList(Projectile) = .empty,
-    projectiles: std.ArrayList(Projectile),
 };
```
