From 4c8871314731672710abe3e45fac9988cd3c901a Mon Sep 17 00:00:00 2001 From: James Collier Date: Wed, 24 Sep 2025 14:29:17 +0100 Subject: [PATCH 1/3] commit --- adhoc/path.scala | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 adhoc/path.scala diff --git a/adhoc/path.scala b/adhoc/path.scala new file mode 100644 index 0000000..2830286 --- /dev/null +++ b/adhoc/path.scala @@ -0,0 +1,44 @@ +import $ivy.`com.lihaoyi::os-lib:0.9.2` +import $ivy.`org.typelevel::cats-core:2.13.0` +import cats._ +import cats.data._ +import cats.syntax.all._ + +val input = "ABC: A->B,B->C" + +val inputRegex = """(\w+): ([\w,\s\->]+)""".r +val transformRegex = """(\w)\->(\w)""".r + +// parse the input +val (chars, transforms) = input match { + case inputRegex(chars, transforms) => + (chars.toList, transforms.split("\\s*,\\s*").toList.map { + case transformRegex(left, right) => + (left.charAt(0), right.charAt(0)) + } + .groupMap(_._1)(_._2) + .view.mapValues(_.toSet) + .toMap) +} + +def loop(c: Char, depth: Long): List[(Char, Long)] = { + (c, depth) :: transforms.get(c).toList.flatMap(_.flatMap(loop(_, depth + 1))) +} + +// generate shortest paths with distance +val shortestPaths = transforms.keys.toList + .flatMap(startC => + loop(startC, 0).map { case (endC, depth) => ((startC, endC), depth)} + ) + .groupMap(_._1)(_._2) + .view.mapValues(_.min) + .toMap + +println("shortest paths: " + shortestPaths) + +val dests = transforms.values.flatten + +val dests2cost = dests.map(dest => (dest, input.flatMap(shortestPaths.get(_, dest)).sum)) +val (cheapestDest, cheapestCost) = dests2cost.minBy(_._2) + +println(s"$cheapestDest (cost $cheapestCost)") From 74c02996fe289ff3bcda830b7c51762f2f00d3e8 Mon Sep 17 00:00:00 2001 From: James Collier Date: Thu, 25 Sep 2025 12:22:59 +0100 Subject: [PATCH 2/3] fix --- adhoc/data/path.dat | 3 +++ adhoc/path.scala | 63 ++++++++++++++++++++++++--------------------- 2 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 adhoc/data/path.dat diff --git a/adhoc/data/path.dat b/adhoc/data/path.dat new file mode 100644 index 0000000..672b8ec --- /dev/null +++ b/adhoc/data/path.dat @@ -0,0 +1,3 @@ +ABC: A->B,B->C +CCC: A->B,B->C +AAD: A->B,B->C,C->D,D->A \ No newline at end of file diff --git a/adhoc/path.scala b/adhoc/path.scala index 2830286..67712d0 100644 --- a/adhoc/path.scala +++ b/adhoc/path.scala @@ -4,41 +4,44 @@ import cats._ import cats.data._ import cats.syntax.all._ -val input = "ABC: A->B,B->C" +val data = os.read.lines(os.pwd / "data" / "path.dat").filter(!_.isEmpty) val inputRegex = """(\w+): ([\w,\s\->]+)""".r val transformRegex = """(\w)\->(\w)""".r -// parse the input -val (chars, transforms) = input match { - case inputRegex(chars, transforms) => - (chars.toList, transforms.split("\\s*,\\s*").toList.map { - case transformRegex(left, right) => - (left.charAt(0), right.charAt(0)) - } +def solve(input: String): String = { + // parse the input + val (chars, transforms) = input match { + case inputRegex(chars, transforms) => + (chars.toList, transforms.split("\\s*,\\s*").toList.map { + case transformRegex(left, right) => + (left.charAt(0), right.charAt(0)) + } + .groupMap(_._1)(_._2) + .view.mapValues(_.toSet) + .toMap) + } + + def loop(c: Char, depth: Long, seen: Set[(Char)]): List[(Char, Long)] = { + if (seen.contains(c)) List.empty + else (c, depth) :: transforms.get(c).toList.flatMap(_.flatMap(loop(_, depth + 1, seen + c))) + } + + val dests = transforms.values.flatten.toSet + + // generate shortest paths with distance + val shortestPaths = chars + .flatMap(startC => + loop(startC, 0L, Set.empty).map { case (endC, depth) => ((startC, endC), depth)} + ) .groupMap(_._1)(_._2) - .view.mapValues(_.toSet) - .toMap) -} - -def loop(c: Char, depth: Long): List[(Char, Long)] = { - (c, depth) :: transforms.get(c).toList.flatMap(_.flatMap(loop(_, depth + 1))) -} + .view.mapValues(_.min) + .toMap -// generate shortest paths with distance -val shortestPaths = transforms.keys.toList - .flatMap(startC => - loop(startC, 0).map { case (endC, depth) => ((startC, endC), depth)} - ) - .groupMap(_._1)(_._2) - .view.mapValues(_.min) - .toMap + val viablePaths = dests.flatMap(dest => chars.map(c => shortestPaths.get(c, dest)).sequence.map(distance => (dest, distance.sum))) + val (cheapestDest, cheapestCost) = viablePaths.minBy(_._2) -println("shortest paths: " + shortestPaths) - -val dests = transforms.values.flatten - -val dests2cost = dests.map(dest => (dest, input.flatMap(shortestPaths.get(_, dest)).sum)) -val (cheapestDest, cheapestCost) = dests2cost.minBy(_._2) + s"$input\n$cheapestDest (cost $cheapestCost)" +} -println(s"$cheapestDest (cost $cheapestCost)") +println(data.map(solve).mkString("\n")) \ No newline at end of file From 3f48c585f447c1bfffaf19c250a11f8ad7187d79 Mon Sep 17 00:00:00 2001 From: James Collier Date: Thu, 25 Sep 2025 12:28:05 +0100 Subject: [PATCH 3/3] handle no path --- adhoc/data/path.dat | 4 +++- adhoc/path.scala | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/adhoc/data/path.dat b/adhoc/data/path.dat index 672b8ec..0ea1504 100644 --- a/adhoc/data/path.dat +++ b/adhoc/data/path.dat @@ -1,3 +1,5 @@ ABC: A->B,B->C CCC: A->B,B->C -AAD: A->B,B->C,C->D,D->A \ No newline at end of file +AAD: A->B,B->C,C->D +AAD: A->B,B->C,C->D,D->A +DAA: A->B,C->D \ No newline at end of file diff --git a/adhoc/path.scala b/adhoc/path.scala index 67712d0..20fc337 100644 --- a/adhoc/path.scala +++ b/adhoc/path.scala @@ -38,10 +38,13 @@ def solve(input: String): String = { .view.mapValues(_.min) .toMap - val viablePaths = dests.flatMap(dest => chars.map(c => shortestPaths.get(c, dest)).sequence.map(distance => (dest, distance.sum))) - val (cheapestDest, cheapestCost) = viablePaths.minBy(_._2) - - s"$input\n$cheapestDest (cost $cheapestCost)" + val viablePaths = dests.flatMap(dest => chars.traverse(c => shortestPaths.get(c, dest)).map(distance => (dest, distance.sum))) + val result = viablePaths.minByOption(_._2) match { + case Some((cheapestDest, cheapestCost)) => + s"$cheapestDest (distance $cheapestCost)" + case None => "No path" + } + s"$input\n$result" } println(data.map(solve).mkString("\n")) \ No newline at end of file