From 1891f99502c12197b60e129d02554445481aa014 Mon Sep 17 00:00:00 2001 From: mmsaki Date: Mon, 8 Jun 2026 05:55:17 +0200 Subject: [PATCH 01/22] fix typo --- exercises/114_packed.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/114_packed.zig b/exercises/114_packed.zig index 933ae0a..deb91e4 100644 --- a/exercises/114_packed.zig +++ b/exercises/114_packed.zig @@ -78,7 +78,7 @@ const FLG = packed struct(u8) { content_checksum: bool, content_size: bool, block_checksum: bool, - block_indepencence: bool, + block_independence: bool, version: u2, }; From d39241a20e3e100d3cc467e86854da9bfdd56e47 Mon Sep 17 00:00:00 2001 From: xaviii Date: Tue, 9 Jun 2026 08:54:40 +0200 Subject: [PATCH 02/22] Fixed typo: "you will wish" -> "you wish" --- exercises/043_pointers5.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/043_pointers5.zig b/exercises/043_pointers5.zig index 9e2fa6f..3117639 100644 --- a/exercises/043_pointers5.zig +++ b/exercises/043_pointers5.zig @@ -1,6 +1,6 @@ // // As with integers, you can pass a pointer to a struct when you -// will wish to modify that struct. Pointers are also useful when +// wish to modify that struct. Pointers are also useful when // you need to store a reference to a struct (a "link" to it). // // const Vertex = struct{ x: u32, y: u32, z: u32 }; From bc443a1113b68a668a6a024ed4b1e7eb2d7c0d80 Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Thu, 11 Jun 2026 09:58:17 -0400 Subject: [PATCH 03/22] Fixed a couple of exercise references as they are no longer accurate after the recent changes - Exercise 107 claimed the async exercises are 84-91, they are actually 85-94 and quiz 95 - Exercise 113 claimed the bit manip exercise is 097, it is now 100 - Exercise 114 has the bit manipulation exercises as 097 and 098, they are now 100 and 101 - Exercise 114 has the last switch exercise as being 108, it is now 111. --- exercises/107_threading.zig | 2 +- exercises/113_quiz9.zig | 2 +- exercises/114_packed.zig | 2 +- exercises/115_packed2.zig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/107_threading.zig b/exercises/107_threading.zig index 3c3fa21..c24757f 100644 --- a/exercises/107_threading.zig +++ b/exercises/107_threading.zig @@ -1,5 +1,5 @@ // -// In Exercises 84-91, we learned about Zig's Io interface for +// In Exercises 85-94, we learned about Zig's Io interface for // concurrent execution: io.async(), Group, Select, and Futures. // Under the hood, the Threaded backend manages a pool of real // OS threads for you - including scheduling, cancellation, and diff --git a/exercises/113_quiz9.zig b/exercises/113_quiz9.zig index 8f5cb61..8d1da9c 100644 --- a/exercises/113_quiz9.zig +++ b/exercises/113_quiz9.zig @@ -14,7 +14,7 @@ // // A common activity in microcontroller programming is setting and clearing // bits on input and output pins. This lets you control LEDs, sensors, motors -// and more! In a previous exercise (097_bit_manipulation.zig) you learned how +// and more! In a previous exercise (100_bit_manipulation.zig) you learned how // to swap two bytes using the ^ (XOR - exclusive or) operator. This quiz will // test your knowledge of bit manipulation in Zig while giving you a taste of // what it's like to control registers in a real microcontroller. Included at diff --git a/exercises/114_packed.zig b/exercises/114_packed.zig index 933ae0a..3f605a2 100644 --- a/exercises/114_packed.zig +++ b/exercises/114_packed.zig @@ -1,6 +1,6 @@ // // We've already learned plenty about bit manipulation using bitwise operations -// in exercises 097 and 098 and in quiz 110. The techniques we already know work +// in exercises 100 and 101 and in quiz 113. The techniques we already know work // just fine, but creating masks and shifting individual bits around can become // quite tedious and unwieldy pretty quickly. // What if there was a better, a more convenient way to control individual bits? diff --git a/exercises/115_packed2.zig b/exercises/115_packed2.zig index 2b6b558..59d31e1 100644 --- a/exercises/115_packed2.zig +++ b/exercises/115_packed2.zig @@ -1,5 +1,5 @@ // -// We've already learned about switch statements in exercises 030, 031 and 108. +// We've already learned about switch statements in exercises 030, 031 and 111. // They also work with packed containers: const S = packed struct(u2) { From c24a170ca853063f6c3ebdae69dc27d957740263 Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Thu, 11 Jun 2026 09:59:17 -0400 Subject: [PATCH 04/22] missed a change to add the phrase 'and quiz 95' to the statement regarding Zig's Io interface. --- exercises/107_threading.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/107_threading.zig b/exercises/107_threading.zig index c24757f..2b7d4c0 100644 --- a/exercises/107_threading.zig +++ b/exercises/107_threading.zig @@ -1,5 +1,5 @@ // -// In Exercises 85-94, we learned about Zig's Io interface for +// In Exercises 85-94 and quiz 95, we learned about Zig's Io interface for // concurrent execution: io.async(), Group, Select, and Futures. // Under the hood, the Threaded backend manages a pool of real // OS threads for you - including scheduling, cancellation, and From 90f847e8b914dcd51279e8c7cc4099a4bdfd0a16 Mon Sep 17 00:00:00 2001 From: Tariq Ibrahim Date: Sat, 13 Jun 2026 17:27:28 -0700 Subject: [PATCH 05/22] fix typos in CONTRIBUTING.md Signed-off-by: Tariq Ibrahim --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d162c2..56446b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,11 +8,11 @@ of Ziglings Maintenance! Ziglings is intended for programmers of all experience levels. No specific language knowledge is expected. Anyone who can install -the current Zig snapshot, setup a copy of Ziglings, and knows +the current Zig snapshot, set up a copy of Ziglings, and knows common language building blocks (if/then/else, loops, and functions) is ready for Ziglings. -Zigling's excercises are self-contained. If you can't solve +Zigling's exercises are self-contained. If you can't solve an exercise from the information you've gleaned so far from Ziglings, then the exercise probably needs some additional work. Please file an issue! From 2cf2a885fd564f6d7a8ca7c506b27288951bda9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Kubi=C5=A1?= Date: Sat, 20 Jun 2026 11:18:05 +0200 Subject: [PATCH 06/22] Use significand terminology As exercise 60 says: Donald Knuth would not be happy with us calling this mantissa. Proposing to use the less amiguous term significand --- exercises/115_packed2.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/115_packed2.zig b/exercises/115_packed2.zig index 2b6b558..bd25e20 100644 --- a/exercises/115_packed2.zig +++ b/exercises/115_packed2.zig @@ -55,7 +55,7 @@ comptime { const Float = packed union(u16) { value: f16, bits: packed struct(u16) { - mantissa: u10, + significand: u10, exponent: u5, sign: u1, }, From dce9c803bfbe4c8fd2add562bbbfd8e16df3dde9 Mon Sep 17 00:00:00 2001 From: Gabriel Sa Date: Sat, 20 Jun 2026 19:09:14 -0300 Subject: [PATCH 07/22] fix style mismatch on 065_builtins2.zig Rename `fetchTheMostBeautifulType` to `FetchTheMostBeautifulType` to follow Zig's standard naming practice. --- exercises/065_builtins2.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/065_builtins2.zig b/exercises/065_builtins2.zig index 78cd23b..7bb3ac6 100644 --- a/exercises/065_builtins2.zig +++ b/exercises/065_builtins2.zig @@ -47,7 +47,7 @@ const Narcissus = struct { myself: *Narcissus = undefined, echo: void = undefined, // Alas, poor Echo! - fn fetchTheMostBeautifulType() type { + fn FetchTheMostBeautifulType() type { return @This(); } }; @@ -70,7 +70,7 @@ pub fn main() void { // // The fix for this is very subtle, but it makes a big // difference! - const Type2 = narcissus.fetchTheMostBeautifulType(); + const Type2 = narcissus.FetchTheMostBeautifulType(); // Now we print a pithy statement about Narcissus. print("A {s} loves all {s}es. ", .{ From 1ad3534e4d276dfddefc80fcd9a2f1e6c74266d1 Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Sun, 21 Jun 2026 15:31:18 +0200 Subject: [PATCH 08/22] fixed grammar --- exercises/108_threading2.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/108_threading2.zig b/exercises/108_threading2.zig index bbf8d78..e1b35ac 100644 --- a/exercises/108_threading2.zig +++ b/exercises/108_threading2.zig @@ -46,7 +46,7 @@ // 1,000,000,000 partial values. And for each additional digit we have to // add a zero. // Even fast computers - and I mean really fast computers - get a bit warmer -// on the CPU when it comes to really many digits. But the 8 digits are +// on the CPU when it comes to a large number of digits. But 8 digits are // enough for us for now, because we want to understand the principle and // nothing more, right? // From f3df08d8e05dc0543137ae9866b389df178b97c6 Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Sun, 21 Jun 2026 15:43:28 +0200 Subject: [PATCH 09/22] fixed description --- exercises/115_packed2.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/115_packed2.zig b/exercises/115_packed2.zig index db5190a..2a772d7 100644 --- a/exercises/115_packed2.zig +++ b/exercises/115_packed2.zig @@ -51,7 +51,7 @@ comptime { // // Try to make the float below negative: -/// IEEE 754 half precision float +// IEEE 754 binary16 floating-point format const Float = packed union(u16) { value: f16, bits: packed struct(u16) { From cbcb570248a5297a0644fc22357bf81aac54545a Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Sun, 21 Jun 2026 16:16:31 +0200 Subject: [PATCH 10/22] expanded explanation about self vs *self --- exercises/047_methods.zig | 18 ++++++++++++++++++ patches/patches/047_methods.patch | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/exercises/047_methods.zig b/exercises/047_methods.zig index 3221ca2..3e88719 100644 --- a/exercises/047_methods.zig +++ b/exercises/047_methods.zig @@ -37,6 +37,24 @@ // self, others use a lowercase version of the type name, but feel // free to use whatever is most appropriate. // +// "But hold on," you say, eyeing a() and b() suspiciously, "why +// does one take 'self' and another take '*self'?" Sharp eye! +// +// It all comes down to a single question: does the function need +// to CHANGE the struct? +// +// * Needs to change it? Take a pointer (*Bar). Without it you'd +// be scribbling on a COPY, and your changes would evaporate the +// instant the function returns. Poof. +// * Only reads it? Plain Bar is just fine. +// (For a big, bulky struct you might still write *const Bar to +// avoid copying it around, but for small ones a copy is cheap.) +// +// You'll see this below: zap() takes 'self: HeatRay' by value +// because it only reads the ray's damage, but it takes the alien +// as '*Alien' because zapping is supposed to HURT - and that means +// changing the alien's health for real, not on a throwaway copy. +// // Okay, you're armed. // // Now, please zap the alien structs until they're all gone or diff --git a/patches/patches/047_methods.patch b/patches/patches/047_methods.patch index a7e8a26..4755ecb 100644 --- a/patches/patches/047_methods.patch +++ b/patches/patches/047_methods.patch @@ -1,6 +1,6 @@ ---- exercises/047_methods.zig 2023-10-03 22:15:22.122241138 +0200 -+++ answers/047_methods.zig 2023-10-05 20:04:07.056100214 +0200 -@@ -88,7 +88,7 @@ +--- exercises/047_methods.zig 2026-06-21 16:11:59.110876971 +0200 ++++ answers/047_methods.zig 2026-06-21 16:13:07.066363558 +0200 +@@ -106,7 +106,7 @@ for (&aliens) |*alien| { // *** Zap the alien with the heat ray here! *** From 935d26828036da962f5c22738d86e8530a3819a2 Mon Sep 17 00:00:00 2001 From: Gabriel Sa Date: Sun, 21 Jun 2026 11:58:29 -0300 Subject: [PATCH 11/22] fix style mismatch on patches/065_builtins2.zig Rename `fetchTheMostBeautifulType` to `FetchTheMostBeautifulType` to follow Zig's standard naming practice. --- patches/patches/065_builtins2.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/patches/065_builtins2.patch b/patches/patches/065_builtins2.patch index c011646..2abbc6f 100644 --- a/patches/patches/065_builtins2.patch +++ b/patches/patches/065_builtins2.patch @@ -13,8 +13,8 @@ // // The fix for this is very subtle, but it makes a big // difference! -- const Type2 = narcissus.fetchTheMostBeautifulType(); -+ const Type2 = Narcissus.fetchTheMostBeautifulType(); +- const Type2 = narcissus.FetchTheMostBeautifulType(); ++ const Type2 = Narcissus.FetchTheMostBeautifulType(); // Now we print a pithy statement about Narcissus. print("A {s} loves all {s}es. ", .{ From 11868112c54dc95023b2012bb040bd9f2de031e4 Mon Sep 17 00:00:00 2001 From: xiaolizhi Date: Sun, 21 Jun 2026 21:22:12 +0200 Subject: [PATCH 12/22] 107: change comment "brackets" -> "braces" Signed-off-by: xiaolizhi --- exercises/107_threading.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/107_threading.zig b/exercises/107_threading.zig index 2b7d4c0..5072648 100644 --- a/exercises/107_threading.zig +++ b/exercises/107_threading.zig @@ -74,9 +74,9 @@ pub fn main() !void { // before the parallel processing begins. std.debug.print("Starting work...\n", .{}); - // These curly brackets are very important, they are necessary + // These curly braces are very important, they are necessary // to enclose the area where the threads are called. - // Without these brackets, the program would not wait for the + // Without these braces, the program would not wait for the // end of the threads and they would continue to run beyond the // end of the program. { From e928d2c7b13fa194dc9b1d2875a24e79f5822597 Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Mon, 22 Jun 2026 17:45:35 +0200 Subject: [PATCH 13/22] skipped #5 until a better solution is found --- rivendell/elrond.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rivendell/elrond.zig b/rivendell/elrond.zig index c49d0ab..d7a0e1a 100644 --- a/rivendell/elrond.zig +++ b/rivendell/elrond.zig @@ -576,6 +576,8 @@ const exercises = [_]Exercise{ .main_file = "005_arrays2.zig", .output = "LEET: 1337, Bits: 100110011001", .hint = "Fill in the two arrays.", + .skip = true, + .skip_hint = "Better solution is needed.", }, .{ .main_file = "006_strings.zig", From f6514ebb13d842da721084f8d4dc2fdb224c7d86 Mon Sep 17 00:00:00 2001 From: omihirofumi <99390907+omihirofumi@users.noreply.github.com> Date: Thu, 25 Jun 2026 12:23:02 +0900 Subject: [PATCH 14/22] fix comment `field_types` is a slice of (strings -> types) --- exercises/065_builtins2.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/065_builtins2.zig b/exercises/065_builtins2.zig index 7bb3ac6..9d137ef 100644 --- a/exercises/065_builtins2.zig +++ b/exercises/065_builtins2.zig @@ -94,7 +94,7 @@ pub fn main() void { print("He has room in his heart for:", .{}); // `field_names` is a slice of strings and it holds the names of the struct's fields - // `field_types` is a slice of strings and it holds the types of the struct's fields, + // `field_types` is a slice of types and it holds the types of the struct's fields, // it is guaranteed to be the same length as `field_names` const field_names = @typeInfo(Narcissus).@"struct".field_names; const field_types = @typeInfo(Narcissus).@"struct".field_types; From fe465a466e436c9362449f8ffe2701970b8cac39 Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Fri, 26 Jun 2026 12:29:25 +0200 Subject: [PATCH 15/22] added absolute path to exercises --- build.zig | 1 + rivendell/elrond.zig | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index afc1021..1dfc7be 100644 --- a/build.zig +++ b/build.zig @@ -81,6 +81,7 @@ pub fn build(b: *Build) !void { const run = b.addRunArtifact(elrond); run.addArg(b.fmt("--zig={s}", .{b.graph.zig_exe})); run.addArg(b.fmt("--work-path={s}", .{work_path})); + run.addArg(b.fmt("--root-path={s}", .{b.root.root_dir.path.?})); if (exno) |n| { run.addArg(b.fmt("--only={d}", .{n})); diff --git a/rivendell/elrond.zig b/rivendell/elrond.zig index d7a0e1a..fa0ed22 100644 --- a/rivendell/elrond.zig +++ b/rivendell/elrond.zig @@ -109,6 +109,7 @@ const Context = struct { arena: std.mem.Allocator, zig_exe: []const u8, work_path: []const u8, + root_path: []const u8, }; const Error = error{Failed}; @@ -158,6 +159,7 @@ pub fn main(init: std.process.Init) !void { var zig_exe: []const u8 = "zig"; var work_path: []const u8 = "exercises"; + var root_path: []const u8 = ""; var mode: Mode = .normal; var only_n: ?usize = null; var start_n: ?usize = null; @@ -170,6 +172,8 @@ pub fn main(init: std.process.Init) !void { zig_exe = v; } else if (cutPrefix(u8, arg, "--work-path=")) |v| { work_path = v; + } else if (cutPrefix(u8, arg, "--root-path=")) |v| { + root_path = v; } else if (cutPrefix(u8, arg, "--only=")) |v| { only_n = std.fmt.parseInt(usize, v, 10) catch { print("invalid --only value: {s}\n", .{v}); @@ -197,6 +201,7 @@ pub fn main(init: std.process.Init) !void { .arena = arena, .zig_exe = zig_exe, .work_path = work_path, + .root_path = root_path, }; switch (mode) { @@ -310,7 +315,7 @@ fn runExe(ctx: Context, ex: Exercise) !void { const arena = ctx.arena; print("Compiling {s}...\n", .{ex.main_file}); - const path = std.fs.path.join(arena, &.{ ctx.work_path, ex.main_file }) catch @panic("OOM"); + const path = std.fs.path.join(arena, &.{ ctx.root_path, ctx.work_path, ex.main_file }) catch @panic("OOM"); var argv = std.ArrayList([]const u8).initCapacity(arena, 8) catch @panic("OOM"); argv.append(arena, ctx.zig_exe) catch @panic("OOM"); From fdf47e3b8c0b5b248cc1fbc071b3262f5cfa816a Mon Sep 17 00:00:00 2001 From: it3x Date: Fri, 26 Jun 2026 12:05:46 +0000 Subject: [PATCH 16/22] Fix misleading comments See #450 --- exercises/107_threading.zig | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/exercises/107_threading.zig b/exercises/107_threading.zig index 5072648..aaec6b1 100644 --- a/exercises/107_threading.zig +++ b/exercises/107_threading.zig @@ -75,10 +75,10 @@ pub fn main() !void { std.debug.print("Starting work...\n", .{}); // These curly braces are very important, they are necessary - // to enclose the area where the threads are called. - // Without these braces, the program would not wait for the - // end of the threads and they would continue to run beyond the - // end of the program. + // to enclose the area where the threads are called and joined. + // With these braces, the program will block and wait for all threads + // to finish right at the closing brace of this block, ensuring + // "Zig is cool!" is always printed last. { // Now we start the first thread, with the number as parameter const handle = try std.Thread.spawn(.{}, thread_function, .{1}); @@ -102,8 +102,7 @@ pub fn main() !void { try io.sleep(std.Io.Duration.fromMilliseconds(400), .awake); std.debug.print("Some weird stuff, after starting the threads.\n", .{}); } - // After we have left the closed area, we wait until - // the threads have run through, if this has not yet been the case. + // The threads are guaranteed to be finished by the time we reach here. std.debug.print("Zig is cool!\n", .{}); } From 4f240aaefac851dcf542b2402704c94ce57a898e Mon Sep 17 00:00:00 2001 From: Aster Echols Date: Fri, 26 Jun 2026 23:27:20 -0400 Subject: [PATCH 17/22] fix typo in CONTRIBUTING.md (License) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 56446b0..6132f39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -95,7 +95,7 @@ interface. Specifically: eternal Ziglings contributor glory is yours! -## Licence +## License If you submit your contribution to the repository/project, you agree that your contribution will be licensed under From 4f5a26345fe57dcef32476b587b58b48d53a750c Mon Sep 17 00:00:00 2001 From: Manlio Perillo Date: Sun, 28 Jun 2026 15:12:35 +0200 Subject: [PATCH 18/22] Update color setup using new IO/Terminal API The new API add support for NO_COLOR and CLICOLOR_FORCE. Move color variables to a namespace. Add a message in case colors are not available un unix systems. Add a message in case on Windows colors are available (on old versions). --- rivendell/elrond.zig | 111 +++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/rivendell/elrond.zig b/rivendell/elrond.zig index fa0ed22..10d0ee3 100644 --- a/rivendell/elrond.zig +++ b/rivendell/elrond.zig @@ -19,12 +19,28 @@ const std = @import("std"); const builtin = @import("builtin"); +const Io = std.Io; +const File = Io.File; const Process = std.process; +const Terminal = std.Io.Terminal; const print = std.debug.print; const cutPrefix = std.mem.cutPrefix; const progress_filename = ".progress.txt"; +const no_colors_help = + \\Colors are not available. Check the NO_COLOR environ variable. + \\ + \\ +; + +const windows_colors_help = + \\Windows colors are not supported. + \\Install Windows Terminal or update to a more recent Windows version. + \\ + \\ +; + pub const logo = \\ _ _ _ \\ ___(_) __ _| (_)_ __ __ _ ___ @@ -114,37 +130,18 @@ const Context = struct { const Error = error{Failed}; -// Ansi colors. -var use_color_escapes = false; -var red_text: []const u8 = ""; -var red_bold_text: []const u8 = ""; -var red_dim_text: []const u8 = ""; -var green_text: []const u8 = ""; -var yellow_text: []const u8 = ""; -var bold_text: []const u8 = ""; -var reset_text: []const u8 = ""; +var stderr_term_mode: Terminal.Mode = .no_color; -fn setupColors(io: std.Io) void { - use_color_escapes = false; - const stderr = std.Io.File.stderr(); - if (stderr.supportsAnsiEscapeCodes(io)) |ok| { - if (ok) use_color_escapes = true; - } else |_| {} - if (!use_color_escapes and builtin.os.tag == .windows) { - if (stderr.enableAnsiEscapeCodes(io)) { - use_color_escapes = true; - } else |_| {} - } - if (use_color_escapes) { - red_text = "\x1b[31m"; - red_bold_text = "\x1b[31;1m"; - red_dim_text = "\x1b[31;2m"; - green_text = "\x1b[32m"; - yellow_text = "\x1b[33m"; - bold_text = "\x1b[1m"; - reset_text = "\x1b[0m"; - } -} +// Ansi colors. +const C = struct { + var red: []const u8 = "\x1b[31m"; + var red_bold: []const u8 = "\x1b[31;1m"; + var red_dim: []const u8 = "\x1b[31;2m"; + var green: []const u8 = "\x1b[32m"; + var yellow: []const u8 = "\x1b[33m"; + var bold: []const u8 = "\x1b[1m"; + var reset: []const u8 = "\x1b[0m"; +}; pub fn main(init: std.process.Init) !void { const io = init.io; @@ -153,7 +150,31 @@ pub fn main(init: std.process.Init) !void { var args_it = try init.minimal.args.iterateAllocator(arena); if (!args_it.skip()) @panic("expected self arg"); - setupColors(io); + const stderr = File.stderr(); + + // Setup colors. + const NO_COLOR = try init.minimal.environ.containsUnempty(arena, "NO_COLOR"); + const CLICOLOR_FORCE = try init.minimal.environ.containsUnempty(arena, "CLICOLOR_FORCE"); + + const term_mode = try Terminal.Mode.detect(io, stderr, NO_COLOR, CLICOLOR_FORCE); + stderr_term_mode = term_mode; + + // Show help in case colors are not available or not supported. + switch (term_mode) { + .no_color => print(no_colors_help, .{}), + .escape_codes => {}, + .windows_api => print(windows_colors_help, .{}), + } + + if (term_mode == .no_color) { + C.red = ""; + C.red_bold = ""; + C.red_dim = ""; + C.green = ""; + C.yellow = ""; + C.bold = ""; + C.reset = ""; + } if (!validateExercises()) std.process.exit(1); @@ -166,7 +187,7 @@ pub fn main(init: std.process.Init) !void { while (args_it.next()) |arg| { if (std.mem.eql(u8, arg, "--logo")) { - print("{s}{s}{s}", .{ yellow_text, logo, reset_text }); + print("{s}{s}{s}", .{ C.yellow, logo, C.reset }); return; } else if (cutPrefix(u8, arg, "--zig=")) |v| { zig_exe = v; @@ -243,7 +264,7 @@ pub fn main(init: std.process.Init) !void { } } else { // All solved. - print("{s}All exercises completed!{s}\n", .{ green_text, reset_text }); + print("{s}All exercises completed!{s}\n", .{ C.green, C.reset }); return; } iterateFrom(ctx, start_index) catch std.process.exit(1); @@ -265,7 +286,7 @@ fn runOne(ctx: Context, ex: Exercise, mode: Mode) Error!void { if (ex.skip) { print("Skipping {s}", .{ex.main_file}); if (ex.skip_hint) |hint| - print("\n{s}Reason: {s}{s}\n", .{ bold_text, hint, reset_text }); + print("\n{s}Reason: {s}{s}\n", .{ C.bold, hint, C.reset }); print("\n\n", .{}); return; } @@ -288,7 +309,7 @@ fn runOne(ctx: Context, ex: Exercise, mode: Mode) Error!void { fn hintAndHelp(ex: Exercise, mode: Mode) void { if (ex.hint) |hint| - print("\n{s}{s}Ziglings hint: {s}{s}", .{ bold_text, green_text, hint, reset_text }); + print("\n{s}{s}Ziglings hint: {s}{s}", .{ C.bold, C.green, hint, C.reset }); help(ex, mode); } @@ -333,7 +354,7 @@ fn runExe(ctx: Context, ex: Exercise) !void { .stderr_limit = .limited(1024 * 1024), }) catch |err| { print("{s}error:{s} unable to run {s}: {s}\n", .{ - red_bold_text, reset_text, ex.main_file, @errorName(err), + C.red_bold, C.reset, ex.main_file, @errorName(err), }); return err; }; @@ -367,7 +388,7 @@ fn runTest(ctx: Context, ex: Exercise) !void { .stderr_limit = .limited(1024 * 1024), }) catch |err| { print("{s}error:{s} unable to run test {s}: {s}\n", .{ - red_bold_text, reset_text, ex.main_file, @errorName(err), + C.red_bold, C.reset, ex.main_file, @errorName(err), }); return err; }; @@ -389,7 +410,7 @@ fn checkOutput(io: std.Io, arena: std.mem.Allocator, ex: Exercise, result: Proce }, else => { print("{s}{s} terminated unexpectedly{s}\n", .{ - red_bold_text, ex.main_file, reset_text, + C.red_bold, ex.main_file, C.reset, }); return Error.Failed; }, @@ -424,8 +445,6 @@ fn checkOutput(io: std.Io, arena: std.mem.Allocator, ex: Exercise, result: Proce } if (!std.mem.eql(u8, output, exercise_output)) { - const red = red_bold_text; - const reset = reset_text; print( \\ \\{s}========= expected this output: =========={s} @@ -433,11 +452,11 @@ fn checkOutput(io: std.Io, arena: std.mem.Allocator, ex: Exercise, result: Proce \\{s}========= but found: ====================={s} \\{s} \\{s}=========================================={s} - ++ "\n", .{ red, reset, exercise_output, red, reset, output, red, reset }); + ++ "\n", .{ C.red_bold, C.reset, exercise_output, C.red_bold, C.reset, output, C.red_bold, C.reset }); return Error.Failed; } - print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text }); + print("{s}PASSED:\n{s}{s}\n\n", .{ C.green, output, C.reset }); } fn checkTest(ex: Exercise, result: Process.RunResult) !void { @@ -449,12 +468,12 @@ fn checkTest(ex: Exercise, result: Process.RunResult) !void { }, else => { print("{s}{s} terminated unexpectedly{s}\n", .{ - red_bold_text, ex.main_file, reset_text, + C.red_bold, ex.main_file, C.reset, }); return Error.Failed; }, } - print("{s}PASSED{s}\n\n", .{ green_text, reset_text }); + print("{s}PASSED{s}\n\n", .{ C.green, C.reset }); } fn help(ex: Exercise, mode: Mode) void { @@ -464,12 +483,12 @@ fn help(ex: Exercise, mode: Mode) void { .random => "zig build -Drandom", }; print("\n{s}Edit exercises/{s} and run '{s}' again.{s}\n", .{ - red_bold_text, ex.main_file, cmd, reset_text, + C.red_bold, ex.main_file, cmd, C.reset, }); } fn resetLine() void { - if (use_color_escapes) print("{s}", .{"\x1b[2K\r"}); + if (stderr_term_mode == .escape_codes) print("{s}", .{"\x1b[2K\r"}); } // Removes trailing whitespace per line and any trailing LF at the end. From 951351aa2e3fa2a02c7dcdbab0b942a151fdff22 Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Mon, 29 Jun 2026 18:30:49 +0200 Subject: [PATCH 19/22] added absolute path to exercises, also for tests --- rivendell/elrond.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rivendell/elrond.zig b/rivendell/elrond.zig index 10d0ee3..371c16f 100644 --- a/rivendell/elrond.zig +++ b/rivendell/elrond.zig @@ -370,8 +370,7 @@ fn runTest(ctx: Context, ex: Exercise) !void { const arena = ctx.arena; print("Compiling {s}...\n", .{ex.main_file}); - const path = std.fs.path.join(arena, &.{ ctx.work_path, ex.main_file }) catch - @panic("OOM"); + const path = std.fs.path.join(arena, &.{ ctx.root_path, ctx.work_path, ex.main_file }) catch @panic("OOM"); var argv = std.ArrayList([]const u8).initCapacity(arena, 8) catch @panic("OOM"); argv.append(arena, ctx.zig_exe) catch @panic("OOM"); From 98b831aec4ea4babe4a71b7bd5d90f2ac392ce81 Mon Sep 17 00:00:00 2001 From: it3x Date: Sat, 27 Jun 2026 09:04:32 +0000 Subject: [PATCH 20/22] Refresh 005_arrays2 exercise Updated the exercise to use SIMD. This allows us to cross-reference the concept from 112_vectors back to 005_arrays2, introducing a handy feature of vectors early on. See #430 --- exercises/005_arrays2.zig | 18 ++++++++++++++++-- exercises/112_vectors.zig | 4 ++++ patches/patches/005_arrays2.patch | 16 ++++++++++------ rivendell/elrond.zig | 2 -- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/exercises/005_arrays2.zig b/exercises/005_arrays2.zig index 07b9abe..1de9a36 100644 --- a/exercises/005_arrays2.zig +++ b/exercises/005_arrays2.zig @@ -26,8 +26,13 @@ pub fn main() void { // (Problem 2) // Please set this array using repetition. // It should result in: 1 0 0 1 1 0 0 1 1 0 0 1 - const bit_pattern_unit = [_]u8{ ??? }; - const bit_pattern: [3 * bit_pattern_unit.len]u8 = @bitCast(@as([3][bit_pattern_unit.len]u8, @splat(bit_pattern_unit))); + const bit_pattern_unit = ???; + + // How long should the bit pattern be? + const len = ???; + + // For now, don't worry about the use of SIMD. + const bit_pattern: [len]u8 = std.simd.repeat(len, bit_pattern_unit); // Okay, that's all of the problems. Let's see the results. // @@ -53,3 +58,12 @@ pub fn main() void { std.debug.print("\n", .{}); } + +// For the curious: +// +// The `std.simd.repeat` function takes a target length and a pattern, +// and returns a vector filled with that pattern repeated to the +// desired length. +// +// For example, `repeat(5, [_]u8{1, 2})` will return a vector +// equivalent to `.{1, 2, 1, 2, 1}`. diff --git a/exercises/112_vectors.zig b/exercises/112_vectors.zig index 748c086..0c43f9c 100644 --- a/exercises/112_vectors.zig +++ b/exercises/112_vectors.zig @@ -145,3 +145,7 @@ pub fn main() void { print("Max difference (old fn): {d: >5.3}\n", .{mpd_old}); print("Max difference (new fn): {d: >5.3}\n", .{mpd_new}); } + +// Another cool feature of Vectors is repeating patterns. +// Remember the arrays exercise from earlier where we created an array +// by repeating a pattern? See `005_arrays2.zig`. diff --git a/patches/patches/005_arrays2.patch b/patches/patches/005_arrays2.patch index d1abb36..0c72bd7 100644 --- a/patches/patches/005_arrays2.patch +++ b/patches/patches/005_arrays2.patch @@ -1,6 +1,6 @@ ---- exercises/005_arrays2.zig 2026-05-04 16:26:32.778330847 +0200 -+++ answers/005_arrays2.zig 2026-05-04 16:26:13.082917974 +0200 -@@ -21,12 +21,12 @@ +--- exercises/005_arrays2.zig 2026-06-27 09:03:29.918124920 +0000 ++++ answers/005_arrays2.zig 2026-06-27 09:01:13.162138431 +0000 +@@ -21,15 +21,15 @@ // (Problem 1) // Please set this array concatenating the two arrays above. // It should result in: 1 3 3 7 @@ -10,8 +10,12 @@ // (Problem 2) // Please set this array using repetition. // It should result in: 1 0 0 1 1 0 0 1 1 0 0 1 -- const bit_pattern_unit = [_]u8{ ??? }; +- const bit_pattern_unit = ???; + const bit_pattern_unit = [_]u8{ 1, 0, 0, 1 }; - const bit_pattern: [3 * bit_pattern_unit.len]u8 = @bitCast(@as([3][bit_pattern_unit.len]u8, @splat(bit_pattern_unit))); - // Okay, that's all of the problems. Let's see the results. + // How long should the bit pattern be? +- const len = ???; ++ const len = 3 * bit_pattern_unit.len; + + // For now, don't worry about the use of SIMD. + const bit_pattern: [len]u8 = std.simd.repeat(len, bit_pattern_unit); diff --git a/rivendell/elrond.zig b/rivendell/elrond.zig index 371c16f..2d26ce9 100644 --- a/rivendell/elrond.zig +++ b/rivendell/elrond.zig @@ -599,8 +599,6 @@ const exercises = [_]Exercise{ .main_file = "005_arrays2.zig", .output = "LEET: 1337, Bits: 100110011001", .hint = "Fill in the two arrays.", - .skip = true, - .skip_hint = "Better solution is needed.", }, .{ .main_file = "006_strings.zig", From e93261571265958a996b663d1cde4ebaa712d24e Mon Sep 17 00:00:00 2001 From: it3x Date: Mon, 29 Jun 2026 17:08:42 +0000 Subject: [PATCH 21/22] Remove prefilled solution See #478 --- exercises/109_files.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/109_files.zig b/exercises/109_files.zig index bf4de9b..5514c43 100644 --- a/exercises/109_files.zig +++ b/exercises/109_files.zig @@ -50,7 +50,7 @@ pub fn main(init: std.process.Init) !void { // wait a minute... // opening a directory might fail! // what should we do here? - var output_dir: std.Io.Dir = try cwd.openDir(io, "output", .{}); + var output_dir: std.Io.Dir = cwd.openDir(io, "output", .{}); defer output_dir.close(io); // we try to open the file `zigling.txt`, From b39f223b8c464cddb473c0d9b3efcb22df10f9e0 Mon Sep 17 00:00:00 2001 From: it3x Date: Mon, 29 Jun 2026 17:14:05 +0000 Subject: [PATCH 22/22] Fix patch for 109_files --- patches/patches/109_files.patch | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/patches/patches/109_files.patch b/patches/patches/109_files.patch index ac59d70..c8ad47c 100644 --- a/patches/patches/109_files.patch +++ b/patches/patches/109_files.patch @@ -1,5 +1,5 @@ ---- exercises/109_files.zig 2026-03-20 19:23:48.874150121 +0100 -+++ answers/109_files.zig 2026-04-02 10:51:15.813831695 +0200 +--- exercises/109_files.zig 2026-06-29 17:08:04.124224900 +0000 ++++ answers/109_files.zig 2026-06-29 17:13:20.696220533 +0000 @@ -41,7 +41,7 @@ // by doing nothing // @@ -9,6 +9,15 @@ // if there's any other unexpected error we just propagate it through else => return e, }; +@@ -50,7 +50,7 @@ + // wait a minute... + // opening a directory might fail! + // what should we do here? +- var output_dir: std.Io.Dir = cwd.openDir(io, "output", .{}); ++ var output_dir: std.Io.Dir = try cwd.openDir(io, "output", .{}); + defer output_dir.close(io); + + // we try to open the file `zigling.txt`, @@ -61,7 +61,7 @@ // but here we are not yet done writing to the file // if only there were a keyword in Zig that