221V 20 hours ago
parent
commit
9c54f9f2ed
1 changed files with 283 additions and 282 deletions
  1. 283 282
      exercises/058_quiz7.zig

+ 283 - 282
exercises/058_quiz7.zig

@@ -50,9 +50,9 @@ const TripError = error{ Unreachable, EatenByAGrue };
 // assign the paths later. And why is that? Because paths contain
 // assign the paths later. And why is that? Because paths contain
 // pointers to places and assigning them now would create a dependency
 // pointers to places and assigning them now would create a dependency
 // loop!
 // loop!
-const Place = struct {
-    name: []const u8,
-    paths: []const Path = undefined,
+const Place = struct{
+  name: []const u8,
+  paths: []const Path = undefined,
 };
 };
 
 
 var a = Place{ .name = "Archer's Point" };
 var a = Place{ .name = "Archer's Point" };
@@ -93,10 +93,10 @@ const place_count = 6;
 
 
 // Now let's create all of the paths between sites. A path goes from
 // Now let's create all of the paths between sites. A path goes from
 // one place to another and has a distance.
 // one place to another and has a distance.
-const Path = struct {
-    from: *const Place,
-    to: *const Place,
-    dist: u8,
+const Path = struct{
+  from: *const Place,
+  to: *const Place,
+  dist: u8,
 };
 };
 
 
 // By the way, if the following code seems like a lot of tedious
 // By the way, if the following code seems like a lot of tedious
