use "collections" use "files" use "format" use "itertools" class Orbit let _name: String var _orbits: Array[Orbit ref] new create(n: String) => _name = n _orbits = Array[Orbit]() fun ref add(o: Orbit ref) => _orbits.push(o) fun name(): String => _name fun print(env: Env) => env.out.write(_name + ":") for o in _orbits.values() do env.out.write(" " + o.name()) end env.out.print("") fun checksum(path_len: USize): USize => var sum = path_len for o in _orbits.values() do sum = sum + o.checksum(path_len + 1) end sum fun ref contains(n: String): Bool => (n == _name) or (Iter[Orbit](_orbits.values()).any({(o) => o.contains(n)})) fun ref closest_parent(l: String, r: String): String ? => for o in _orbits.values() do try return o.closest_parent(l, r)? end end if contains(l) and contains(r) then return _name end error fun ref find_distance(n: String, dist: USize = 0): USize ? => if n == _name then return dist end for o in _orbits.values() do try return o.find_distance(n, dist + 1)? end end error class System var orbits: Map[String, Orbit] new create(mapping: Array[(String, String)]) => orbits = Map[String, Orbit]() orbits.insert("COM", Orbit("COM")) for (inner_name, outer_name) in mapping.values() do let outer = orbits.insert_if_absent(outer_name, Orbit(outer_name)) let inner = orbits.insert_if_absent(inner_name, Orbit(inner_name)) inner.add(outer) end fun print(env: Env) => for o in orbits.values() do o.print(env) end fun checksum(): USize => try orbits("COM")?.checksum(0) else 0 end fun ref closest_parent(l: String, r: String): (String | None) => try orbits("COM")?.closest_parent(l, r)? else None end fun ref distance(parent: String, child: String): USize => try orbits(parent)?.find_distance(child)? else 0 end actor Main new create(env: Env) => let caps = recover val FileCaps.>set(FileRead).>set(FileStat) end let target_file = try env.args(1)? else "input.txt" end try with file = OpenFile(FilePath(env.root as AmbientAuth, target_file, caps)?) as File do let mapping = Array[(String, String)]() for line in file.lines() do try let sline = line.split(")") mapping.push((sline(0)?, sline(1)?)) else env.out.write("Failed to parse line: " + line.clone()) end end let s = System(mapping) env.out.print("Checksum: " + Format.int[USize](s.checksum())) s.print(env) match s.closest_parent("YOU", "SAN") | None => env.out.print("No connection between the two somehow?") | let o: String => env.out.print("Closest parent between YOU and SAN is " + o) let d1 = s.distance(o, "YOU") let d2 = s.distance(o, "SAN") env.out.print("Distance is " + Format.int[USize]((d1 + d2) - 2)) end end end