aboutsummaryrefslogtreecommitdiff
path: root/exercises
diff options
context:
space:
mode:
Diffstat (limited to 'exercises')
-rw-r--r--exercises/01_hello.zig22
-rw-r--r--exercises/02_std.zig24
-rw-r--r--exercises/03_assignment.zig48
-rw-r--r--exercises/04_arrays.zig51
-rw-r--r--exercises/05_arrays2.zig47
-rw-r--r--exercises/06_strings.zig48
-rw-r--r--exercises/07_strings2.zig24
-rw-r--r--exercises/08_quiz.zig34
-rw-r--r--exercises/09_if.zig32
-rw-r--r--exercises/10_if2.zig25
-rw-r--r--exercises/11_while.zig34
-rw-r--r--exercises/12_while2.zig35
-rw-r--r--exercises/13_while3.zig33
-rw-r--r--exercises/14_while4.zig26
-rw-r--r--exercises/15_for.zig28
-rw-r--r--exercises/16_for2.zig33
-rw-r--r--exercises/17_quiz2.zig28
-rw-r--r--exercises/18_functions.zig34
-rw-r--r--exercises/19_functions2.zig31
-rw-r--r--exercises/20_quiz3.zig45
-rw-r--r--exercises/21_errors.zig46
-rw-r--r--exercises/22_errors2.zig30
-rw-r--r--exercises/23_errors3.zig28
-rw-r--r--exercises/24_errors4.zig68
-rw-r--r--exercises/25_errors5.zig40
-rw-r--r--exercises/26_hello2.zig23
-rw-r--r--exercises/27_defer.zig25
-rw-r--r--exercises/28_defer2.zig29
-rw-r--r--exercises/29_errdefer.zig60
-rw-r--r--exercises/30_switch.zig55
-rw-r--r--exercises/31_switch2.zig42
-rw-r--r--exercises/32_unreachable.zig38
-rw-r--r--exercises/33_iferror.zig49
-rw-r--r--exercises/34_quiz4.zig24
-rw-r--r--exercises/35_enums.zig49
-rw-r--r--exercises/36_enums2.zig61
-rw-r--r--exercises/37_structs.zig59
-rw-r--r--exercises/38_structs2.zig51
-rw-r--r--exercises/39_pointers.zig36
-rw-r--r--exercises/40_pointers2.zig27
-rw-r--r--exercises/41_pointers3.zig41
-rw-r--r--exercises/42_pointers4.zig33
-rw-r--r--exercises/43_pointers5.zig84
43 files changed, 1680 insertions, 0 deletions
diff --git a/exercises/01_hello.zig b/exercises/01_hello.zig
new file mode 100644
index 0000000..8d26940
--- /dev/null
+++ b/exercises/01_hello.zig
@@ -0,0 +1,22 @@
+//
+// Oh no! This program is supposed to print "Hello world!" but it needs
+// your help!
+//
+//
+// Zig functions are private by default but the main() function should
+// be public.
+//
+// A function is declared public with the "pub" statement like so:
+//
+// pub fn foo() void {
+// ...
+// }
+//
+// Try to fix the program and run `ziglings` to see if it works!
+//
+const std = @import("std");
+
+fn main() void {
+ std.debug.print("Hello world!\n", .{});
+}
+
diff --git a/exercises/02_std.zig b/exercises/02_std.zig
new file mode 100644
index 0000000..dcc1b87
--- /dev/null
+++ b/exercises/02_std.zig
@@ -0,0 +1,24 @@
+//
+// Oops! This program is supposed to print a line like our Hello World
+// example. But we forgot how to import the Zig Standard Library.
+//
+// The @import() function is built into Zig. It returns a value which
+// represents the imported code. It's a good idea to store the import as
+// a constant value with the same name as the import:
+//
+// const foo = @import("foo");
+//
+// Please complete the import below:
+//
+
+??? = @import("std");
+
+pub fn main() void {
+ std.debug.print("Standard Library.\n", .{});
+}
+
+// Going deeper: imports must be declared as "constants" (with the 'const'
+// keyword rather than "variables" (with the 'var' keyword) is that they
+// can only be used at "compile time" rather than "run time". Zig evaluates
+// const values at compile time. Don't worry if none of this makes sense
+// yet! See also this answer: https://stackoverflow.com/a/62567550/695615
diff --git a/exercises/03_assignment.zig b/exercises/03_assignment.zig
new file mode 100644
index 0000000..d26f2a2
--- /dev/null
+++ b/exercises/03_assignment.zig
@@ -0,0 +1,48 @@
+//
+// It seems we got a little carried away making everything "const u8"!
+//
+// "const" values cannot change.
+// "u" types are "unsigned" and cannot store negative values.
+// "8" means the type is 8 bits in size.
+//
+// Example: foo cannot change (it is CONSTant)
+// bar can change (it is VARiable):
+//
+// const foo: u8 = 20;
+// var bar: u8 = 20;
+//
+// Example: foo cannot be negative and can hold 0 to 255
+// bar CAN be negative and can hold −128 to 127
+//
+// const foo: u8 = 20;
+// var bar: i8 = -20;
+//
+// Example: foo can hold 8 bits (0 to 255)
+// bar can hold 16 bits (0 to 65,535)
+//
+// You can do just about any combination of these that you can think of:
+//
+// u32 can hold 0 to 4,294,967,295
+// i64 can hold −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
+//
+// Please fix this program so that the types can hold the desired values
+// and the errors go away!
+//
+const std = @import("std");
+
+pub fn main() void {
+ const n: u8 = 50;
+ n = n + 5;
+
+ const pi: u8 = 314159;
+
+ const negative_eleven: u8 = -11;
+
+ // There are no errors in the next line, just explanation:
+ // Perhaps you noticed before that the print function takes two
+ // parameters. Now it will make more sense: the first parameter
+ // is a string. The string may contain placeholders '{}', and the
+ // second parameter is an "anonymous list literal" (don't worry
+ // about this for now!) with the values to be printed.
+ std.debug.print("{} {} {}\n", .{n, pi, negative_eleven});
+}
diff --git a/exercises/04_arrays.zig b/exercises/04_arrays.zig
new file mode 100644
index 0000000..0f4ffe1
--- /dev/null
+++ b/exercises/04_arrays.zig
@@ -0,0 +1,51 @@
+//
+// Let's learn some array basics. Arrays are declared with:
+//
+// var foo [3]u32 = [3]u32{ 42, 108, 5423 };
+//
+// When Zig can infer the size of the array, you can use '_' for the
+// size. You can also let Zig infer the type of the value so the
+// declaration is much less verbose.
+//
+// var foo = [_]u32{ 42, 108, 5423 };
+//
+// Get values of an array using array[index] notation:
+//
+// const bar = foo[3]; // 5423
+//
+// Set values of an array using array[index] notation:
+//
+// foo[3] = 16;
+//
+// Get the length of an array using the len property:
+//
+// const length = foo.len;
+//
+const std = @import("std");
+
+pub fn main() void {
+ // (Problem 1)
+ // This "const" is going to cause a problem later - can you see what it is?
+ // How do we fix it?
+ const some_primes = [_]u8{ 1, 3, 5, 7, 11, 13, 17, 19 };
+
+ // Individual values can be set with '[]' notation.
+ // Example: This line changes the first prime to 2 (which is correct):
+ some_primes[0] = 2;
+
+ // Individual values can also be accessed with '[]' notation.
+ // Example: This line stores the first prime in "first":
+ const first = some_primes[0];
+
+ // (Problem 2)
+ // Looks like we need to complete this expression. Use the example
+ // above to set "fourth" to the fourth element of the some_primes array:
+ const fourth = some_primes[???];
+
+ // (Problem 3)
+ // Use the len property to get the length of the array:
+ const length = some_primes.???;
+
+ std.debug.print("First: {}, Fourth: {}, Length: {}\n",
+ .{first, fourth, length});
+}
diff --git a/exercises/05_arrays2.zig b/exercises/05_arrays2.zig
new file mode 100644
index 0000000..9282a31
--- /dev/null
+++ b/exercises/05_arrays2.zig
@@ -0,0 +1,47 @@
+//
+// Zig has some fun array operators.
+//
+// You can use '++' to concatenate two arrays:
+//
+// const a = [_]u8{ 1,2 };
+// const b = [_]u8{ 3,4 };
+// const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5
+//
+// You can use '**' to repeat an array:
+//
+// const d = [_]u8{ 1,2,3 } ** 2; // equals 1 2 3 1 2 3
+//
+const std = @import("std");
+
+pub fn main() void {
+ const le = [_]u8{ 1, 3 };
+ const et = [_]u8{ 3, 7 };
+
+ // (Problem 1)
+ // Please set this array concatenating the two arrays above.
+ // It should result in: 1 3 3 7
+ const leet = ???;
+
+ // (Problem 2)
+ // Please set this array to using repetition.
+ // It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
+ const bit_pattern = [_]u8{ ??? } ** 3;
+
+ // Okay, that's all of the problems. Let's see the results.
+ //
+ // We could print these arrays with leet[0], leet[1],...but let's
+ // have a little preview of Zig "for" loops instead:
+ std.debug.print("LEET: ", .{});
+
+ for (leet) |*n| {
+ std.debug.print("{}", .{n.*});
+ }
+
+ std.debug.print(", Bits: ", .{});
+
+ for (bit_pattern) |*n| {
+ std.debug.print("{}", .{n.*});
+ }
+
+ std.debug.print("\n", .{});
+}
diff --git a/exercises/06_strings.zig b/exercises/06_strings.zig
new file mode 100644
index 0000000..2430884
--- /dev/null
+++ b/exercises/06_strings.zig
@@ -0,0 +1,48 @@
+//
+// Now that we've learned about arrays, we can talk about strings.
+//
+// We've already seen Zig string literals: "Hello world.\n"
+//
+// Zig stores strings as arrays of bytes.
+//
+// const foo = "Hello";
+//
+// Is the same as:
+//
+// const foo = [_]u8{ 'H', 'e', 'l', 'l', 'o' };
+//
+const std = @import("std");
+
+pub fn main() void {
+ const ziggy = "stardust";
+
+ // (Problem 1)
+ // Use array square bracket syntax to get the letter 'd' from
+ // the string "stardust" above.
+ const d: u8 = ziggy[???];
+
+ // (Problem 2)
+ // Use the array repeat '**' operator to make "ha ha ha".
+ const laugh = "ha " ???;
+
+ // (Problem 3)
+ // Use the array concatenation '++' operator to make "Major Tom".
+ // (You'll need to add a space as well!)
+ const major = "Major";
+ const tom = "Tom";
+ const major_tom = major ??? tom;
+
+ // That's all the problems. Let's see our results:
+ std.debug.print("d={u} {}{}\n",.{d, laugh, major_tom});
+ //
+ // Keen eyes will notice that we've put a 'u' inside the '{}'
+ // placeholder in the format string above. This tells the
+ // print() function to format the values as a UTF-8 character.
+ // If we didn't do this, we'd see '100', which is the decimal
+ // number corresponding with the 'd' character in UTF-8.
+ //
+ // While we're on this subject, 'c' (ASCII encoded character)
+ // would work in place for 'u' because the first 128 characters
+ // of UTF-8 are the same as ASCII!
+ //
+}
diff --git a/exercises/07_strings2.zig b/exercises/07_strings2.zig
new file mode 100644
index 0000000..bb81bc7
--- /dev/null
+++ b/exercises/07_strings2.zig
@@ -0,0 +1,24 @@
+//
+// Here's a fun one: Zig has multi-line strings!
+//
+// To make a multi-line string, put '\\' at the beginning of each
+// line just like a code comment but with backslashes instead:
+//
+// const two_lines =
+// \\Line One
+// \\Line Two
+// ;
+//
+// See if you can make this program print some song lyrics.
+//
+const std = @import("std");
+
+pub fn main() void {
+ const lyrics =
+ Ziggy played guitar
+ Jamming good with Andrew Kelley
+ And the Spiders from Mars
+ ;
+
+ std.debug.print("{}\n",.{lyrics});
+}
diff --git a/exercises/08_quiz.zig b/exercises/08_quiz.zig
new file mode 100644
index 0000000..e23f856
--- /dev/null
+++ b/exercises/08_quiz.zig
@@ -0,0 +1,34 @@
+//
+// Quiz time! Let's see if you can fix this whole program.
+//
+// This is meant to be challenging.
+//
+// Let the compiler tell you what's wrong.
+//
+// Start at the top.
+//
+const std = @import("std");
+
+pub fn main() void {
+ // What is this nonsense? :-)
+ const letters = "YZhifg";
+
+ const x: u8 = 1;
+
+ // This is something you haven't seen before: declaring an array
+ // without putting anything in it. There is no error here:
+ var lang: [3]u8 = undefined;
+
+ // The following lines attempt to put 'Z', 'i', and 'g' into the
+ // 'lang' array we just created.
+ lang[0] = letters[x];
+
+ x = 3;
+ lang[???] = letters[x];
+
+ x = ???;
+ lang[2] = letters[???];
+
+ // We want to "Program in Zig!" of course:
+ std.debug.print("Program in {}!\n", .{lang});
+}
diff --git a/exercises/09_if.zig b/exercises/09_if.zig
new file mode 100644
index 0000000..28ac712
--- /dev/null
+++ b/exercises/09_if.zig
@@ -0,0 +1,32 @@
+//
+// Now we get into the fun stuff, starting with the 'if' statement!
+//
+// if (true) {
+// ...
+// } else {
+// ...
+// }
+//
+// Zig has the "usual" comparison operators such as:
+//
+// a == b means "a equals b"
+// a < b means "a is less than b"
+// a !=b means "a does not equal b"
+//
+// The important thing about Zig's "if" is that it *only* accepts
+// boolean values. It won't coerce numbers or other types of data
+// to true and false.
+//
+const std = @import("std");
+
+pub fn main() void {
+ const foo = 1;
+
+ // Please fix this condition:
+ if (foo) {
+ // We want out program to print this message!
+ std.debug.print("Foo is 1!\n", .{});
+ } else {
+ std.debug.print("Foo is not 1!\n", .{});
+ }
+}
diff --git a/exercises/10_if2.zig b/exercises/10_if2.zig
new file mode 100644
index 0000000..4f559cd
--- /dev/null
+++ b/exercises/10_if2.zig
@@ -0,0 +1,25 @@
+//
+// If statements are also valid expressions:
+//
+// var foo: u8 = if (a) 2 else 3;
+//
+// Note: You'll need to declare a variable type when assigning a value
+// from a statement like this because the compiler isn't smart enough
+// to infer the type for you.
+//
+// This WON'T work:
+//
+// var foo = if (a) 2 else 3; // error!
+//
+const std = @import("std");
+
+pub fn main() void {
+ var discount = true;
+
+ // Please use an if...else expression to set "price".
+ // If discount is true, the price should be $17, otherwise $20:
+ var price = if ???;
+
+ std.debug.print("With the discount, the price is ${}.\n", .{price});
+}
+
diff --git a/exercises/11_while.zig b/exercises/11_while.zig
new file mode 100644
index 0000000..4c4fc4f
--- /dev/null
+++ b/exercises/11_while.zig
@@ -0,0 +1,34 @@
+//
+// Zig 'while' statements create a loop that runs while the
+// condition is true. This runs once (at most):
+//
+// while (condition) {
+// condition = false;
+// }
+//
+// Remember that the condition must be a boolean value and
+// that we can get a boolean value from conditional operators
+// such as:
+//
+// a == b means "a equals b"
+// a < b means "a is less than b"
+// a > b means "a is greater than b"
+// a !=b means "a does not equal b"
+//
+const std = @import("std");
+
+pub fn main() void {
+ var n: u32 = 2;
+
+ // Please use a condition that is true UNTIL "n" reaches 1024:
+ while ( ??? ){
+ // Print the current number
+ std.debug.print("{} ", .{n});
+
+ // Set n to n multiplied by 2
+ n *= 2;
+ }
+
+ // Once the above is correct, this will print "n=1024"
+ std.debug.print("n={}\n", .{n});
+}
diff --git a/exercises/12_while2.zig b/exercises/12_while2.zig
new file mode 100644
index 0000000..6f808c8
--- /dev/null
+++ b/exercises/12_while2.zig
@@ -0,0 +1,35 @@
+//
+// Zig 'while' statements can have an optional 'continue expression'
+// which runs every time the while loop continues (either at the
+// end of the loop or when an explicit 'continue' is invoked (we'll
+// try those out next):
+//
+// while (condition) : (continue expression)
+// ...
+// }
+//
+// Example:
+//
+// var foo = 2;
+// while (foo<10) : (foo+=2)
+// // Do something with even numbers less than 10...
+// }
+//
+// See if you can re-write the last exercise using a continue
+// expression:
+//
+const std = @import("std");
+
+pub fn main() void {
+ var n: u32 = 2;
+
+ // Please set the continue expression so that we get the desired
+ // results in the print statement below.
+ while (n < 1000) : ??? {
+ // Print the current number
+ std.debug.print("{} ", .{n});
+ }
+
+ // As in the last exercise, we want this to result in "n=1024"
+ std.debug.print("n={}\n", .{n});
+}
diff --git a/exercises/13_while3.zig b/exercises/13_while3.zig
new file mode 100644
index 0000000..3ff42ff
--- /dev/null
+++ b/exercises/13_while3.zig
@@ -0,0 +1,33 @@
+//
+// The last two exercises were functionally identical. Continue
+// expressions really show their utility when used with 'continue'
+// statements!
+//
+// Example:
+//
+// while (condition) : (continue expression){
+//
+// if(other condition) continue;
+//
+// }
+//
+// The "continue expression" executes every time the loop restarts
+// whether the "continue" statement happens or not.
+//
+const std = @import("std");
+
+pub fn main() void {
+ var n: u32 = 1;
+
+ // I want to print every number between 1 and 20 that is NOT
+ // divisible by 3 or 5.
+ while (n <= 20) : (n+=1) {
+ // The '%' symbol is the "modulo" operator and it
+ // returns the remainder after division.
+ if(n % 3 == 0) ???;
+ if(n % 5 == 0) ???;
+ std.debug.print("{} ", .{n});
+ }
+
+ std.debug.print("\n", .{});
+}
diff --git a/exercises/14_while4.zig b/exercises/14_while4.zig
new file mode 100644
index 0000000..a28b9a9
--- /dev/null
+++ b/exercises/14_while4.zig
@@ -0,0 +1,26 @@
+//
+// You can force a loop to exit immediately with a "break" statement:
+//
+// while (condition) : (continue expression){
+//
+// if(other condition) break;
+//
+// }
+//
+// Continue expressions do NOT execute when a while loop stops
+// because of a break!
+//
+const std = @import("std");
+
+pub fn main() void {
+ var n: u32 = 1;
+
+ // Oh dear! This while loop will go forever!?
+ // Please fix this so the print statement below gives the desired output.
+ while (true) : (n+=1) {
+ if(???) ???;
+ }
+
+ // Result: we want n=4
+ std.debug.print("n={}\n", .{n});
+}
diff --git a/exercises/15_for.zig b/exercises/15_for.zig
new file mode 100644
index 0000000..652478b
--- /dev/null
+++ b/exercises/15_for.zig
@@ -0,0 +1,28 @@
+//
+// Behold the 'for' loop! It lets you execute code for each
+// member of an array:
+//
+// for (items) |item| {
+//
+// // Do something with item
+//
+// }
+//
+const std = @import("std");
+
+pub fn main() void {
+ const story = [_]u8{ 'h', 'h', 's', 'n', 'h' };
+
+ std.debug.print("A Dramatic Story: ", .{});
+
+ for (???) |???| {
+ if(scene == 'h') std.debug.print(":-) ", .{});
+ if(scene == 's') std.debug.print(":-( ", .{});
+ if(scene == 'n') std.debug.print(":-| ", .{});
+ }
+
+ std.debug.print("The End.\n", .{});
+}
+//
+// Note that "for" loops also work on things called "slices"
+// which we'll see later.
diff --git a/exercises/16_for2.zig b/exercises/16_for2.zig
new file mode 100644
index 0000000..0a62a1a
--- /dev/null
+++ b/exercises/16_for2.zig
@@ -0,0 +1,33 @@
+//
+// For loops also let you store the "index" of the iteration - a
+// number starting with 0 that counts up with each iteration:
+//
+// for (items) |item, index| {
+//
+// // Do something with item and index
+//
+// }
+//
+// You can name "item" and "index" anything you want. "i" is a popular
+// shortening of "index". The item name is often the singular form of
+// the items you're looping through.
+//
+const std = @import("std");
+
+pub fn main() void {
+ // Let's store the bits of binary number 1101 in
+ // 'little-endian' order (least significant byte first):
+ const bits = [_]u8{ 1, 0, 1, 1 };
+ var value: u32 = 0;
+
+ // Now we'll convert the binary bits to a number value by adding
+ // the value of the place as a power of two for each bit.
+ //
+ // See if you can figure out the missing piece:
+ for (bits) |bit, ???| {
+ var place_value = std.math.pow(u32, 2, @intCast(u32, i));
+ value += place_value * bit;
+ }
+
+ std.debug.print("The value of bits '1101': {}.\n", .{value});
+}
diff --git a/exercises/17_quiz2.zig b/exercises/17_quiz2.zig
new file mode 100644
index 0000000..339f733
--- /dev/null
+++ b/exercises/17_quiz2.zig
@@ -0,0 +1,28 @@
+//
+// Quiz time again! Let's see if you can solve the famous "Fizz Buzz"!
+//
+// "Players take turns to count incrementally, replacing
+// any number divisible by three with the word "fizz",
+// and any number divisible by five with the word "buzz".
+// - From https://en.wikipedia.org/wiki/Fizz_buzz
+//
+// Let's go from 1 to 16. This has been started for you, but there's
+// some problems. :-(
+//
+const std = import standard library;
+
+function main() void {
+ var i: u8 = 1;
+ var stop_at: u8 = 16;
+
+ // What kind of loop is this? A 'for' or a 'while'?
+ ??? (i <= stop_at) : (i += 1) {
+ if (i % 3 == 0) std.debug.print("Fizz", .{});
+ if (i % 5 == 0) std.debug.print("Buzz", .{});
+ if ( !(i % 3 == 0) and !(i % 5 == 0) ) {
+ std.debug.print("{}", .{???});
+ }
+ std.debug.print(", ", .{});
+ }
+ std.debug.print("\n",.{});
+}
diff --git a/exercises/18_functions.zig b/exercises/18_functions.zig
new file mode 100644
index 0000000..bda90cd
--- /dev/null
+++ b/exercises/18_functions.zig
@@ -0,0 +1,34 @@
+//
+// Functions! We've already seen a lot of one called "main()". Now let's try
+// writing one of our own:
+//
+// fn foo(n: u8) u8 {
+// return n+1;
+// }
+//
+// The foo() function above takes a number "n" and returns a number that is
+// larger by one.
+//
+// If your function doesn't take any parameters and doesn't return anything,
+// it would be defined like main():
+//
+// fn foo() void { }
+//
+const std = @import("std");
+
+pub fn main() void {
+ // The new function deepThought() should return the number 42. See below.
+ const answer: u8 = deepThought();
+
+ std.debug.print("Answer to the Ultimate Question: {}\n", .{answer});
+}
+
+//
+// Please define the deepThought() function below.
+//
+// We're just missing a couple things. One thing we're NOT missing is the
+// keyword "pub", which is not needed here. Can you guess why?
+//
+??? deepThought() ??? {
+ return 42; // Number courtesy Douglas Adams
+}
diff --git a/exercises/19_functions2.zig b/exercises/19_functions2.zig
new file mode 100644
index 0000000..4d195a7
--- /dev/null
+++ b/exercises/19_functions2.zig
@@ -0,0 +1,31 @@
+//
+// Now let's create a function that takes a parameter. Here's an
+// example that takes two parameters. As you can see, parameters
+// are declared just like an other types ("name": "type"):
+//
+// fn myFunction( number: u8, is_lucky: bool ) {
+// ...
+// }
+//
+const std = @import( "std" );
+
+pub fn main() void {
+ std.debug.print("Powers of two: {} {} {} {}\n", .{
+ twoToThe(1),
+ twoToThe(2),
+ twoToThe(3),
+ twoToThe(4),
+ });
+}
+
+//
+// Please give this function the correct input parameter(s).
+// You'll need to figure out the parameter name and type that we're
+// expecting. The output type has already been specified for you.
+//
+fn twoToThe(???) u32 {
+ return std.math.pow(u32, 2, my_number);
+ // std.math.pow(type, a, b) takes a numeric type and two numbers
+ // of that type and returns "a to the power of b" as that same
+ // numeric type.
+}
diff --git a/exercises/20_quiz3.zig b/exercises/20_quiz3.zig
new file mode 100644
index 0000000..e18ef37
--- /dev/null
+++ b/exercises/20_quiz3.zig
@@ -0,0 +1,45 @@
+//
+// Let's see if we can make use of some of things we've learned so far.
+// We'll create two functions: one that contains a "for" loop and one
+// that contains a "while" loop.
+//
+// Both of these are simply labeled "loop" below.
+//
+const std = @import( "std" );
+
+pub fn main() void {
+ const my_numbers = [4]u16{ 5,6,7,8 };
+
+ printPowersOfTwo(my_numbers);
+ std.debug.print("\n", .{});
+}
+
+//
+// You won't see this every day: a function that takes an array with
+// exactly four u16 numbers. This is not how you would normally pass
+// an array to a function. We'll learn about slices and pointers in
+// a little while. For now, we're using what we know.
+//
+// This function prints, but does not return anything.
+//
+fn printPowersOfTwo(numbers: [4]u16) ??? {
+ loop (numbers) |n| {
+ std.debug.print("{} ", .{twoToThe(n)});
+ }
+}
+
+//
+// This function bears a striking resemblance to twoToThe() in the last
+// exercise. But don't be fooled! This one does the math without the aid
+// of the standard library!
+//
+fn twoToThe(number: u16) ??? {
+ var n: u16 = 0;
+ var total: u16 = 1;
+
+ loop (n < number) : (n += 1) {
+ total *= 2;
+ }
+
+ return ???;
+}
diff --git a/exercises/21_errors.zig b/exercises/21_errors.zig
new file mode 100644
index 0000000..34c5e18
--- /dev/null
+++ b/exercises/21_errors.zig
@@ -0,0 +1,46 @@
+//
+// Believe it or not, sometimes things to wrong in programs.
+//
+// In Zig, an error is a value. Errors are named so we can identify
+// things that can go wrong. Errors are created in "error sets", which
+// are just a collection of named errors.
+//
+// We have the start of an error set, but we're missing the condition
+// "TooSmall". Please add it where needed!
+const MyNumberError = error{
+ TooBig,
+ ???,
+ TooFour,
+};
+
+const std = @import("std");
+
+pub fn main() void {
+ var nums = [_]u8{2,3,4,5,6};
+
+ for (nums) |n| {
+ std.debug.print("{}", .{n});
+
+ const number_error = numberFail(n);
+
+ if (number_error == MyNumberError.TooBig) {
+ std.debug.print(">4. ", .{});
+ }
+ if (???) {
+ std.debug.print("<4. ", .{});
+ }
+ if (number_error == MyNumberError.TooFour) {
+ std.debug.print("=4. ", .{});
+ }
+ }
+
+ std.debug.print("\n", .{});
+}
+
+// Notice how this function can return any member of the MyNumberError
+// error set.
+fn numberFail(n: u8) MyNumberError {
+ if(n > 4) return MyNumberError.TooBig;
+ if(n < 4) return MyNumberError.TooSmall; // <---- this one is free!
+ return MyNumberError.TooFour;
+}
diff --git a/exercises/22_errors2.zig b/exercises/22_errors2.zig
new file mode 100644
index 0000000..fcfd391
--- /dev/null
+++ b/exercises/22_errors2.zig
@@ -0,0 +1,30 @@
+//
+// A common case for errors is a situation where we're expecting to
+// have a value OR something has gone wrong. Take this example:
+//
+// var text: Text = getText('foo.txt');
+//
+// What happens if getText() can't find 'foo.txt'? How do we express
+// this in Zig?
+//
+// Zig let's us make what's called an "error union" which is a value
+// which could either be a regular value OR an error from a set:
+//
+// var text: MyErrorSet!Text = getText('foo.txt');
+//
+// For now, let's just see if we can try making an error union!
+//
+const std = @import("std");
+
+const MyNumberError = error{ TooSmall };
+
+pub fn main() void {
+ var my_number: ??? = 5;
+
+ // Looks like my_number will need to either store a number OR
+ // an error. Can you set the type correctly above?
+ my_number = MyNumberError.TooSmall;
+
+ std.debug.print("I compiled!", .{});
+}
+
diff --git a/exercises/23_errors3.zig b/exercises/23_errors3.zig
new file mode 100644
index 0000000..6060bf1
--- /dev/null
+++ b/exercises/23_errors3.zig
@@ -0,0 +1,28 @@
+//
+// One way to deal with error unions is to "catch" any error and
+// replace it with a default value.
+//
+// foo = canFail() catch 6;
+//
+// If canFail() fails, foo will equal 6.
+//
+const std = @import("std");
+
+const MyNumberError = error{ TooSmall };
+
+pub fn main() void {
+ var a: u32 = addTwenty(44) catch 22;
+ var b: u32 = addTwenty(4) ??? 22;
+
+ std.debug.print("a={}, b={}", .{a,b});
+}
+
+// Please provide the return type from this function.
+// Hint: it'll be an error union.
+fn addTwenty(n: u32) ??? {
+ if (n < 5) {
+ return MyNumberError.TooSmall;
+ } else {
+ return n + 20;
+ }
+}
diff --git a/exercises/24_errors4.zig b/exercises/24_errors4.zig
new file mode 100644
index 0000000..b60cc2d
--- /dev/null
+++ b/exercises/24_errors4.zig
@@ -0,0 +1,68 @@
+//
+// Using `catch` to replace an error with a default value is a bit
+// of a blunt instrument since it doesn't matter what the error is.
+//
+// Catch lets us capture the error value and perform additional
+// actions with this form:
+//
+// canFail() catch |err| {
+// if (err == FishError.TunaMalfunction) {
+// ...
+// }
+// };
+//
+const std = @import("std");
+
+const MyNumberError = error{
+ TooSmall,
+ TooBig,
+};
+
+pub fn main() void {
+ // The "catch 0" below is just our way of dealing with the fact
+ // that makeJustRight() returns a error union (for now).
+ var a: u32 = makeJustRight(44) catch 0;
+ var b: u32 = makeJustRight(14) catch 0;
+ var c: u32 = makeJustRight(4) catch 0;
+
+ std.debug.print("a={}, b={}, c={}", .{a,b,c});
+}
+
+// In this silly example we've split the responsibility of making
+// a number just right into four (!) functions:
+//
+// makeJustRight() Calls fixTooBig(), cannot fix any errors.
+// fixTooBig() Calls fixTooSmall(), fixes TooBig errors.
+// fixTooSmall() Calls detectProblems(), fixes TooSmall errors.
+// detectProblems() Returns the number or an error.
+//
+fn makeJustRight(n: u32) MyNumberError!u32 {
+ return fixTooBig(n) catch |err| { return err; };
+}
+
+fn fixTooBig(n: u32) MyNumberError!u32 {
+ return fixTooSmall(n) catch |err| {
+ if (err == MyNumberError.TooBig) {
+ return 20;
+ }
+
+ return err;
+ };
+}
+
+fn fixTooSmall(n: u32) MyNumberError!u32 {
+ // Oh dear, this is missing a lot! But don't worry, it's nearly
+ // identical to fixTooBig() above.
+ //
+ // If we get a TooSmall error, we should return 10.
+ // If we get any other error, we should return that error.
+ // Otherwise, we return the u32 number.
+ return detectProblems(n) ???
+}
+
+fn detectProblems(n: u32) MyNumberError!u32 {
+ if (n < 10) return MyNumberError.TooSmall;
+ if (n > 20) return MyNumberError.TooBig;
+ return n;
+}
+
diff --git a/exercises/25_errors5.zig b/exercises/25_errors5.zig
new file mode 100644
index 0000000..d9e9ce1
--- /dev/null
+++ b/exercises/25_errors5.zig
@@ -0,0 +1,40 @@
+//
+// Zig has a handy "try" shortcut for this common error handling pattern:
+//
+// canFail() catch |err| return err;
+//
+// which can be more compactly written as:
+//
+// try canFail();
+//
+const std = @import("std");
+
+const MyNumberError = error{
+ TooSmall,
+ TooBig,
+};
+
+pub fn main() void {
+ var a: u32 = addFive(44) catch 0;
+ var b: u32 = addFive(14) catch 0;
+ var c: u32 = addFive(4) catch 0;
+
+ std.debug.print("a={}, b={}, c={}", .{a,b,c});
+}
+
+fn addFive(n: u32) MyNumberError!u32 {
+ //
+ // This function needs to return any error which might come back from fix().
+ // Please use a "try" statement rather than a "catch".
+ //
+ var x = detect(n);
+
+ return x + 5;
+}
+
+fn detect(n: u32) MyNumberError!u32 {
+ if (n < 10) return MyNumberError.TooSmall;
+ if (n > 20) return MyNumberError.TooBig;
+ return n;
+}
+
diff --git a/exercises/26_hello2.zig b/exercises/26_hello2.zig
new file mode 100644
index 0000000..237d27c
--- /dev/null
+++ b/exercises/26_hello2.zig
@@ -0,0 +1,23 @@
+//
+// Great news! Now we know enough to understand a "real" Hello World
+// program in Zig - one that uses the system Standard Out resource...which
+// can fail!
+//
+const std = @import("std");
+
+// Take note that this main() definition now returns "!void" rather
+// than just "void". Since there's no specific error type, this means
+// that Zig will infer the error type. This is appropriate in the case
+// of main(), but can have consequences elsewhere.
+pub fn main() !void {
+
+ // We get a Writer for Standard Out so we can print() to it.
+ const stdout = std.io.getStdOut().writer();
+
+ // Unlike std.debug.print(), the Standard Out writer can fail
+ // with an error. We don't care _what_ the error is, we want
+ // to be able to pass it up as a return value of main().
+ //
+ // We just learned of a single statement which can accomplish this.
+ stdout.print("Hello world!\n", .{});
+}
diff --git a/exercises/27_defer.zig b/exercises/27_defer.zig
new file mode 100644
index 0000000..b41e2af
--- /dev/null
+++ b/exercises/27_defer.zig
@@ -0,0 +1,25 @@
+//
+// You can assign some code to run _after_ a block of code exits by
+// deferring it with a "defer" statement:
+//
+// {
+// defer runLater();
+// runNow();
+// }
+//
+// In the example above, runLater() will run when the block ({...})
+// is finished. So the code above will run in the following order:
+//
+// runNow();
+// runLater();
+//
+// This feature seems strange at first, but we'll see how it could be
+// useful in the next exercise.
+const std = @import("std");
+
+pub fn main() void {
+ // Without changing anything else, please add a 'defer' statement
+ // to this code so that our program prints "One Two\n":
+ std.debug.print("Two\n", .{});
+ std.debug.print("One ", .{});
+}
diff --git a/exercises/28_defer2.zig b/exercises/28_defer2.zig
new file mode 100644
index 0000000..5c991da
--- /dev/null
+++ b/exercises/28_defer2.zig
@@ -0,0 +1,29 @@
+//
+// Now that you know how "defer" works, let's do something more
+// interesting with it.
+//
+const std = @import("std");
+
+pub fn main() void {
+ const animals = [_]u8{ 'g', 'c', 'd', 'd', 'g', 'z' };
+
+ for (animals) |a| printAnimal(a);
+
+
+ std.debug.print("done.\n", .{});
+}
+
+// This function is _supposed_ to print an animal name in parentheses
+// like "(Goat) ", but we somehow need to print the end parenthesis
+// even though this function can return in four different places!
+fn printAnimal(animal: u8) void {
+ std.debug.print("(", .{});
+
+ std.debug.print(") ", .{}); // <---- how!?
+
+ if (animal == 'g'){ std.debug.print("Goat", .{}); return; }
+ if (animal == 'c'){ std.debug.print("Cat", .{}); return; }
+ if (animal == 'd'){ std.debug.print("Dog", .{}); return; }
+
+ std.debug.print("Unknown", .{});
+}
diff --git a/exercises/29_errdefer.zig b/exercises/29_errdefer.zig
new file mode 100644
index 0000000..cd2158d
--- /dev/null
+++ b/exercises/29_errdefer.zig
@@ -0,0 +1,60 @@
+//
+// Another common problem is a block of code that could exit in multiple
+// places due to an error - but that needs to run do something before it
+// exits (typically to clean up after itself).
+//
+// An "errdefer" is a defer that only runs if the block exits with an error:
+//
+// {
+// errdefer cleanup();
+// try canFail();
+// }
+//
+// The cleanup() function is called ONLY if the "try" statement returns an
+// error produced by canFail().
+//
+const std = @import("std");
+
+//
+var counter: u32 = 0;
+
+const MyErr = error{ GetFail, IncFail };
+
+pub fn main() void {
+ // We simply quit the entire program if we fail to get a number:
+ var a: u32 = makeNumber() catch return;
+ var b: u32 = makeNumber() catch return;
+
+ std.debug.print("Numbers: {}, {}\n", .{a,b});
+}
+
+fn makeNumber() MyErr!u32 {
+ std.debug.print("Getting number...", .{});
+
+ // Please make the "failed" message print ONLY if the makeNumber()
+ // function exits with an error:
+ std.debug.print("failed!\n", .{});
+
+ var num = try getNumber(); // <-- This could fail!
+
+ num = try increaseNumber(num); // <-- This could ALSO fail!
+
+ std.debug.print("got {}. ", .{num});
+
+ return num;
+}
+
+fn getNumber() MyErr!u32 {
+ // I _could_ fail...but I don't!
+ return 4;
+}
+
+fn increaseNumber(n: u32) MyErr!u32 {
+ // I fail after the first time you run me!
+ if (counter > 0) return MyErr.IncFail;
+
+ // Sneaky, weird global stuff.
+ counter += 1;
+
+ return n + 1;
+}
diff --git a/exercises/30_switch.zig b/exercises/30_switch.zig
new file mode 100644
index 0000000..b10ad14
--- /dev/null
+++ b/exercises/30_switch.zig
@@ -0,0 +1,55 @@
+//
+// The "switch" statement lets you match the possible values of an
+// expression and perform a different action for each.
+//
+// This switch:
+//
+// switch (players) {
+// 1 => startOnePlayerGame(),
+// 2 => startTwoPlayerGame(),
+// else => {
+// alert();
+// return GameError.TooManyPlayers;
+// }
+// }
+//
+// Is equivalent to this if/else:
+//
+// if (players == 1) startOnePlayerGame();
+// else if (players == 2) startTwoPlayerGame();
+// else {
+// alert();
+// return GameError.TooManyPlayers;
+// }
+//
+//
+//
+const std = @import("std");
+
+pub fn main() void {
+ const lang_chars = [_]u8{ 26, 9, 7, 42 };
+
+ for (lang_chars) |c| {
+ switch (c) {
+ 1 => std.debug.print("A", .{}),
+ 2 => std.debug.print("B", .{}),
+ 3 => std.debug.print("C", .{}),
+ 4 => std.debug.print("D", .{}),
+ 5 => std.debug.print("E", .{}),
+ 6 => std.debug.print("F", .{}),
+ 7 => std.debug.print("G", .{}),
+ 8 => std.debug.print("H", .{}),
+ 9 => std.debug.print("I", .{}),
+ 10 => std.debug.print("J", .{}),
+ // ... we don't need everything in between ...
+ 25 => std.debug.print("Y", .{}),
+ 26 => std.debug.print("Z", .{}),
+ // Switch statements must be "exhaustive" (there must be a
+ // match for every possible value). Please add an "else"
+ // to this switch to print a question mark "?" when c is
+ // not one of the existing matches.
+ }
+ }
+
+ std.debug.print("\n", .{});
+}
diff --git a/exercises/31_switch2.zig b/exercises/31_switch2.zig
new file mode 100644
index 0000000..138b809
--- /dev/null
+++ b/exercises/31_switch2.zig
@@ -0,0 +1,42 @@
+//
+// What's really nice is that you can use a switch statement as an
+// expression to return a value.
+//
+// var a = switch (x) {
+// 1 => 9,
+// 2 => 16,
+// 3 => 7,
+// ...
+// }
+//
+const std = @import("std");
+
+pub fn main() void {
+ const lang_chars = [_]u8{ 26, 9, 7, 42 };
+
+ for (lang_chars) |c| {
+ var real_char: u8 = switch (c) {
+ 1 => 'A',
+ 2 => 'B',
+ 3 => 'C',
+ 4 => 'D',
+ 5 => 'E',
+ 6 => 'F',
+ 7 => 'G',
+ 8 => 'H',
+ 9 => 'I',
+ 10 => 'J',
+ // ...
+ 25 => 'Y',
+ 26 => 'Z',
+ // As in the last exercise, please add the "else" clause
+ // and this time, have it return an exclamation mark "!".
+ };
+
+ std.debug.print("{c}", .{real_char});
+ // Note: "{c}" forces print() to display the value as a character.
+ // Can you guess what happens if you remove the "c"? Try it!
+ }
+
+ std.debug.print("\n", .{});
+}
diff --git a/exercises/32_unreachable.zig b/exercises/32_unreachable.zig
new file mode 100644
index 0000000..c81efac
--- /dev/null
+++ b/exercises/32_unreachable.zig
@@ -0,0 +1,38 @@
+//
+// Zig has an "unreachable" statement. Use it when you want to tell the
+// compiler that a branch of code should never be executed and that the
+// mere act of reaching it is an error.
+//
+// if (true) {
+// ...
+// } else {
+// unreachable;
+// }
+//
+// Here we've made a little virtual machine that performs mathematical
+// operations on a single numeric value. It looks great but there's one
+// little problem: the switch statement doesn't cover every possible
+// value of a u8 number!
+//
+// WE know there are only three operations but Zig doesn't. Use the
+// unreachable statement to make the switch complete. Or ELSE. :-)
+//
+const std = @import("std");
+
+pub fn main() void {
+ const operations = [_]u8{ 1, 1, 1, 3, 2, 2 };
+
+ var current_value: u32 = 0;
+
+ for (operations) |op| {
+ switch (op) {
+ 1 => { current_value += 1; },
+ 2 => { current_value -= 1; },
+ 3 => { current_value *= current_value; },
+ }
+
+ std.debug.print("{} ", .{current_value});
+ }
+
+ std.debug.print("\n", .{});
+}
diff --git a/exercises/33_iferror.zig b/exercises/33_iferror.zig
new file mode 100644
index 0000000..ed92e94
--- /dev/null
+++ b/exercises/33_iferror.zig
@@ -0,0 +1,49 @@
+//
+// Let's revisit the very first error exercise. This time, we're going to
+// look at a special error-handling type of the "if" statement.
+//
+// if (foo) |value| {
+//
+// // foo was NOT an error; value is the non-error value of foo
+//
+// } else |err| {
+//
+// // foo WAS an error; err is the error value of foo
+//
+// }
+//
+// We'll take it even further and use a switch statement to handle
+// the error types.
+//
+const MyNumberError = error{
+ TooBig,
+ TooSmall,
+};
+
+const std = @import("std");
+
+pub fn main() void {
+ var nums = [_]u8{2,3,4,5,6};
+
+ for (nums) |num| {
+ std.debug.print("{}", .{num});
+
+ var n = numberMaybeFail(num);
+ if (n) |value| {
+ std.debug.print("=4. ", .{});
+ } else |err| switch (err) {
+ MyNumberError.TooBig => std.debug.print(">4. ", .{}),
+ // Please add a match for TooSmall here and have it print: "<4. "
+ }
+ }
+
+ std.debug.print("\n", .{});
+}
+
+// This time we'll have numberMaybeFail() return an error union rather
+// than a straight error.
+fn numberMaybeFail(n: u8) MyNumberError!u8 {
+ if(n > 4) return MyNumberError.TooBig;
+ if(n < 4) return MyNumberError.TooSmall;
+ return n;
+}
diff --git a/exercises/34_quiz4.zig b/exercises/34_quiz4.zig
new file mode 100644
index 0000000..43734b7
--- /dev/null
+++ b/exercises/34_quiz4.zig
@@ -0,0 +1,24 @@
+//
+// Quiz time. See if you can make this program work!
+//
+// Solve this any way you like, just be sure the output is:
+//
+// my_num=42
+//
+const std = @import("std");
+
+const NumError = error{ IllegalNumber };
+
+pub fn main() void {
+ const stdout = std.io.getStdOut().writer();
+
+ const my_num: u32 = getNumber();
+
+ try stdout.print("my_num={}\n", .{my_num});
+}
+
+// Just don't modify this function. It's "perfect" the way it is. :-)
+fn getNumber() NumError!u32 {
+ if( false ) return NumError.IllegalNumber;
+ return 42;
+}
diff --git a/exercises/35_enums.zig b/exercises/35_enums.zig
new file mode 100644
index 0000000..cf455a4
--- /dev/null
+++ b/exercises/35_enums.zig
@@ -0,0 +1,49 @@
+//
+// Remember that little mathematical virtual machine we made using the
+// "unreachable" statement? Well, there were two problems with the
+// way we were using op codes:
+//
+// 1. Having to remember op codes by number is no good.
+// 2. We had to use "unreachable" because Zig had no way of knowing
+// how many valid op codes there were.
+//
+// An "enum" is a Zig construct that lets you give names to numeric
+// values and store them in a set. They look a lot like error sets:
+//
+// const Fruit = enum{ apple, pear, orange };
+//
+// const my_fruit = Fruit.apple;
+//
+// Let's use an enum in place of the numbers we were using in the
+// previous version!
+//
+const std = @import("std");
+
+// Please complete the enum!
+const Ops = enum{ ??? };
+
+pub fn main() void {
+ const operations = [_]Ops{
+ Ops.inc,
+ Ops.inc,
+ Ops.inc,
+ Ops.pow,
+ Ops.dec,
+ Ops.dec
+ };
+
+ var current_value: u32 = 0;
+
+ for (operations) |op| {
+ switch (op) {
+ Ops.inc => { current_value += 1; },
+ Ops.dec => { current_value -= 1; },
+ Ops.pow => { current_value *= current_value; },
+ // No "else" needed! Why is that?
+ }
+
+ std.debug.print("{} ", .{current_value});
+ }
+
+ std.debug.print("\n", .{});
+}
diff --git a/exercises/36_enums2.zig b/exercises/36_enums2.zig
new file mode 100644
index 0000000..2e04415
--- /dev/null
+++ b/exercises/36_enums2.zig
@@ -0,0 +1,61 @@
+//
+// Enums are really just a set of numbers. You can leave the
+// numbering up to the compiler, or you can assign them
+// explicitly. You can even specify the numeric type used.
+//
+// const Stuff = enum(u8){ foo = 16 };
+//
+// You can get the integer out with a built-in function:
+//
+// var my_stuff: u8 = @enumToInt(Stuff.foo);
+//
+// Note how that built-in function starts with "@" just like the
+// @import() function we've been using.
+//
+const std = @import("std");
+
+// Zig lets us write integers in hexadecimal format:
+//
+// 0xf (is the value 15 in hex)
+//
+// Web browsers let us specify colors using a hexadecimal
+// number where each byte represents the brightness of the
+// Red, Green, or Blue component (RGB) where two hex digits
+// are one byte with a value range of 0-255:
+//
+// #RRGGBB
+//
+// Please define and use a pure blue value Color:
+const Color = enum(u32){
+ red = 0xff0000,
+ green = 0x00ff00,
+ blue = ???,
+};
+
+pub fn main() void {
+ // Remeber Zig's multi-line strings? Here they are again.
+ // Also, check out this cool format string:
+ //
+ // {x:0>6}
+ // ^
+ // x type ('x' is lower-case hexadecimal)
+ // : separator (needed for format syntax)
+ // 0 padding character (default is ' ')
+ // > alignment ('>' aligns right)
+ // 6 width (use padding to force width)
+ //
+ // Please add this formatting to the blue value.
+ // (Even better, experiment without it, or try parts of it
+ // to see what prints!)
+ std.debug.print(
+ \\<p>
+ \\ <span style="color: #{x:0>6}">Red</span>
+ \\ <span style="color: #{x:0>6}">Green</span>
+ \\ <span style="color: #{}">Blue</span>
+ \\</p>
+ , .{
+ @enumToInt(Color.red),
+ @enumToInt(Color.green),
+ @enumToInt(???), // Oops! We're missing something!
+ });
+}
diff --git a/exercises/37_structs.zig b/exercises/37_structs.zig
new file mode 100644
index 0000000..dd4b633
--- /dev/null
+++ b/exercises/37_structs.zig
@@ -0,0 +1,59 @@
+//
+// Being able to group values together lets us turn this:
+//
+// point1_x = 3;
+// point1_y = 16;
+// point1_z = 27;
+// point2_x = 7;
+// point2_y = 13;
+// point2_z = 34;
+//
+// into this:
+//
+// point1 = Point{ .x=3, .y=16, .y=27 };
+// point2 = Point{ .x=7, .y=13, .y=34 };
+//
+// The Point above is an example of a "struct" (short for "structure").
+// Here's how it could have been defined:
+//
+// const Point = struct{ x: u32, y: u32, z: u32 };
+//
+// Let's store something fun with a struct: a roleplaying character!
+//
+const std = @import("std");
+
+// We'll use an enum to specify the character class.
+const Class = enum{
+ wizard,
+ thief,
+ bard,
+ warrior,
+};
+
+// Please add a new property to this struct called "health" and make
+// it a u8 integer type.
+const Character = struct{
+ class: Class,
+ gold: u32,
+ experience: u32,
+};
+
+pub fn main() void {
+ // Please initialize Glorp with 100 health.
+ var glorp_the_wise = Character{
+ .class = Class.wizard,
+ .gold = 20,
+ .experience = 10,
+ };
+
+ // Glorp gains some gold.
+ glorp_the_wise.gold += 5;
+
+ // Ouch! Glorp takes a punch!
+ glorp_the_wise.health -= 10;
+
+ std.debug.print("Your wizard has {} health and {} gold.", .{
+ glorp_the_wise.health,
+ glorp_the_wise.gold
+ });
+}
diff --git a/exercises/38_structs2.zig b/exercises/38_structs2.zig
new file mode 100644
index 0000000..b6def93
--- /dev/null
+++ b/exercises/38_structs2.zig
@@ -0,0 +1,51 @@
+//
+// Grouping values in structs is not merely convenient. It also allows
+// us to treat the values as a single item when storing them, passing
+// them to functions, etc.
+//
+// This exercise demonstrates how we can store structs in an array and
+// how doing so lets us print them all (both) using a loop.
+//
+const std = @import("std");
+
+const Class = enum{
+ wizard,
+ thief,
+ bard,
+ warrior,
+};
+
+const Character = struct{
+ class: Class,
+ gold: u32,
+ health: u8,
+ experience: u32,
+};
+
+pub fn main() void {
+ var chars: [2]Character = undefined;
+
+ // Glorp the Wise
+ chars[0] = Character{
+ .class = Class.wizard,
+ .gold = 20,
+ .health = 100,
+ .experience = 10,
+ };
+
+ // Please add "Zump the Loud" with the following properties:
+ //
+ // class bard
+ // gold 10
+ // health 100
+ // experience 20
+ //
+ // Feel free to run this program without adding Zump. What does
+ // it do and why?
+
+ // Printing all RPG characters in a loop:
+ for (chars) |c, num| {
+ std.debug.print("Character {} - G:{} H:{} XP:{}\n",
+ .{num+1, c.gold, c.health, c.experience});
+ }
+}
diff --git a/exercises/39_pointers.zig b/exercises/39_pointers.zig
new file mode 100644
index 0000000..25b56c6
--- /dev/null
+++ b/exercises/39_pointers.zig
@@ -0,0 +1,36 @@
+//
+// Check this out:
+//
+// var foo: u8 = 5; // foo is 5
+// var bar: *u8 = &foo; // bar is a pointer
+//
+// What is a pointer? It's a reference to a value. In this example
+// bar is a reference to the memory space that current contains the
+// value 5.
+//
+// A cheatsheet given the above declarations:
+//
+// u8 the type of a u8 value
+// foo the value 5
+// *u8 the type of a pointer to a u8 value
+// &foo a reference to foo
+// bar a pointer to the value at foo
+// bar.* the value 5 (the dereferenced value "at" bar)
+//
+// We'll see why pointers are useful in a moment. For now, see if you
+// can make this example work!
+//
+const std = @import("std");
+
+pub fn main() void {
+ var num1: u8 = 5;
+ var num1_pointer: *u8 = &num1;
+
+ var num2: u8 = undefined;
+
+ // Please make num2 equal 5 using num1_pointer!
+ // (See the "cheatsheet" above for ideas.)
+ num2 = ???;
+
+ std.debug.print("num1: {}, num2: {}\n", .{num1, num2});
+}
diff --git a/exercises/40_pointers2.zig b/exercises/40_pointers2.zig
new file mode 100644
index 0000000..b046dc1
--- /dev/null
+++ b/exercises/40_pointers2.zig
@@ -0,0 +1,27 @@
+//
+// It's important to note that variable pointers and constant pointers
+// are different types.
+//
+// Given:
+//
+// var foo: u8 = 5;
+// const bar: u8 = 5;
+//
+// Then:
+//
+// &foo is of type "*u8"
+// &bar is of type "*const u8"
+//
+// You can always make a constant pointer to a variable, but you cannot
+// make a variable pointer to a constant. This sounds like a logic puzzle,
+// but it just means that once data is declared immutable, you can't
+// coerce it to a mutable type. It's a safety thing (to prevent mistakes).
+//
+const std = @import("std");
+
+pub fn main() void {
+ const a: u8 = 12;
+ const b: *u8 = &a; // fix this!
+
+ std.debug.print("a: {}, b: {}\n", .{a, b.*});
+}
diff --git a/exercises/41_pointers3.zig b/exercises/41_pointers3.zig
new file mode 100644
index 0000000..21a43bd
--- /dev/null
+++ b/exercises/41_pointers3.zig
@@ -0,0 +1,41 @@
+//
+// The tricky part is that the pointer's mutability (var vs const) refers
+// to the ability to change what the pointer POINTS TO, not the ability
+// to change the VALUE at that location!
+//
+// const locked: u8 = 5;
+// var unlocked: u8 = 10;
+//
+// const p1: *const u8 = &locked;
+// var p2: *const u8 = &locked;
+//
+// Both p1 and p2 point to constant values which cannot change. However,
+// p2 can be changed to point to something else and p1 cannot!
+//
+// const p3: *u8 = &unlocked;
+// var p4: *u8 = &unlocked;
+// const p5: *const u8 = &unlocked;
+// var p6: *const u8 = &unlocked;
+//
+// Here p3 and p4 can both be used to change the value they point to but
+// p3 cannot point at anything else.
+// What's interesting is that p5 and p6 act like p1 and p2, but point to
+// the value at "unlocked". This is what we mean when we say that we can
+// make a constant reference to any value!
+//
+const std = @import("std");
+
+pub fn main() void {
+ var foo: u8 = 5;
+ var bar: u8 = 10;
+
+ // Please define pointer "p" so that it can point to EITHER foo or
+ // bar AND change the value it points to!
+ ??? p: ??? = undefined;
+
+ p = &foo;
+ p.* += 1;
+ p = &bar;
+ p.* += 1;
+ std.debug.print("foo={}, bar={}\n", .{foo, bar});
+}
diff --git a/exercises/42_pointers4.zig b/exercises/42_pointers4.zig
new file mode 100644
index 0000000..e6b8964
--- /dev/null
+++ b/exercises/42_pointers4.zig
@@ -0,0 +1,33 @@
+//
+// Now let's use pointers to do something we haven't been
+// able to do before: pass a value by reference to a function!
+//
+const std = @import("std");
+
+pub fn main() void {
+ var num: u8 = 1;
+ var more_nums = [_]u8{ 1, 1, 1, 1 };
+
+ // Let's pass a reference to num to our function and print it:
+ makeFive(&num);
+ std.debug.print("num: {}, ", .{num});
+
+
+ // Now something interesting. Let's pass a reference to a
+ // specific array value:
+ makeFive(&more_nums[2]);
+
+ // And print the array:
+ std.debug.print("more_nums: ", .{});
+ for (more_nums) |n| {
+ std.debug.print("{} ", .{n});
+ }
+
+ std.debug.print("\n", .{});
+}
+
+// This function should take a reference to a u8 value and set it
+// to 5.
+fn makeFive(x: *u8) void {
+ ??? = 5; // fix me!
+}
diff --git a/exercises/43_pointers5.zig b/exercises/43_pointers5.zig
new file mode 100644
index 0000000..adfaea1
--- /dev/null
+++ b/exercises/43_pointers5.zig
@@ -0,0 +1,84 @@
+//
+// Passing integer pointers around is generally not something you're going
+// to do. Integers are cheap to copy.
+//
+// But you know what IS useful? Pointers to structs:
+//
+// const Vertex = struct{ x: u32, y: u32, z: u32 };
+//
+// var v1 = Vertex{ .x=3, .y=2, .z=5 };
+//
+// var pv: *Vertex = &v1; // <-- a pointer to our struct
+//
+// Note that you don't need to dereference the "pv" pointer to access
+// the struct's fields:
+//
+// YES: pv.x
+// NO: pv.*.x
+//
+// We can write functions that take pointer arguments:
+//
+// fn foo(v: *Vertex) void {
+// v.x += 2;
+// v.y += 3;
+// v.z += 7;
+// }
+//
+// And pass references to them:
+//
+// foo(&v1);
+//
+//
+// Let's revisit our RPG example and make a printCharacter() function
+// that takes a Character pointer.
+//
+const std = @import("std");
+
+const Class = enum{
+ wizard,
+ thief,
+ bard,
+ warrior,
+};
+
+const Character = struct{
+ class: Class,
+ gold: u32,
+ health: u8,
+ experience: u32,
+};
+
+pub fn main() void {
+ var glorp = Character{
+ .class = Class.wizard,
+ .gold = 10,
+ .health = 100,
+ .experience = 20,
+ };
+
+ // FIX ME!
+ // Please pass our Character "glorp" to printCharacter():
+ printCharacter( ??? );
+}
+
+
+// Note how this function's "c" parameter is a pointer to a Character struct.
+fn printCharacter(c: *Character) void {
+
+ // Here's something you haven't seen before: when switching an enum, you
+ // don't have to write the full enum name. Zig understands that ".wizard"
+ // means "Class.wizard" when we switch on a Class enum value:
+ const class_name = switch (c.class) {
+ .wizard => "Wizard",
+ .thief => "Thief",
+ .bard => "Bard",
+ .warrior => "Warrior",
+ };
+
+ std.debug.print("{s} (G:{} H:{} XP:{})", .{
+ class_name,
+ c.gold,
+ c.health,
+ c.experience,
+ });
+}