@@ -105,97 +105,97 @@ const Path = struct {
 // code (much like macros in other languages), but we haven't learned
 // code (much like macros in other languages), but we haven't learned
 // how to do that yet!
 // how to do that yet!
 const a_paths = [_]Path{
 const a_paths = [_]Path{
-    Path{
-        .from = &a, // from: Archer's Point
-        .to = &b, //   to: Bridge
-        .dist = 2,
-    },
+  Path{
+    .from = &a, // from: Archer's Point
+    .to = &b, //   to: Bridge
+    .dist = 2,
+  },
 };
 };
 
 
 const b_paths = [_]Path{
 const b_paths = [_]Path{
-    Path{
-        .from = &b, // from: Bridge
-        .to = &a, //   to: Archer's Point
-        .dist = 2,
-    },
-    Path{
-        .from = &b, // from: Bridge
-        .to = &d, //   to: Dogwood Grove
-        .dist = 1,
-    },
+  Path{
+    .from = &b, // from: Bridge
+    .to = &a, //   to: Archer's Point
+    .dist = 2,
+  },
+  Path{
+    .from = &b, // from: Bridge
+    .to = &d, //   to: Dogwood Grove
+    .dist = 1,
+  },
 };
 };
 
 
 const c_paths = [_]Path{
 const c_paths = [_]Path{
-    Path{
-        .from = &c, // from: Cottage
-        .to = &d, //   to: Dogwood Grove
-        .dist = 3,
-    },
-    Path{
-        .from = &c, // from: Cottage
-        .to = &e, //   to: East Pond
-        .dist = 2,
-    },
+  Path{
+    .from = &c, // from: Cottage
+    .to = &d, //   to: Dogwood Grove
+    .dist = 3,
+  },
+  Path{
+    .from = &c, // from: Cottage
+    .to = &e, //   to: East Pond
+    .dist = 2,
+  },
 };
 };
 
 
 const d_paths = [_]Path{
 const d_paths = [_]Path{
-    Path{
-        .from = &d, // from: Dogwood Grove
-        .to = &b, //   to: Bridge
-        .dist = 1,
-    },
-    Path{
-        .from = &d, // from: Dogwood Grove
-        .to = &c, //   to: Cottage
-        .dist = 3,
-    },
-    Path{
-        .from = &d, // from: Dogwood Grove
-        .to = &f, //   to: Fox Pond
-        .dist = 7,
-    },
+  Path{
+    .from = &d, // from: Dogwood Grove
+    .to = &b, //   to: Bridge
+    .dist = 1,
+  },
+  Path{
+    .from = &d, // from: Dogwood Grove
+    .to = &c, //   to: Cottage
+    .dist = 3,
+  },
+  Path{
+    .from = &d, // from: Dogwood Grove
+    .to = &f, //   to: Fox Pond
+    .dist = 7,
+  },
 };
 };
 
 
 const e_paths = [_]Path{
 const e_paths = [_]Path{
-    Path{
-        .from = &e, // from: East Pond
-        .to = &c, //   to: Cottage
-        .dist = 2,
-    },
-    Path{
-        .from = &e, // from: East Pond
-        .to = &f, //   to: Fox Pond
-        .dist = 1, // (one-way down a short waterfall!)
-    },
+  Path{
+    .from = &e, // from: East Pond
+    .to = &c, //   to: Cottage
+    .dist = 2,
+  },
+  Path{
+    .from = &e, // from: East Pond
+    .to = &f, //   to: Fox Pond
+    .dist = 1, // (one-way down a short waterfall!)
+  },
 };
 };
 
 
 const f_paths = [_]Path{
 const f_paths = [_]Path{
-    Path{
-        .from = &f, // from: Fox Pond
-        .to = &d, //   to: Dogwood Grove
-        .dist = 7,
-    },
+  Path{
+    .from = &f, // from: Fox Pond
+    .to = &d, //   to: Dogwood Grove
+    .dist = 7,
+  },
 };
 };
 
 
 // Once we've plotted the best course through the woods, we'll make a
 // Once we've plotted the best course through the woods, we'll make a
 // "trip" out of it. A trip is a series of Places connected by Paths.
 // "trip" out of it. A trip is a series of Places connected by Paths.
 // We use a TripItem union to allow both Places and Paths to be in the
 // We use a TripItem union to allow both Places and Paths to be in the
 // same array.
 // same array.
-const TripItem = union(enum) {
-    place: *const Place,
-    path: *const Path,
-
-    // This is a little helper function to print the two different
-    // types of item correctly.
-    fn printMe(self: TripItem) void {
-        switch (self) {
-            // Oops! The hermit forgot how to capture the union values
-            // in a switch statement. Please capture each value as
-            // 'p' so the print statements work!
-            .place => print("{s}", .{p.name}),
-            .path => print("--{}->", .{p.dist}),
-        }
+const TripItem = union(enum){
+  place: *const Place,
+  path: *const Path,
+  
+  // This is a little helper function to print the two different
+  // types of item correctly.
+  fn printMe(self: TripItem) void{
+    switch(self){
+      // Oops! The hermit forgot how to capture the union values
+      // in a switch statement. Please capture each value as
+      // 'p' so the print statements work!
+      .place => print("{s}", .{self.place.*.name}),
+      .path => print("--{}->", .{self.path.*.dist}),
     }
     }
+  }
 };
 };
 
 
 // The Hermit's Notebook is where all the magic happens. A notebook
 // The Hermit's Notebook is where all the magic happens. A notebook
@@ -204,11 +204,11 @@ const TripItem = union(enum) {
 // find a better Path to reach a Place (shorter distance), we update the
 // find a better Path to reach a Place (shorter distance), we update the
 // entry. Entries also serve as a "todo" list which is how we keep
 // entry. Entries also serve as a "todo" list which is how we keep
 // track of which paths to explore next.
 // track of which paths to explore next.
-const NotebookEntry = struct {
-    place: *const Place,
-    coming_from: ?*const Place,
-    via_path: ?*const Path,
-    dist_to_reach: u16,
+const NotebookEntry = struct{
+  place: *const Place,
+  coming_from: ?*const Place,
+  via_path: ?*const Path,
+  dist_to_reach: u16,
 };
 };
 
 
 // +------------------------------------------------+
 // +------------------------------------------------+
@@ -223,201 +223,201 @@ const NotebookEntry = struct {
 // |                      ...                       |
 // |                      ...                       |
 // +---+----------------+----------------+----------+
 // +---+----------------+----------------+----------+
 //
 //
-const HermitsNotebook = struct {
-    // Remember the array repetition operator `**`? It is no mere
-    // novelty, it's also a great way to assign multiple items in an
-    // array without having to list them one by one. Here we use it to
-    // initialize an array with null values.
-    entries: [place_count]?NotebookEntry = .{null} ** place_count,
-
-    // The next entry keeps track of where we are in our "todo" list.
-    next_entry: u8 = 0,
-
-    // Mark the start of empty space in the notebook.
-    end_of_entries: u8 = 0,
-
-    // We'll often want to find an entry by Place. If one is not
-    // found, we return null.
-    fn getEntry(self: *HermitsNotebook, place: *const Place) ?*NotebookEntry {
-        for (&self.entries, 0..) |*entry, i| {
-            if (i >= self.end_of_entries) break;
-
-            // Here's where the hermit got stuck. We need to return
-            // an optional pointer to a NotebookEntry.
-            //
-            // What we have with "entry" is the opposite: a pointer to
-            // an optional NotebookEntry!
-            //
-            // To get one from the other, we need to dereference
-            // "entry" (with .*) and get the non-null value from the
-            // optional (with .?) and return the address of that. The
-            // if statement provides some clues about how the
-            // dereference and optional value "unwrapping" look
-            // together. Remember that you return the address with the
-            // "&" operator.
-            if (place == entry.*.?.place) return entry;
-            // Try to make your answer this long:__________;
-        }
-        return null;
-    }
-
-    // The checkNote() method is the beating heart of the magical
-    // notebook. Given a new note in the form of a NotebookEntry
-    // struct, we check to see if we already have an entry for the
-    // note's Place.
-    //
-    // If we DON'T, we'll add the entry to the end of the notebook
-    // along with the Path taken and distance.
-    //
-    // If we DO, we check to see if the path is "better" (shorter
-    // distance) than the one we'd noted before. If it is, we
-    // overwrite the old entry with the new one.
-    fn checkNote(self: *HermitsNotebook, note: NotebookEntry) void {
-        const existing_entry = self.getEntry(note.place);
-
-        if (existing_entry == null) {
-            self.entries[self.end_of_entries] = note;
-            self.end_of_entries += 1;
-        } else if (note.dist_to_reach < existing_entry.?.dist_to_reach) {
-            existing_entry.?.* = note;
-        }
+const HermitsNotebook = struct{
+  // Remember the array repetition operator `**`? It is no mere
+  // novelty, it's also a great way to assign multiple items in an
+  // array without having to list them one by one. Here we use it to
+  // initialize an array with null values.
+  entries: [place_count]?NotebookEntry = .{null} ** place_count,
+  
+  // The next entry keeps track of where we are in our "todo" list.
+  next_entry: u8 = 0,
+  
+  // Mark the start of empty space in the notebook.
+  end_of_entries: u8 = 0,
+  
+  // We'll often want to find an entry by Place. If one is not
+  // found, we return null.
+  fn getEntry(self: *HermitsNotebook, place: *const Place) ?*NotebookEntry{
+    for(&self.entries, 0..) |*entry, i|{
+      if(i >= self.end_of_entries){ break; }
+      
+      // Here's where the hermit got stuck. We need to return
+      // an optional pointer to a NotebookEntry.
+      //
+      // What we have with "entry" is the opposite: a pointer to
+      // an optional NotebookEntry!
+      //
+      // To get one from the other, we need to dereference
+      // "entry" (with .*) and get the non-null value from the
+      // optional (with .?) and return the address of that. The
+      // if statement provides some clues about how the
+      // dereference and optional value "unwrapping" look
+      // together. Remember that you return the address with the
+      // "&" operator.
+      if(place == entry.*.?.place){ return &entry.*.?; }
+        // Try to make your answer this long:__________;
     }
     }
-
-    // The next two methods allow us to use the notebook as a "todo"
-    // list.
-    fn hasNextEntry(self: *HermitsNotebook) bool {
-        return self.next_entry < self.end_of_entries;
+    return null;
+  }
+  
+  // The checkNote() method is the beating heart of the magical
+  // notebook. Given a new note in the form of a NotebookEntry
+  // struct, we check to see if we already have an entry for the
+  // note's Place.
+  //
+  // If we DON'T, we'll add the entry to the end of the notebook
+  // along with the Path taken and distance.
+  //
+  // If we DO, we check to see if the path is "better" (shorter
+  // distance) than the one we'd noted before. If it is, we
+  // overwrite the old entry with the new one.
+  fn checkNote(self: *HermitsNotebook, note: NotebookEntry) void{
+    const existing_entry = self.getEntry(note.place);
+    
+    if(existing_entry == null){
+      self.entries[self.end_of_entries] = note;
+      self.end_of_entries += 1;
+    }else if(note.dist_to_reach < existing_entry.?.dist_to_reach){
+      existing_entry.?.* = note;
     }
     }
-
-    fn getNextEntry(self: *HermitsNotebook) *const NotebookEntry {
-        defer self.next_entry += 1; // Increment after getting entry
-        return &self.entries[self.next_entry].?;
+  }
+  
+  // The next two methods allow us to use the notebook as a "todo"
+  // list.
+  fn hasNextEntry(self: *HermitsNotebook) bool{
+    return self.next_entry < self.end_of_entries;
+  }
+  
+  fn getNextEntry(self: *HermitsNotebook) *const NotebookEntry{
+    defer self.next_entry += 1; // Increment after getting entry
+    return &self.entries[self.next_entry].?;
+  }
+  
+  // After we've completed our search of the map, we'll have
+  // computed the shortest Path to every Place. To collect the
+  // complete trip from the start to the destination, we need to
+  // walk backwards from the destination's notebook entry, following
+  // the coming_from pointers back to the start. What we end up with
+  // is an array of TripItems with our trip in reverse order.
+  //
+  // We need to take the trip array as a parameter because we want
+  // the main() function to "own" the array memory. What do you
+  // suppose could happen if we allocated the array in this
+  // function's stack frame (the space allocated for a function's
+  // "local" data) and returned a pointer or slice to it?
+  //
+  // Looks like the hermit forgot something in the return value of
+  // this function. What could that be?
+  fn getTripTo(self: *HermitsNotebook, trip: []?TripItem, dest: *Place) TripError!void{
+    // We start at the destination entry.
+    const destination_entry = self.getEntry(dest);
+    
+    // This function needs to return an error if the requested
+    // destination was never reached. (This can't actually happen
+    // in our map since every Place is reachable by every other
+    // Place.)
+    if(destination_entry == null){
+      return TripError.Unreachable;
     }
     }
-
-    // After we've completed our search of the map, we'll have
-    // computed the shortest Path to every Place. To collect the
-    // complete trip from the start to the destination, we need to
-    // walk backwards from the destination's notebook entry, following
-    // the coming_from pointers back to the start. What we end up with
-    // is an array of TripItems with our trip in reverse order.
-    //
-    // We need to take the trip array as a parameter because we want
-    // the main() function to "own" the array memory. What do you
-    // suppose could happen if we allocated the array in this
-    // function's stack frame (the space allocated for a function's
-    // "local" data) and returned a pointer or slice to it?
-    //
-    // Looks like the hermit forgot something in the return value of
-    // this function. What could that be?
-    fn getTripTo(self: *HermitsNotebook, trip: []?TripItem, dest: *Place) void {
-        // We start at the destination entry.
-        const destination_entry = self.getEntry(dest);
-
-        // This function needs to return an error if the requested
-        // destination was never reached. (This can't actually happen
-        // in our map since every Place is reachable by every other
-        // Place.)
-        if (destination_entry == null) {
-            return TripError.Unreachable;
-        }
-
-        // Variables hold the entry we're currently examining and an
-        // index to keep track of where we're appending trip items.
-        var current_entry = destination_entry.?;
-        var i: u8 = 0;
-
-        // At the end of each looping, a continue expression increments
-        // our index. Can you see why we need to increment by two?
-        while (true) : (i += 2) {
-            trip[i] = TripItem{ .place = current_entry.place };
-
-            // An entry "coming from" nowhere means we've reached the
-            // start, so we're done.
-            if (current_entry.coming_from == null) break;
-
-            // Otherwise, entries have a path.
-            trip[i + 1] = TripItem{ .path = current_entry.via_path.? };
-
-            // Now we follow the entry we're "coming from".  If we
-            // aren't able to find the entry we're "coming from" by
-            // Place, something has gone horribly wrong with our
-            // program! (This really shouldn't ever happen. Have you
-            // checked for grues?)
-            // Note: you do not need to fix anything here.
-            const previous_entry = self.getEntry(current_entry.coming_from.?);
-            if (previous_entry == null) return TripError.EatenByAGrue;
-            current_entry = previous_entry.?;
-        }
+    
+    // Variables hold the entry we're currently examining and an
+    // index to keep track of where we're appending trip items.
+    var current_entry = destination_entry.?;
+    var i: u8 = 0;
+    
+    // At the end of each looping, a continue expression increments
+    // our index. Can you see why we need to increment by two?
+    while(true) : (i += 2){
+      trip[i] = TripItem{ .place = current_entry.place };
+      
+      // An entry "coming from" nowhere means we've reached the
+      // start, so we're done.
+      if(current_entry.coming_from == null){ break; }
+      
+      // Otherwise, entries have a path.
+      trip[i + 1] = TripItem{ .path = current_entry.via_path.? };
+      
+      // Now we follow the entry we're "coming from".  If we
+      // aren't able to find the entry we're "coming from" by
+      // Place, something has gone horribly wrong with our
+      // program! (This really shouldn't ever happen. Have you
+      // checked for grues?)
+      // Note: you do not need to fix anything here.
+      const previous_entry = self.getEntry(current_entry.coming_from.?);
+      if(previous_entry == null){ return TripError.EatenByAGrue; }
+      current_entry = previous_entry.?;
     }
     }
+  }
 };
 };
 
 
-pub fn main() void {
-    // Here's where the hermit decides where he would like to go. Once
-    // you get the program working, try some different Places on the
-    // map!
-    const start = &a; // Archer's Point
-    const destination = &f; // Fox Pond
-
-    // Store each Path array as a slice in each Place. As mentioned
-    // above, we needed to delay making these references to avoid
-    // creating a dependency loop when the compiler is trying to
-    // figure out how to allocate space for each item.
-    a.paths = a_paths[0..];
-    b.paths = b_paths[0..];
-    c.paths = c_paths[0..];
-    d.paths = d_paths[0..];
-    e.paths = e_paths[0..];
-    f.paths = f_paths[0..];
-
-    // Now we create an instance of the notebook and add the first
-    // "start" entry. Note the null values. Read the comments for the
-    // checkNote() method above to see how this entry gets added to
-    // the notebook.
-    var notebook = HermitsNotebook{};
-    var working_note = NotebookEntry{
-        .place = start,
-        .coming_from = null,
-        .via_path = null,
-        .dist_to_reach = 0,
-    };
-    notebook.checkNote(working_note);
-
-    // Get the next entry from the notebook (the first being the
-    // "start" entry we just added) until we run out, at which point
-    // we'll have checked every reachable Place.
-    while (notebook.hasNextEntry()) {
-        const place_entry = notebook.getNextEntry();
-
-        // For every Path that leads FROM the current Place, create a
-        // new note (in the form of a NotebookEntry) with the
-        // destination Place and the total distance from the start to
-        // reach that place. Again, read the comments for the
-        // checkNote() method to see how this works.
-        for (place_entry.place.paths) |*path| {
-            working_note = NotebookEntry{
-                .place = path.to,
-                .coming_from = place_entry.place,
-                .via_path = path,
-                .dist_to_reach = place_entry.dist_to_reach + path.dist,
-            };
-            notebook.checkNote(working_note);
-        }
+pub fn main() void{
+  // Here's where the hermit decides where he would like to go. Once
+  // you get the program working, try some different Places on the
+  // map!
+  const start = &a; // Archer's Point
+  const destination = &f; // Fox Pond
+  
+  // Store each Path array as a slice in each Place. As mentioned
+  // above, we needed to delay making these references to avoid
+  // creating a dependency loop when the compiler is trying to
+  // figure out how to allocate space for each item.
+  a.paths = a_paths[0..];
+  b.paths = b_paths[0..];
+  c.paths = c_paths[0..];
+  d.paths = d_paths[0..];
+  e.paths = e_paths[0..];
+  f.paths = f_paths[0..];
+  
+  // Now we create an instance of the notebook and add the first
+  // "start" entry. Note the null values. Read the comments for the
+  // checkNote() method above to see how this entry gets added to
+  // the notebook.
+  var notebook = HermitsNotebook{};
+  var working_note = NotebookEntry{
+    .place = start,
+    .coming_from = null,
+    .via_path = null,
+    .dist_to_reach = 0,
+  };
+  notebook.checkNote(working_note);
+  
+  // Get the next entry from the notebook (the first being the
+  // "start" entry we just added) until we run out, at which point
+  // we'll have checked every reachable Place.
+  while(notebook.hasNextEntry()){
+    const place_entry = notebook.getNextEntry();
+    
+    // For every Path that leads FROM the current Place, create a
+    // new note (in the form of a NotebookEntry) with the
+    // destination Place and the total distance from the start to
+    // reach that place. Again, read the comments for the
+    // checkNote() method to see how this works.
+    for(place_entry.place.paths) |*path|{
+      working_note = NotebookEntry{
+        .place = path.to,
+        .coming_from = place_entry.place,
+        .via_path = path,
+        .dist_to_reach = place_entry.dist_to_reach + path.dist,
+      };
+      notebook.checkNote(working_note);
     }
     }
-
-    // Once the loop above is complete, we've calculated the shortest
-    // path to every reachable Place! What we need to do now is set
-    // aside memory for the trip and have the hermit's notebook fill
-    // in the trip from the destination back to the path. Note that
-    // this is the first time we've actually used the destination!
-    var trip = [_]?TripItem{null} ** (place_count * 2);
-
-    notebook.getTripTo(trip[0..], destination) catch |err| {
-        print("Oh no! {}\n", .{err});
-        return;
-    };
-
-    // Print the trip with a little helper function below.
-    printTrip(trip[0..]);
+  }
+  
+  // Once the loop above is complete, we've calculated the shortest
+  // path to every reachable Place! What we need to do now is set
+  // aside memory for the trip and have the hermit's notebook fill
+  // in the trip from the destination back to the path. Note that
+  // this is the first time we've actually used the destination!
+  var trip = [_]?TripItem{null} ** (place_count * 2);
+  
+  notebook.getTripTo(trip[0..], destination) catch |err|{
+    print("Oh no! {}\n", .{err});
+    return;
+  };
+  
+  // Print the trip with a little helper function below.
+  printTrip(trip[0..]);
 }
 }
 
 
 // Remember that trips will be a series of alternating TripItems
 // Remember that trips will be a series of alternating TripItems
@@ -425,19 +425,19 @@ pub fn main() void {
 // The remaining space in the trip array will contain null values, so
 // The remaining space in the trip array will contain null values, so
 // we need to loop through the items in reverse, skipping nulls, until
 // we need to loop through the items in reverse, skipping nulls, until
 // we reach the destination at the front of the array.
 // we reach the destination at the front of the array.
-fn printTrip(trip: []?TripItem) void {
-    // We convert the usize length to a u8 with @intCast(), a
-    // builtin function just like @import().  We'll learn about
-    // these properly in a later exercise.
-    var i: u8 = @intCast(trip.len);
-
-    while (i > 0) {
-        i -= 1;
-        if (trip[i] == null) continue;
-        trip[i].?.printMe();
-    }
-
-    print("\n", .{});
+fn printTrip(trip: []?TripItem) void{
+  // We convert the usize length to a u8 with @intCast(), a
+  // builtin function just like @import().  We'll learn about
+  // these properly in a later exercise.
+  var i: u8 = @intCast(trip.len);
+  
+  while(i > 0){
+    i -= 1;
+    if(trip[i] == null){ continue; }
+    trip[i].?.printMe();
+  }
+  
+  print("\n", .{});
 }
 }
 
 
 // Going deeper:
 // Going deeper:
@@ -469,3 +469,4 @@ fn printTrip(trip: []?TripItem) void {
 // would keep the paths with the shortest distance at the front of the
 // would keep the paths with the shortest distance at the front of the
 // queue). Dijkstra's algorithm is more efficient because longer paths
 // queue). Dijkstra's algorithm is more efficient because longer paths
 // can be eliminated more quickly. (Work it out on paper to see why!)
 // can be eliminated more quickly. (Work it out on paper to see why!)
+