From 588a279b8b8326068fc238f290a602592c6affb9 Mon Sep 17 00:00:00 2001 From: Juris Date: Sat, 7 Dec 2024 08:30:41 +0200 Subject: [PATCH] Refactoring 2024-06 Scala 2 --- rust/Cargo.lock | 2 +- rust/common/Cargo.toml | 1 + rust/y2024/Cargo.toml | 1 - .../scala/jurisk/adventofcode/Advent00.scala | 9 +-- .../jurisk/adventofcode/y2024/Advent06.scala | 40 +++++++------ .../collections/mutable/MutableBitSet.scala | 59 +++++++++++++++++++ 6 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 scala2/src/main/scala/jurisk/collections/mutable/MutableBitSet.scala diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b69c9ca1..601c45fd 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -118,7 +118,6 @@ name = "advent-of-code-2024" version = "0.1.0" dependencies = [ "advent-of-code-common", - "bit-set", "chumsky", "itertools", "regex", @@ -128,6 +127,7 @@ dependencies = [ name = "advent-of-code-common" version = "0.1.0" dependencies = [ + "bit-set", "itertools", "md5", "nonempty", diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml index f76fc8b4..6a3e1cdb 100644 --- a/rust/common/Cargo.toml +++ b/rust/common/Cargo.toml @@ -16,3 +16,4 @@ num-traits.workspace = true num-derive.workspace = true itertools.workspace = true md5.workspace = true +bit-set.workspace = true \ No newline at end of file diff --git a/rust/y2024/Cargo.toml b/rust/y2024/Cargo.toml index 9be465a1..b4e295a7 100644 --- a/rust/y2024/Cargo.toml +++ b/rust/y2024/Cargo.toml @@ -14,4 +14,3 @@ advent-of-code-common = { path = "../common" } chumsky.workspace = true itertools.workspace = true regex.workspace = true -bit-set.workspace = true \ No newline at end of file diff --git a/scala2/src/main/scala/jurisk/adventofcode/Advent00.scala b/scala2/src/main/scala/jurisk/adventofcode/Advent00.scala index e27311f9..d0aa6dcb 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/Advent00.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/Advent00.scala @@ -8,19 +8,20 @@ import org.scalatest.matchers.should.Matchers._ object Advent00 { type Input = List[Command] + type N = Long sealed trait Command extends Product with Serializable object Command { case object Noop extends Command final case class Something( - values: List[Int] + values: List[N] ) extends Command final case class Other(value: String) extends Command def parse(s: String): Command = s match { case "noop" => Noop - case s"something $rem" => Something(rem.extractIntList) + case s"something $rem" => Something(rem.extractLongList) case s if s.nonEmpty => Other(s) case _ => s.failedToParse } @@ -29,10 +30,10 @@ object Advent00 { def parse(input: String): Input = input.parseLines(Command.parse) - def part1(data: Input): Int = + def part1(data: Input): N = 0 - def part2(data: Input): Int = + def part2(data: Input): N = 0 def parseFile(fileName: String): Input = diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent06.scala b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent06.scala index 068286f6..0b691c10 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent06.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2024/Advent06.scala @@ -3,6 +3,8 @@ package jurisk.adventofcode.y2024 import cats.implicits._ import jurisk.adventofcode.y2024.Advent06.Block.Empty import jurisk.adventofcode.y2024.Advent06.Block.Wall +import jurisk.collections.mutable.BitSetKey +import jurisk.collections.mutable.MutableBitSet import jurisk.geometry.Coords2D import jurisk.geometry.Direction2D import jurisk.geometry.Direction2D.CardinalDirection2D @@ -21,11 +23,6 @@ object Advent06 { type Input = (Coords2D, Field2D[Block]) - final private case class GuardWithVisitedLog( - guard: Guard, - visited: Set[Coords2D], - ) - final private case class Guard( location: Coords2D, direction: CardinalDirection2D, @@ -57,24 +54,30 @@ object Advent06 { (location, field) } - private def guardsPath(data: Input): Set[Coords2D] = { - // TODO: Try a BitSet instead of a Set? - + private def guardsPath(data: Input): MutableBitSet[Coords2D] = { val (location, field) = data - val state = - GuardWithVisitedLog(Guard(location, Direction2D.N), Set(location)) + + implicit val key: BitSetKey[Coords2D] = new BitSetKey[Coords2D] { + def toInt(value: Coords2D): Int = value.x + value.y * field.width + def fromInt(value: Int): Coords2D = + Coords2D(value % field.width, value / field.width) + } + + val visited = MutableBitSet[Coords2D](location) + + val state = Guard(location, Direction2D.N) Simulation.run(state) { s => - s.guard.next(field) match { + s.next(field) match { case Some(next) => - s.copy( - guard = next, - visited = s.visited + next.location, - ).asRight + visited.add(next.location) + next.asRight case None => - s.visited.asLeft + ().asLeft } } + + visited } def part1(data: Input): Int = @@ -94,8 +97,9 @@ object Advent06 { val (location, field) = data guardsPath(data) - .filterNot(_ == location) - .count(c => wouldLoop(location, field.updatedAtUnsafe(c, Wall))) + .count(c => + c != location && wouldLoop(location, field.updatedAtUnsafe(c, Wall)) + ) } def parseFile(fileName: String): Input = diff --git a/scala2/src/main/scala/jurisk/collections/mutable/MutableBitSet.scala b/scala2/src/main/scala/jurisk/collections/mutable/MutableBitSet.scala new file mode 100644 index 00000000..40dddf76 --- /dev/null +++ b/scala2/src/main/scala/jurisk/collections/mutable/MutableBitSet.scala @@ -0,0 +1,59 @@ +package jurisk.collections.mutable + +import jurisk.collections.mutable.BitSetKeySyntax.BitSetKeyOps1 +import jurisk.collections.mutable.BitSetKeySyntax.BitSetKeyOps2 + +import scala.collection.mutable + +trait BitSetKey[T] { + def toInt(value: T): Int + def fromInt(value: Int): T +} + +object BitSetKeyInstances { + implicit val intBitSetKey: BitSetKey[Int] = new BitSetKey[Int] { + def toInt(value: Int): Int = value + def fromInt(value: Int): Int = value + } +} + +object BitSetKeySyntax { + implicit class BitSetKeyOps1[T](value: T)(implicit ev: BitSetKey[T]) { + def toInt: Int = ev.toInt(value) + } + + implicit class BitSetKeyOps2[T](value: Int)(implicit ev: BitSetKey[T]) { + def fromInt: T = ev.fromInt(value) + } +} + +final class MutableBitSet[T: BitSetKey]( + private val underlying: mutable.BitSet +) { + private def this() = + this(mutable.BitSet.empty) + + def add(value: T): Boolean = + underlying.add(value.toInt) + + def size: Int = + underlying.size + + def filterNot( + predicate: T => Boolean + ): MutableBitSet[T] = + new MutableBitSet(underlying.filterNot(x => predicate(x.fromInt))) + + def count( + predicate: T => Boolean + ): Int = + underlying.count(x => predicate(x.fromInt)) +} + +object MutableBitSet { + def apply[T: BitSetKey](values: T*): MutableBitSet[T] = { + val result = new MutableBitSet[T]() + values.foreach(result.add) + result + } +}