diff --git a/texttests/config.gr b/texttests/config.gr index 00b0d73a..b7166cd0 100755 --- a/texttests/config.gr +++ b/texttests/config.gr @@ -14,6 +14,9 @@ diff_program:meld # Settings for the c (cmocka) version #executable:${TEXTTEST_HOME}/c_cmocka/cmake-build-debug/main +# Settings for the zig version +#executable:${TEXTTEST_HOME}/zig/zig-out/bin/zig + # Settings for the Java version using Gradle wrapped in a python script #executable:${TEXTTEST_HOME}/Java/texttest_rig.py #interpreter:python diff --git a/zig/.gitignore b/zig/.gitignore new file mode 100644 index 00000000..7dc0ebe5 --- /dev/null +++ b/zig/.gitignore @@ -0,0 +1,13 @@ +# Created by https://www.toptal.com/developers/gitignore/api/zig +# Edit at https://www.toptal.com/developers/gitignore?templates=zig + +### zig ### +# Zig programming language + +zig-cache/ +zig-out/ +build/ +build-*/ +docgen_tmp/ + +# End of https://www.toptal.com/developers/gitignore/api/zig diff --git a/zig/README.md b/zig/README.md new file mode 100644 index 00000000..f0e22897 --- /dev/null +++ b/zig/README.md @@ -0,0 +1,34 @@ +# Gilded Rose starting position in Zig + +I assume you have [installed](https://ziglang.org/learn/getting-started/#installing-zig) `zig` on your system. + +## Run unit tests from the command line + +```sh +$ zig build test +``` + +## Run the TextTest fixture on the command line + +Build and install the executable + +```sh +$ zig build +``` + +Execute it on the command line with an argument for the number of days: + +```sh +$ ./zig/zig-out/bin/zig 10 +``` + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. +You will need to specify the executable in [config.gr](../texttests/config.gr). +Uncomment this line to use it: + +``` +#executable:${TEXTTEST_HOME}/zig/zig-out/bin/zig +``` + diff --git a/zig/build.zig b/zig/build.zig new file mode 100644 index 00000000..21ecdf84 --- /dev/null +++ b/zig/build.zig @@ -0,0 +1,68 @@ +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 exe = b.addExecutable(.{ + .name = "zig", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + // 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); + + // Creates a step for unit testing. This only builds the test executable + // but does not run it. + const lib_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/gilded_rose.zig"), + .target = target, + .optimize = optimize, + }); + + const run_lib_unit_tests = b.addRunArtifact(lib_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_lib_unit_tests.step); +} diff --git a/zig/src/gilded_rose.zig b/zig/src/gilded_rose.zig new file mode 100644 index 00000000..297aeab6 --- /dev/null +++ b/zig/src/gilded_rose.zig @@ -0,0 +1,79 @@ +const std = @import("std"); + +pub const Item = struct { + name: []const u8, + sell_in: i32, + quality: i32, + + pub fn init(name: []const u8, sell_in: i32, quality: i32) Item { + return Item{ + .name = name, + .sell_in = sell_in, + .quality = quality, + }; + } +}; + +pub const GildedRose = struct { + items: []Item, + + pub fn init(items: []Item) GildedRose { + return GildedRose{ .items = items }; + } + + pub fn updateQuality(self: *GildedRose) []Item { + for (0..self.items.len) |i| { + if (!std.mem.eql(u8, self.items[i].name, "Aged Brie") and !std.mem.eql(u8, self.items[i].name, "Backstage passes to a TAFKAL80ETC concert")) { + if (self.items[i].quality > 0) { + if (!std.mem.eql(u8, self.items[i].name, "Sulfuras, Hand of Ragnaros")) { + self.items[i].quality = self.items[i].quality - 1; + } + } + } else { + if (self.items[i].quality < 50) { + self.items[i].quality = self.items[i].quality + 1; + if (std.mem.eql(u8, self.items[i].name, "Backstage passes to a TAFKAL80ETC concert")) { + if (self.items[i].sell_in < 11) { + if (self.items[i].quality < 50) { + self.items[i].quality = self.items[i].quality + 1; + } + } + if (self.items[i].sell_in < 6) { + if (self.items[i].quality < 50) { + self.items[i].quality = self.items[i].quality + 1; + } + } + } + } + } + if (!std.mem.eql(u8, self.items[i].name, "Sulfuras, Hand of Ragnaros")) { + self.items[i].sell_in = self.items[i].sell_in - 1; + } + if (self.items[i].sell_in < 0) { + if (!std.mem.eql(u8, self.items[i].name, "Aged Brie")) { + if (!std.mem.eql(u8, self.items[i].name, "Backstage passes to a TAFKAL80ETC concert")) { + if (self.items[i].quality > 0) { + if (!std.mem.eql(u8, self.items[i].name, "Sulfuras, Hand of Ragnaros")) { + self.items[i].quality = self.items[i].quality - 1; + } + } + } else { + self.items[i].quality = self.items[i].quality - self.items[i].quality; + } + } else { + if (self.items[i].quality < 50) { + self.items[i].quality = self.items[i].quality + 1; + } + } + } + } + return self.items; + } +}; + +test "updateQuality" { + var items = [_]Item{Item.init("foo", 0, 0)}; + var app = GildedRose.init(&items); + _ = app.updateQuality(); + try std.testing.expectEqualStrings("fixme", items[0].name); +} diff --git a/zig/src/main.zig b/zig/src/main.zig new file mode 100644 index 00000000..4d99ea22 --- /dev/null +++ b/zig/src/main.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const gilded_rose = @import("gilded_rose.zig"); +const Item = gilded_rose.Item; +const GildedRose = gilded_rose.GildedRose; + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + const args = try std.process.argsAlloc(allocator); + var days: u8 = 2; + if (args.len >= 2) { + days = try std.fmt.parseInt(u8, args[1], 10); + } + + var items = [_]Item{ + Item.init("+5 Dexterity Vest", 10, 20), + Item.init("Aged Brie", 2, 0), + Item.init("Elixir of the Mongoose", 5, 7), + Item.init("Sulfuras, Hand of Ragnaros", 0, 80), + Item.init("Sulfuras, Hand of Ragnaros", -1, 80), + Item.init("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item.init("Backstage passes to a TAFKAL80ETC concert", 10, 49), + Item.init("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this Conjured item doesn't yet work properly + Item.init("Conjured Mana Cake", 3, 6), + }; + var app = GildedRose.init(&items); + + const stdout = std.io.getStdOut().writer(); + try stdout.print("OMGHAI!\n", .{}); + + for (0..days + 1) |day| { + try stdout.print("-------- day {d} --------\n", .{day}); + try stdout.print("name, sellIn, quality\n", .{}); + for (items) |item| { + try stdout.print("{s}, {d}, {d}\n", .{ item.name, item.sell_in, item.quality }); + } + try stdout.print("\n", .{}); + _ = app.updateQuality(); + } +}