075_quiz8.zig 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. //
  2. // Quiz Time!
  3. //
  4. // Let's revisit the Hermit's Map from Quiz 7.
  5. //
  6. // Oh, don't worry, it's not nearly as big without all the
  7. // explanatory comments. And we're only going to change one part
  8. // of it.
  9. //
  10. const print = @import("std").debug.print;
  11. const TripError = error{ Unreachable, EatenByAGrue };
  12. const Place = struct {
  13. name: []const u8,
  14. paths: []const Path = undefined,
  15. };
  16. var a = Place{ .name = "Archer's Point" };
  17. var b = Place{ .name = "Bridge" };
  18. var c = Place{ .name = "Cottage" };
  19. var d = Place{ .name = "Dogwood Grove" };
  20. var e = Place{ .name = "East Pond" };
  21. var f = Place{ .name = "Fox Pond" };
  22. // Remember how we didn't have to declare the numeric type of the
  23. // place_count because it is only used at compile time? That
  24. // probably makes a lot more sense now. :-)
  25. const place_count = 6;
  26. const Path = struct {
  27. from: *const Place,
  28. to: *const Place,
  29. dist: u8,
  30. };
  31. // Okay, so as you may recall, we had to create each Path struct
  32. // by hand and each one took 5 lines of code to define:
  33. //
  34. // Path{
  35. // .from = &a, // from: Archer's Point
  36. // .to = &b, // to: Bridge
  37. // .dist = 2,
  38. // },
  39. //
  40. // Well, armed with the knowledge that we can run code at compile
  41. // time, we can perhaps shorten this a bit with a simple function
  42. // instead.
  43. //
  44. // Please fill in the body of this function!
  45. fn makePath(from: *Place, to: *Place, dist: u8) Path {
  46. }
  47. // Using our new function, these path definitions take up considerably less
  48. // space in our program now!
  49. const a_paths = [_]Path{makePath(&a, &b, 2)};
  50. const b_paths = [_]Path{ makePath(&b, &a, 2), makePath(&b, &d, 1) };
  51. const c_paths = [_]Path{ makePath(&c, &d, 3), makePath(&c, &e, 2) };
  52. const d_paths = [_]Path{ makePath(&d, &b, 1), makePath(&d, &c, 3), makePath(&d, &f, 7) };
  53. const e_paths = [_]Path{ makePath(&e, &c, 2), makePath(&e, &f, 1) };
  54. const f_paths = [_]Path{makePath(&f, &d, 7)};
  55. //
  56. // But is it more readable? That could be argued either way.
  57. //
  58. // We've seen that it is possible to parse strings at compile
  59. // time, so the sky's really the limit on how fancy we could get
  60. // with this.
  61. //
  62. // For example, we could create our own "path language" and
  63. // create Paths from that. Something like this, perhaps:
  64. //
  65. // a -> (b[2])
  66. // b -> (a[2] d[1])
  67. // c -> (d[3] e[2])
  68. // ...
  69. //
  70. // Feel free to implement something like that as a SUPER BONUS EXERCISE!
  71. const TripItem = union(enum) {
  72. place: *const Place,
  73. path: *const Path,
  74. fn printMe(self: TripItem) void {
  75. switch (self) {
  76. .place => |p| print("{s}", .{p.name}),
  77. .path => |p| print("--{}->", .{p.dist}),
  78. }
  79. }
  80. };
  81. const NotebookEntry = struct {
  82. place: *const Place,
  83. coming_from: ?*const Place,
  84. via_path: ?*const Path,
  85. dist_to_reach: u16,
  86. };
  87. const HermitsNotebook = struct {
  88. entries: [place_count]?NotebookEntry = .{null} ** place_count,
  89. next_entry: u8 = 0,
  90. end_of_entries: u8 = 0,
  91. fn getEntry(self: *HermitsNotebook, place: *const Place) ?*NotebookEntry {
  92. for (&self.entries, 0..) |*entry, i| {
  93. if (i >= self.end_of_entries) break;
  94. if (place == entry.*.?.place) return &entry.*.?;
  95. }
  96. return null;
  97. }
  98. fn checkNote(self: *HermitsNotebook, note: NotebookEntry) void {
  99. const existing_entry = self.getEntry(note.place);
  100. if (existing_entry == null) {
  101. self.entries[self.end_of_entries] = note;
  102. self.end_of_entries += 1;
  103. } else if (note.dist_to_reach < existing_entry.?.dist_to_reach) {
  104. existing_entry.?.* = note;
  105. }
  106. }
  107. fn hasNextEntry(self: *HermitsNotebook) bool {
  108. return self.next_entry < self.end_of_entries;
  109. }
  110. fn getNextEntry(self: *HermitsNotebook) *const NotebookEntry {
  111. defer self.next_entry += 1;
  112. return &self.entries[self.next_entry].?;
  113. }
  114. fn getTripTo(self: *HermitsNotebook, trip: []?TripItem, dest: *Place) TripError!void {
  115. const destination_entry = self.getEntry(dest);
  116. if (destination_entry == null) {
  117. return TripError.Unreachable;
  118. }
  119. var current_entry = destination_entry.?;
  120. var i: u8 = 0;
  121. while (true) : (i += 2) {
  122. trip[i] = TripItem{ .place = current_entry.place };
  123. if (current_entry.coming_from == null) break;
  124. trip[i + 1] = TripItem{ .path = current_entry.via_path.? };
  125. const previous_entry = self.getEntry(current_entry.coming_from.?);
  126. if (previous_entry == null) return TripError.EatenByAGrue;
  127. current_entry = previous_entry.?;
  128. }
  129. }
  130. };
  131. pub fn main() void {
  132. const start = &a; // Archer's Point
  133. const destination = &f; // Fox Pond
  134. // We could either have this:
  135. //
  136. // a.paths = a_paths[0..];
  137. // b.paths = b_paths[0..];
  138. // c.paths = c_paths[0..];
  139. // d.paths = d_paths[0..];
  140. // e.paths = e_paths[0..];
  141. // f.paths = f_paths[0..];
  142. //
  143. // or this comptime wizardry:
  144. //
  145. const letters = [_][]const u8{ "a", "b", "c", "d", "e", "f" };
  146. inline for (letters) |letter| {
  147. @field(@This(), letter).paths = @field(@This(), letter ++ "_paths")[0..];
  148. }
  149. var notebook = HermitsNotebook{};
  150. var working_note = NotebookEntry{
  151. .place = start,
  152. .coming_from = null,
  153. .via_path = null,
  154. .dist_to_reach = 0,
  155. };
  156. notebook.checkNote(working_note);
  157. while (notebook.hasNextEntry()) {
  158. const place_entry = notebook.getNextEntry();
  159. for (place_entry.place.paths) |*path| {
  160. working_note = NotebookEntry{
  161. .place = path.to,
  162. .coming_from = place_entry.place,
  163. .via_path = path,
  164. .dist_to_reach = place_entry.dist_to_reach + path.dist,
  165. };
  166. notebook.checkNote(working_note);
  167. }
  168. }
  169. var trip = [_]?TripItem{null} ** (place_count * 2);
  170. notebook.getTripTo(trip[0..], destination) catch |err| {
  171. print("Oh no! {}\n", .{err});
  172. return;
  173. };
  174. printTrip(trip[0..]);
  175. }
  176. fn printTrip(trip: []?TripItem) void {
  177. var i: u8 = @intCast(trip.len);
  178. while (i > 0) {
  179. i -= 1;
  180. if (trip[i] == null) continue;
  181. trip[i].?.printMe();
  182. }
  183. print("\n", .{});
  184. }