|
@@ -50,9 +50,9 @@ const TripError = error{ Unreachable, EatenByAGrue };
|
|
|
// assign the paths later. And why is that? Because paths contain
|
|
|
// pointers to places and assigning them now would create a dependency
|
|
|
// 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" };
|
|
@@ -93,10 +93,10 @@ const place_count = 6;
|
|
|
|
|
|
// Now let's create all of the paths between sites. A path goes from
|
|
|
// 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
|
|
@@ -105,97 +105,97 @@ const Path = struct {
|
|
|
// code (much like macros in other languages), but we haven't learned
|
|
|
// how to do that yet!
|
|
|
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{
|
|
|
- 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{
|
|
|
- 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{
|
|
|
- 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{
|
|
|
- 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{
|
|
|
- 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
|
|
|
// "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
|
|
|
// 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
|
|
@@ -204,11 +204,11 @@ const TripItem = union(enum) {
|
|
|
// 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
|
|
|
// 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
|
|
@@ -425,19 +425,19 @@ pub fn main() void {
|
|
|
// 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 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:
|
|
@@ -469,3 +469,4 @@ fn printTrip(trip: []?TripItem) void {
|
|
|
// would keep the paths with the shortest distance at the front of the
|
|
|
// queue). Dijkstra's algorithm is more efficient because longer paths
|
|
|
// can be eliminated more quickly. (Work it out on paper to see why!)
|
|
|
+
|