// // "You have a long way to go, // and the path is not easy." // Elrond, The Hobbit or The Fellowship of the Ring // // // In the reworked Zig build system (configurer/maker split) a build Step no // longer carries a `makeFn`, and the configure phase is cached and not re-run // when build.zig is unchanged. So Elrond is a standalone program that owns // the entire Ziglings logic: it holds the exercise list, reads/writes // .progress.txt, and iterates through exercises itself -- compiling (via // `zig run`), checking output, printing progress and hints. // The build only ever launches this one program. // // Exit codes: // 0 all requested exercises passed (or skipped, or --logo) // 1 an exercise failed (compile error, output mismatch, runtime error) const std = @import("std"); const builtin = @import("builtin"); const Process = std.process; const print = std.debug.print; const progress_filename = ".progress.txt"; pub const logo = \\ _ _ _ \\ ___(_) __ _| (_)_ __ __ _ ___ \\ |_ | |/ _' | | | '_ \ / _' / __| \\ / /| | (_| | | | | | | (_| \__ \ \\ /___|_|\__, |_|_|_| |_|\__, |___/ \\ |___/ |___/ \\ \\ "Look out! Broken programs below!" \\ \\ ; // How Elrond was called. const Mode = enum { // `zig build`: iterate from after the last solved exercise to the first unsolved one (or the end). normal, // `zig build -Dn=n`: check exactly one exercise. named, // `zig build -Drandom`: check one random exercise. random, // `zig build -Ds=n`: iterate starting at exercise n. start, }; const Kind = enum { // Run the artifact as a normal executable. exe, // Run the artifact as a test. @"test", }; pub const Exercise = struct { // main_file must have the format key_name.zig. main_file: []const u8, // Desired output. A program passes if its output, excluding trailing whitespace, equals this string. output: []const u8, // Optional hint shown if the program does not succeed. hint: ?[]const u8 = null, // By default, output is verified against stderr; set to check stdout. check_stdout: bool = false, // This exercise uses C functions; compile with libc. link_libc: bool = false, // Exercise kind. kind: Kind = .exe, // Not supported by the current Zig compiler. skip: bool = false, // Why this has been skipped. skip_hint: ?[]const u8 = null, timestamp: bool = false, // Name of the main file with .zig stripped. pub fn name(self: Exercise) []const u8 { return std.fs.path.stem(self.main_file); } // Key of the main file: the string before the '_' with zero padding removed. "001_hello.zig" -> "1". pub fn key(self: Exercise) []const u8 { const end_index = std.mem.indexOfScalar(u8, self.main_file, '_') orelse unreachable; var start_index: usize = 0; while (self.main_file[start_index] == '0') start_index += 1; return self.main_file[start_index..end_index]; } // Exercise key as an integer. pub fn number(self: Exercise) usize { return std.fmt.parseInt(usize, self.key(), 10) catch unreachable; } }; // 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 = ""; 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"; } } pub fn main(init: std.process.Init) !void { const io = init.io; const arena = init.arena.allocator(); const args = try init.minimal.args.toSlice(arena); setupColors(io); if (!validateExercises()) std.process.exit(1); var zig_exe: []const u8 = "zig"; var work_path: []const u8 = "exercises"; var mode: Mode = .normal; var only_n: ?usize = null; var start_n: ?usize = null; for (1..args.len) |n| { const arg = args[n]; if (std.mem.eql(u8, arg, "--logo")) { print("{s}{s}{s}", .{ yellow_text, logo, reset_text }); return; } else if (prefix(arg, "--zig=")) |v| { zig_exe = v; } else if (prefix(arg, "--work-path=")) |v| { work_path = v; } else if (prefix(arg, "--only=")) |v| { only_n = std.fmt.parseInt(usize, v, 10) catch { print("invalid --only value: {s}\n", .{v}); std.process.exit(1); }; mode = .named; } else if (prefix(arg, "--start=")) |v| { start_n = std.fmt.parseInt(usize, v, 10) catch { print("invalid --start value: {s}\n", .{v}); std.process.exit(1); }; mode = .start; } else if (std.mem.eql(u8, arg, "--random")) { mode = .random; } else { print("unknown argument: {s}\n", .{arg}); std.process.exit(2); } } print("{s}", .{logo}); const ctx: Context = .{ .io = io, .arena = arena, .zig_exe = zig_exe, .work_path = work_path }; switch (mode) { .named => { const n = only_n.?; if (n == 0 or n > exercises.len - 1) { print("unknown exercise number: {}\n", .{n}); std.process.exit(1); } runOne(ctx, exercises[n - 1], .named) catch std.process.exit(1); }, .random => { var prng = std.Random.DefaultPrng.init(blk: { var seed: u64 = undefined; io.random(std.mem.asBytes(&seed)); break :blk seed; }); const num = prng.random().intRangeLessThan(usize, 0, exercises.len); print("random exercise: {s}\n", .{exercises[num].main_file}); runOne(ctx, exercises[num], .random) catch std.process.exit(1); }, .start => { const s = start_n.?; if (s == 0 or s > exercises.len - 1) { print("unknown exercise number: {}\n", .{s}); std.process.exit(1); } // Iterate from exercise s to the end (or first failure). iterateFrom(ctx, s - 1) catch std.process.exit(1); }, .normal => { // Start after the last solved exercise recorded in .progress.txt. const solved = readProgress(io, arena); var start_index: usize = 0; for (exercises, 0..) |ex, idx| { if (solved < ex.number()) { start_index = idx; break; } } else { // All solved. print("{s}All exercises completed!{s}\n", .{ green_text, reset_text }); return; } iterateFrom(ctx, start_index) catch std.process.exit(1); }, } } fn prefix(arg: []const u8, pre: []const u8) ?[]const u8 { if (std.mem.startsWith(u8, arg, pre)) return arg[pre.len..]; return null; } // Shared, read-only run context threaded through the helpers. const Context = struct { io: std.Io, arena: std.mem.Allocator, zig_exe: []const u8, work_path: []const u8, }; const Error = error{Failed}; // Iterates exercises from `start_index` to the end, stopping at the first failure. // Progress is written after each passed exercise. fn iterateFrom(ctx: Context, start_index: usize) Error!void { for (exercises[start_index..]) |ex| { try runOne(ctx, ex, .normal); } } // Checks a single exercise: progress bar, compile+run (or test), output // verification, progress file update on success, hint on failure. 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\n", .{}); return; } printProgress(ex.number(), exercises.len - 1); switch (ex.kind) { .exe => runExe(ctx, ex) catch { hintAndHelp(ex, mode); return Error.Failed; }, .@"test" => runTest(ctx, ex) catch { hintAndHelp(ex, mode); return Error.Failed; }, } writeProgress(ctx.io, ctx.arena, ex.number()) catch {}; } 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 }); help(ex, mode); } fn printProgress(num: usize, max: usize) void { const bar_width = 60; const safe_max = if (max == 0) 1 else max; const filled_len_u64 = (@as(u64, num) * bar_width) / safe_max; const filled_len = @as(u32, @intCast(filled_len_u64)); var bar_buf: [bar_width]u8 = undefined; for (0..bar_width) |n| { const ord = std.math.order(n, filled_len); bar_buf[n] = switch (ord) { .lt => '#', .eq => '>', .gt => '-', }; } print("\rProgress: [{s}] {d}/{d}\n\n", .{ &bar_buf, num, max }); } fn runExe(ctx: Context, ex: Exercise) !void { const io = ctx.io; 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"); var argv = std.ArrayList([]const u8).initCapacity(arena, 8) catch @panic("OOM"); argv.append(arena, ctx.zig_exe) catch @panic("OOM"); argv.append(arena, "run") catch @panic("OOM"); if (ex.link_libc) { argv.append(arena, "-lc") catch @panic("OOM"); argv.append(arena, "-fllvm") catch @panic("OOM"); } argv.append(arena, path) catch @panic("OOM"); // `zig run` compiles and runs in one step using Zig's own cache. const result = Process.run(arena, io, .{ .argv = argv.items, .stdout_limit = .limited(1024 * 1024), .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), }); return err; }; resetLine(); print("Checking {s}...\n", .{ex.main_file}); return checkOutput(io, arena, ex, result); } fn runTest(ctx: Context, ex: Exercise) !void { const io = ctx.io; 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"); var argv = std.ArrayList([]const u8).initCapacity(arena, 8) catch @panic("OOM"); argv.append(arena, ctx.zig_exe) catch @panic("OOM"); argv.append(arena, "test") catch @panic("OOM"); if (ex.link_libc) { argv.append(arena, "-lc") catch @panic("OOM"); argv.append(arena, "-fllvm") catch @panic("OOM"); } argv.append(arena, path) catch @panic("OOM"); const result = Process.run(arena, io, .{ .argv = argv.items, .stdout_limit = .limited(1024 * 1024), .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), }); return err; }; resetLine(); print("Checking {s}...\n", .{ex.main_file}); return checkTest(ex, result); } fn checkOutput(io: std.Io, arena: std.mem.Allocator, ex: Exercise, result: Process.RunResult) !void { switch (result.term) { .exited => |code| if (code != 0) { // `zig run` puts both compile errors and runtime panics on stderr; // show them so the "read the compiler messages above" hint applies. const diag = std.mem.trimEnd(u8, result.stderr, " \r\n"); if (diag.len > 0) print("{s}\n", .{diag}); return Error.Failed; }, else => { print("{s}{s} terminated unexpectedly{s}\n", .{ red_bold_text, ex.main_file, reset_text, }); return Error.Failed; }, } const raw_output = if (ex.check_stdout) result.stdout else result.stderr; const output = trimLines(arena, raw_output) catch @panic("OOM"); var exercise_output = ex.output; if (ex.timestamp) { var ts_buf: [20]u8 = undefined; const ts_slice = output[14..24]; const ts_value = try std.fmt.parseInt(i64, ts_slice, 10); const ts_build = std.Io.Timestamp.now(io, .real).toSeconds(); const ts_diff = @abs(ts_build - ts_value); const timestamp = std.fmt.bufPrint( &ts_buf, "{}", .{if (ts_diff < 5) ts_value else ts_build}, ) catch unreachable; var buf: [100]u8 = undefined; const prefix_len = 14; const placeholder_len = 11; @memcpy(buf[0..prefix_len], exercise_output[0..prefix_len]); @memcpy(buf[prefix_len..][0..timestamp.len], timestamp); const suffix = exercise_output[prefix_len + placeholder_len ..]; const suffix_dest_start = prefix_len + timestamp.len; @memcpy(buf[suffix_dest_start..][0..suffix.len], suffix); const total_len = prefix_len + timestamp.len + suffix.len; exercise_output = buf[0..total_len]; } if (!std.mem.eql(u8, output, exercise_output)) { const red = red_bold_text; const reset = reset_text; print( \\ \\{s}========= expected this output: =========={s} \\{s} \\{s}========= but found: ====================={s} \\{s} \\{s}=========================================={s} ++ "\n", .{ red, reset, exercise_output, red, reset, output, red, reset }); return Error.Failed; } print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text }); } fn checkTest(ex: Exercise, result: Process.RunResult) !void { switch (result.term) { .exited => |code| if (code != 0) { const stderr = std.mem.trimEnd(u8, result.stderr, " \r\n"); print("\n{s}\n", .{stderr}); return Error.Failed; }, else => { print("{s}{s} terminated unexpectedly{s}\n", .{ red_bold_text, ex.main_file, reset_text, }); return Error.Failed; }, } print("{s}PASSED{s}\n\n", .{ green_text, reset_text }); } fn help(ex: Exercise, mode: Mode) void { const cmd = switch (mode) { .normal, .start => "zig build", .named => "zig build -Dn=...", .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, }); } fn resetLine() void { if (use_color_escapes) print("{s}", .{"\x1b[2K\r"}); } // Removes trailing whitespace per line and any trailing LF at the end. fn trimLines(arena: std.mem.Allocator, buf: []const u8) ![]const u8 { var list = try std.ArrayList(u8).initCapacity(arena, buf.len); var iter = std.mem.splitSequence(u8, buf, " \n"); while (iter.next()) |line| { const data = std.mem.trimEnd(u8, line, " \r"); try list.appendSlice(arena, data); try list.append(arena, '\n'); } const result = try list.toOwnedSlice(arena); return std.mem.trimEnd(u8, result, "\n"); } // Reads the last solved exercise number from .progress.txt; 0 if absent. fn readProgress(io: std.Io, arena: std.mem.Allocator) u32 { const file = std.Io.Dir.cwd().openFile(io, progress_filename, .{}) catch return 0; defer file.close(io); const size = file.length(io) catch return 0; if (size == 0) return 0; const contents = arena.alloc(u8, size) catch return 0; var file_buffer: [1024]u8 = undefined; var reader = file.reader(io, &file_buffer); const n = reader.interface.readSliceShort(contents) catch return 0; const trimmed = std.mem.trim(u8, contents[0..n], " \r\n"); return std.fmt.parseInt(u32, trimmed, 10) catch 0; } fn writeProgress(io: std.Io, arena: std.mem.Allocator, number: usize) !void { const progress = try std.fmt.allocPrint(arena, "{d}", .{number}); const file = try std.Io.Dir.cwd().createFile( io, progress_filename, .{ .read = true, .truncate = true }, ); defer file.close(io); try file.writeStreamingAll(io, progress); try file.sync(io); } // Checks that each exercise number (except the last) forms the sequence // [1, exercises.len), and that output lines have no trailing whitespace. fn validateExercises() bool { var i: usize = 0; for (exercises[0..]) |ex| { const exno = ex.number(); const last = 999; i += 1; if (exno != i and exno != last) { print("exercise {s} has an incorrect number: expected {}, got {s}\n", .{ ex.main_file, i, ex.key(), }); return false; } var iter = std.mem.splitScalar(u8, ex.output, '\n'); while (iter.next()) |line| { const out = std.mem.trimEnd(u8, line, " \r"); if (out.len != line.len) { print("exercise {s} output field lines have trailing whitespace\n", .{ ex.main_file, }); return false; } } if (!std.mem.endsWith(u8, ex.main_file, ".zig")) { print("exercise {s} is not a zig source file\n", .{ex.main_file}); return false; } } return true; } const exercises = [_]Exercise{ .{ .main_file = "001_hello.zig", .output = "Hello world!", .hint = \\DON'T PANIC! \\Read the compiler messages above. (Something about 'main'?) \\Open up the source file as noted below and read the comments. \\ \\(Hints like these will occasionally show up, but for the \\most part, you'll be taking directions from the Zig \\compiler itself.) \\ , // pay attention to the comma }, .{ .main_file = "002_std.zig", .output = "Standard Library.", }, .{ .main_file = "003_assignment.zig", .output = "55 314159 -11", .hint = "There are three mistakes in this one!", }, .{ .main_file = "004_arrays.zig", .output = "First: 2, Fourth: 7, Length: 8", .hint = "There are two things to complete here.", }, .{ .main_file = "005_arrays2.zig", .output = "LEET: 1337, Bits: 100110011001", .hint = "Fill in the two arrays.", }, .{ .main_file = "006_strings.zig", .output = "d=d Major Tom", .hint = "Each '???' needs something filled in.", }, .{ .main_file = "007_strings2.zig", .output = \\Ziggy played guitar \\Jamming good with Andrew Kelley \\And the Spiders from Mars , // pay attention to the comma .hint = "Please fix the lyrics!", }, .{ .main_file = "008_quiz.zig", .output = "Program in Zig!", .hint = "See if you can fix the program!", }, .{ .main_file = "009_if.zig", .output = "Foo is 42!", }, .{ .main_file = "010_if2.zig", .output = "With the discount, the price is $17.", }, .{ .main_file = "011_while.zig", .output = "2 4 8 16 32 64 128 256 512 n=1024", .hint = "You probably want a 'less than' condition.", }, .{ .main_file = "012_while2.zig", .output = "2 4 8 16 32 64 128 256 512 n=1024", .hint = "It might help to look back at the previous exercise.", }, .{ .main_file = "013_while3.zig", .output = "1 2 4 7 8 11 13 14 16 17 19", }, .{ .main_file = "014_while4.zig", .output = "n=4", }, .{ .main_file = "015_for.zig", .output = "A Dramatic Story: :-) :-) :-( :-| :-) The End.", }, .{ .main_file = "016_for2.zig", .output = "The value of bits '1101': 13.", }, .{ .main_file = "017_quiz2.zig", .output = "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16,", .hint = "This is a famous game!", }, .{ .main_file = "018_functions.zig", .output = "Answer to the Ultimate Question: 42", .hint = "Can you help write the function?", }, .{ .main_file = "019_functions2.zig", .output = "Powers of two: 2 4 8 16", }, .{ .main_file = "020_quiz3.zig", .output = "32 64 128 256", .hint = "Unexpected pop quiz! Help!", }, .{ .main_file = "021_errors.zig", .output = "2<4. 3<4. 4=4. 5>4. 6>4.", .hint = "What's the deal with fours?", }, .{ .main_file = "022_errors2.zig", .output = "I compiled!", .hint = "Get the error union type right to allow this to compile.", }, .{ .main_file = "023_errors3.zig", .output = "a=64, b=22", }, .{ .main_file = "024_errors4.zig", .output = "a=20, b=14, c=10", }, .{ .main_file = "025_errors5.zig", .output = "a=0, b=19, c=0", }, .{ .main_file = "026_hello2.zig", .output = "Hello world!", .hint = "Try using a try!", .check_stdout = true, }, .{ .main_file = "027_defer.zig", .output = "One Two", }, .{ .main_file = "028_defer2.zig", .output = \\(Goat) (Cat) (Dog) (Dog) (Goat) (Unknown) done. \\Answer to everything? 42 , // pay attention to the comma }, .{ .main_file = "029_errdefer.zig", .output = "Getting number...got 5. Getting number...failed!", }, .{ .main_file = "030_switch.zig", .output = "ZIG?", }, .{ .main_file = "031_switch2.zig", .output = "ZIG!", }, .{ .main_file = "032_unreachable.zig", .output = "1 2 3 9 8 7", }, .{ .main_file = "033_iferror.zig", .output = "2<4. 3<4. 4=4. 5>4. 6>4.", .hint = "Seriously, what's the deal with fours?", }, .{ .main_file = "034_quiz4.zig", .output = "my_num=42", .hint = "Can you make this work?", .check_stdout = true, }, .{ .main_file = "035_enums.zig", .output = "1 2 3 9 8 7", .hint = "This problem seems familiar...", }, .{ .main_file = "036_enums2.zig", .output = \\
\\ Red \\ Green \\ Blue \\
, // pay attention to the comma .hint = "I'm feeling blue about this.", }, .{ .main_file = "037_structs.zig", .output = "Your wizard has 90 health and 25 gold.", }, .{ .main_file = "038_structs2.zig", .output = \\Character 1 - G:20 H:100 XP:10 \\Character 2 - G:10 H:100 XP:20 , // pay attention to the comma }, .{ .main_file = "039_pointers.zig", .output = "num1: 5, num2: 5", .hint = "Pointers aren't so bad.", }, .{ .main_file = "040_pointers2.zig", .output = "a: 12, b: 12", }, .{ .main_file = "041_pointers3.zig", .output = "foo=6, bar=11", }, .{ .main_file = "042_pointers4.zig", .output = "num: 5, more_nums: 1 1 5 1", }, .{ .main_file = "043_pointers5.zig", .output = \\Wizard (G:10 H:100 XP:20) \\ Mentor: Wizard (G:10000 H:100 XP:2340) , // pay attention to the comma }, .{ .main_file = "044_quiz5.zig", .output = "Elephant A. Elephant B. Elephant C.", .hint = "Oh no! We forgot Elephant B!", }, .{ .main_file = "045_optionals.zig", .output = "The Ultimate Answer: 42.", }, .{ .main_file = "046_optionals2.zig", .output = "Elephant A. Elephant B. Elephant C.", .hint = "Elephants again!", }, .{ .main_file = "047_methods.zig", .output = "5 aliens. 4 aliens. 1 aliens. 0 aliens. Earth is saved!", .hint = "Use the heat ray. And the method!", }, .{ .main_file = "048_methods2.zig", .output = "A B C", .hint = "This just needs one little fix.", }, .{ .main_file = "049_quiz6.zig", .output = "A B C Cv Bv Av", .hint = "Now you're writing Zig!", }, .{ .main_file = "050_no_value.zig", .output = "That is not dead which can eternal lie / And with strange aeons even death may die.", }, .{ .main_file = "051_values.zig", .output = "1:false!. 2:true!. 3:true!. XP before:0, after:200.", }, .{ .main_file = "052_slices.zig", .output = \\Hand1: A 4 K 8 \\Hand2: 5 2 Q J , // pay attention to the comma }, .{ .main_file = "053_slices2.zig", .output = "'all your base are belong to us.' 'for great justice.'", }, .{ .main_file = "054_manypointers.zig", .output = "Memory is a resource.", }, .{ .main_file = "055_unions.zig", .output = "Insect report! Ant alive is: true. Bee visited 15 flowers.", }, .{ .main_file = "056_unions2.zig", .output = "Insect report! Ant alive is: true. Bee visited 16 flowers.", }, .{ .main_file = "057_unions3.zig", .output = "Insect report! Ant alive is: true. Bee visited 17 flowers.", }, .{ .main_file = "058_quiz7.zig", .output = "Archer's Point--2->Bridge--1->Dogwood Grove--3->Cottage--2->East Pond--1->Fox Pond", .hint = "This is the biggest program we've seen yet. But you can do it!", }, .{ .main_file = "059_integers.zig", .output = "Zig is cool.", }, .{ .main_file = "060_floats.zig", .output = "Shuttle liftoff weight: 2.032e3 metric tons", }, .{ .main_file = "061_coercions.zig", .output = "Letter: A", }, .{ .main_file = "062_loop_expressions.zig", .output = "Current language: Zig", .hint = "Surely the current language is 'Zig'!", }, .{ .main_file = "063_labels.zig", .output = "Enjoy your Cheesy Chili!", }, .{ .main_file = "064_builtins.zig", .output = "1101 + 0101 = 0010 (true). Without overflow: 00010010. Furthermore, 11110000 backwards is 00001111.", }, .{ .main_file = "065_builtins2.zig", .output = "A Narcissus loves all Narcissuses. He has room in his heart for: me myself.", }, .{ .main_file = "066_comptime.zig", .output = "Immutable: 12345, 987.654; Mutable: 54321, 456.789; Types: comptime_int, comptime_float, u32, f32", .hint = "It may help to read this one out loud to your favorite stuffed animal until it sinks in completely.", }, .{ .main_file = "067_comptime2.zig", .output = "A BB CCC DDDD", }, .{ .main_file = "068_comptime3.zig", .output = \\Minnow (1:32, 4 x 2) \\Shark (1:16, 8 x 5) \\Whale (1:1, 143 x 95) , }, .{ .main_file = "069_comptime4.zig", .output = "s1={ 1, 2, 3 }, s2={ 1, 2, 3, 4, 5 }, s3={ 1, 2, 3, 4, 5, 6, 7 }", }, .{ .main_file = "070_comptime5.zig", .output = \\"Quack." ducky1: true, "Squeek!" ducky2: true, ducky3: false , .hint = "Have you kept the wizard hat on?", }, .{ .main_file = "071_comptime6.zig", .output = "Narcissus has room in his heart for: me myself.", }, .{ .main_file = "072_comptime7.zig", .output = "26", }, .{ .main_file = "073_comptime8.zig", .output = "My llama value is 25.", }, .{ .main_file = "074_comptime9.zig", .output = "MouseLlama joins the crew!", }, .{ .main_file = "075_quiz8.zig", .output = "Archer's Point--2->Bridge--1->Dogwood Grove--3->Cottage--2->East Pond--1->Fox Pond", .hint = "Roll up those sleeves. You get to WRITE some code for this one.", }, .{ .main_file = "076_sentinels.zig", .output = "Array:123056. Many-item pointer:123.", }, .{ .main_file = "077_sentinels2.zig", .output = "Weird Data!", }, .{ .main_file = "078_sentinels3.zig", .output = "Weird Data!", }, .{ .main_file = "079_quoted_identifiers.zig", .output = "Sweet freedom: 55, false.", .hint = "Help us, Zig Programmer, you're our only hope!", }, .{ .main_file = "080_anonymous_structs.zig", .output = "[Circle(i32): 25,70,15] [Circle(f32): 25.2,71.0,15.7]", }, .{ .main_file = "081_anonymous_structs2.zig", .output = "x:205 y:187 radius:12", }, .{ .main_file = "082_anonymous_structs3.zig", .output = \\"0"(bool):true "1"(bool):false "2"(i32):42 "3"(f32):3.141592 , // pay attention to the comma .hint = "This one is a challenge! But you have everything you need.", }, .{ .main_file = "083_anonymous_lists.zig", .output = "I say hello!", }, .{ .main_file = "084_interfaces.zig", .output = \\=== Doctor Zoraptera's Insect Report === \\Ant is alive. \\Bee visited 17 flowers. \\Grasshopper hopped 32 meters. , // pay attention to the comma }, .{ .main_file = "085_async.zig", .output = "Current time: