diff --git a/README.md b/README.md
index 5872791..4eb9bf8 100644
--- a/README.md
+++ b/README.md
@@ -5,11 +5,11 @@ This repo contains my attempt at this year's Advent of Code (2023)
I will try and do some problems (probably not all of them) in Scala
## Progress:
-#### Stars: 18 / 50
-| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
-|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|
-| | | | | 1
:star::star: | 2
:star::star: | 3
:star::star: |
-| 4
:star::star: | 5
:star::star: | 6
:star::star: | 7
:star::star: | 8
:star::star: | 9
:star::star: | 10
:star: |
-| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
-| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
-| 25 | | | | | | |
\ No newline at end of file
+#### Stars: 20 / 50
+| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
+|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:------------------:|
+| | | | | 1
:star::star: | 2
:star::star: | 3
:star::star: |
+| 4
:star::star: | 5
:star::star: | 6
:star::star: | 7
:star::star: | 8
:star::star: | 9
:star::star: | 10
:star::star: |
+| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
+| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
+| 25 | | | | | | |
\ No newline at end of file
diff --git a/src/day10/Painter.scala b/src/day10/Painter.scala
new file mode 100644
index 0000000..00c7819
--- /dev/null
+++ b/src/day10/Painter.scala
@@ -0,0 +1,77 @@
+package day10
+
+import scala.collection.mutable.ArrayBuffer
+
+class Painter(startX: Int, startY: Int, excludeX: Int, excludeY: Int) extends Walker(startX, startY, excludeX, excludeY) {
+ def walk(grid: Array[Array[Byte]], zones: Array[Array[Int]], path: ArrayBuffer[(Int, Int)]): Unit = {
+ val prevX: Int = lastX
+ val prevY: Int = lastY
+ val curX: Int = x
+ val curY: Int = y
+ val curTile: Byte = grid(y)(x)
+ super.walk(grid)
+ val newX: Int = x
+ val newY: Int = y
+ val dx: Int = newX - prevX
+ val dy: Int = newY - prevY
+
+ // West-East
+ if (curTile == 5) {
+ // Going east
+ if (dx > 0) {
+ setZone(path, zones, curX, curY - 1, 1)
+ setZone(path, zones, curX, curY + 1, 2)
+
+ // Going west
+ } else {
+ setZone(path, zones, curX, curY - 1, 2)
+ setZone(path, zones, curX, curY + 1, 1)
+ }
+
+ // North-South
+ } else if (curTile == 10) {
+ // Going south
+ if (dy > 0) {
+ setZone(path, zones, curX+1, curY, 1)
+ setZone(path, zones, curX-1, curY, 2)
+
+ // Going north
+ } else {
+ setZone(path, zones, curX+1, curY, 2)
+ setZone(path, zones, curX-1, curY, 1)
+ }
+
+ // Corner
+ } else if (curTile != 15) {
+ val east: Boolean = (curTile & 1) != 0
+ val south: Boolean = (curTile & 2) != 0
+
+ val toEast: Boolean = dx > 0
+ val toSouth: Boolean = dy > 0
+
+ val offsetBottom: Boolean = east ^ toEast ^ toSouth
+ val zoneY: Boolean = !toSouth ^ east
+
+ val offsetRight: Boolean = south ^ toEast ^ toSouth
+ val zoneX: Boolean = toEast ^ south
+
+ val offsetY: Int = if (offsetBottom) 1 else -1
+ val zoneYVal: Int = if (zoneY) 2 else 1
+ setZone(path, zones, curX, curY+offsetY, zoneYVal)
+
+ val offsetX: Int = if (offsetRight) 1 else -1
+ val zoneXVal: Int = if (zoneX) 2 else 1
+ setZone(path, zones, curX+offsetX, curY, zoneXVal)
+ }
+ }
+
+ private def setZone(path: ArrayBuffer[(Int, Int)], zones: Array[Array[Int]], posX: Int, posY: Int, zone: Int): Unit = {
+ val height: Int = zones.length
+ val width: Int = if (height == 0) 0 else zones(0).length
+ if (0 <= posX && posX < width && 0 <= posY && posY < height) {
+ if (!path.contains((posX, posY))) {
+ zones(posY)(posX) = zone
+ }
+ }
+ }
+}
diff --git a/src/day10/Puzzle2.scala b/src/day10/Puzzle2.scala
new file mode 100644
index 0000000..075a436
--- /dev/null
+++ b/src/day10/Puzzle2.scala
@@ -0,0 +1,147 @@
+package day10
+
+import scala.collection.immutable.HashMap
+import scala.collection.mutable.ArrayBuffer
+import scala.io.{BufferedSource, Source}
+
+object Puzzle2 {
+ var grid: Array[Array[Byte]] = Array.empty
+ var zones: Array[Array[Int]] = Array.empty
+
+ // bits: NWSE
+ val TILES: Map[Char, Byte] = HashMap(
+ '|' -> 10,
+ '-' -> 5,
+ 'L' -> 9,
+ 'J' -> 12,
+ '7' -> 6,
+ 'F' -> 3,
+ '.' -> 0,
+ 'S' -> 15
+ )
+
+ var height: Int = 0
+ var width: Int = 0
+ var startX: Int = 0
+ var startY: Int = 0
+
+ def loadInput(path: String): Unit = {
+ val source: BufferedSource = Source.fromFile(path)
+ val lines: Array[String] = source.getLines().toArray
+
+ height = lines.length
+ width = if (height == 0) 0 else lines(0).length
+ grid = Array.ofDim(height, width)
+ zones = Array.ofDim(height, width)
+
+ for ((line: String, y: Int) <- lines.zipWithIndex) {
+ for ((c: Char, x: Int) <- line.zipWithIndex) {
+ grid(y)(x) = TILES(c)
+ if (c == 'S') {
+ startX = x
+ startY = y
+ }
+ }
+ }
+
+ source.close()
+ }
+
+ def calculateArea(): Int = {
+ val path: ArrayBuffer[(Int, Int)] = new ArrayBuffer()
+ path.addOne((startX, startY))
+
+ val walker: Walker = new Walker(startX, startY)
+
+ do {
+ walker.walk(grid)
+ path.addOne(walker.getPos())
+ } while (walker.getX() != startX || walker.getY() != startY)
+ println(s"Found path (length = ${path.length})")
+
+ val painter: Painter = new Painter(startX, startY, 2*startX-path(1)._1, 2*startY-path(1)._2)
+ do {
+ painter.walk(grid, zones, path)
+ } while (painter.getX() != startX || painter.getY() != startY)
+ println("Painted path neighbours")
+
+ var newZones: Array[Array[Int]] = Array.ofDim(height, width)
+ var filled: Boolean = false
+ var exteriorZone: Int = 0
+
+ do {
+ filled = true
+ newZones = copyZones()
+ for (y: Int <- 0 until height) {
+ for (x: Int <- 0 until width) {
+ if (zones(y)(x) == 0 && !path.contains((x, y))) {
+ filled = false
+ fillTile(x, y, newZones)
+ }
+ }
+ }
+ zones = newZones
+ } while (!filled)
+ println("Filled zones")
+
+ for (y: Int <- 0 until height) {
+ for (x: Int <- 0 until width) {
+ if (x == 0 || x == width - 1 || y == 0 || y == height - 1) {
+ if (zones(y)(x) != 0) {
+ exteriorZone = zones(y)(x)
+ }
+ }
+ }
+ }
+ println(s"Found exterior zone: $exteriorZone")
+
+ var area: Int = 0
+ for (y: Int <- 0 until height) {
+ for (x: Int <- 0 until width) {
+ if (zones(y)(x) != exteriorZone && zones(y)(x) != 0) area += 1
+ }
+ }
+ println(s"Calculated area: $area")
+
+ return area
+ }
+
+ def fillTile(x: Int, y: Int, newZones: Array[Array[Int]]): Unit = {
+ var zone: Int = 0
+
+ for ((dx: Int, dy: Int) <- Walker.OFFSETS) {
+ val x2: Int = x + dx
+ val y2: Int = y + dy
+ if (0 <= x2 && x2 < width && 0 <= y2 && y2 < height) {
+ if (zones(y2)(x2) != 0) {
+ zone = zones(y2)(x2)
+ }
+ }
+ }
+
+ newZones(y)(x) = zone
+ }
+
+ def copyZones(): Array[Array[Int]] = {
+ val result: Array[Array[Int]] = Array.ofDim(height, width)
+ for (y: Int <- 0 until height) {
+ for (x: Int <- 0 until width) {
+ result(y)(x) = zones(y)(x)
+ }
+ }
+ return result
+ }
+
+ def solve(path: String): Int = {
+ loadInput(path)
+
+ val solution: Int = calculateArea()
+
+ return solution
+ }
+
+ def main(args: Array[String]): Unit = {
+ val solution: Int = solve("./res/day10/input1.txt")
+ println(solution)
+ }
+}
diff --git a/src/day10/Walker.scala b/src/day10/Walker.scala
index 8da9609..10d391e 100644
--- a/src/day10/Walker.scala
+++ b/src/day10/Walker.scala
@@ -1,15 +1,11 @@
package day10
class Walker {
- private var x: Int = 0
- private var y: Int = 0
- private var lastX: Int = -1
- private var lastY: Int = -1
- private var distance: Int = 0
-
- val OFFSETS: Array[(Int, Int)] = Array(
- (1, 0), (0, 1), (-1, 0), (0, -1)
- )
+ protected var x: Int = 0
+ protected var y: Int = 0
+ protected var lastX: Int = -1
+ protected var lastY: Int = -1
+ protected var distance: Int = 0
def this(startX: Int, startY: Int) = {
this()
@@ -41,7 +37,7 @@ class Walker {
private def getNextPos(grid: Array[Array[Byte]], width: Int, height: Int): (Int, Int) = {
val curTile: Byte = grid(y)(x)
- for (((dx: Int, dy: Int), i: Int) <- OFFSETS.zipWithIndex) {
+ for (((dx: Int, dy: Int), i: Int) <- Walker.OFFSETS.zipWithIndex) {
val x2: Int = x + dx
val y2: Int = y + dy
if (x2 != lastX || y2 != lastY) {
@@ -58,3 +54,9 @@ class Walker {
throw new Exception("Dead-end path")
}
}
+
+object Walker {
+ val OFFSETS: Array[(Int, Int)] = Array(
+ (1, 0), (0, 1), (-1, 0), (0, -1)
+ )
+}
\ No newline at end of file
diff --git a/tests/day10/Puzzle2Test.scala b/tests/day10/Puzzle2Test.scala
new file mode 100644
index 0000000..bfeb306
--- /dev/null
+++ b/tests/day10/Puzzle2Test.scala
@@ -0,0 +1,24 @@
+package day10
+
+import org.scalatest.funsuite.AnyFunSuite
+
+class Puzzle2Test extends AnyFunSuite {
+ test("Puzzle2.solve 1") {
+ assert(Puzzle2.solve("tests_res/day10/input1.txt") == 1)
+ }
+ test("Puzzle2.solve 2") {
+ assert(Puzzle2.solve("tests_res/day10/input2.txt") == 1)
+ }
+ test("Puzzle2.solve 3") {
+ assert(Puzzle2.solve("tests_res/day10/input3.txt") == 4)
+ }
+ test("Puzzle2.solve 4") {
+ assert(Puzzle2.solve("tests_res/day10/input4.txt") == 4)
+ }
+ test("Puzzle2.solve 5") {
+ assert(Puzzle2.solve("tests_res/day10/input5.txt") == 8)
+ }
+ test("Puzzle2.solve 6") {
+ assert(Puzzle2.solve("tests_res/day10/input6.txt") == 10)
+ }
+}
diff --git a/tests_res/day10/input3.txt b/tests_res/day10/input3.txt
new file mode 100644
index 0000000..6933a28
--- /dev/null
+++ b/tests_res/day10/input3.txt
@@ -0,0 +1,9 @@
+...........
+.S-------7.
+.|F-----7|.
+.||.....||.
+.||.....||.
+.|L-7.F-J|.
+.|..|.|..|.
+.L--J.L--J.
+...........
\ No newline at end of file
diff --git a/tests_res/day10/input4.txt b/tests_res/day10/input4.txt
new file mode 100644
index 0000000..c9f3dd0
--- /dev/null
+++ b/tests_res/day10/input4.txt
@@ -0,0 +1,9 @@
+..........
+.S------7.
+.|F----7|.
+.||....||.
+.||....||.
+.|L-7F-J|.
+.|..||..|.
+.L--JL--J.
+..........
\ No newline at end of file
diff --git a/tests_res/day10/input5.txt b/tests_res/day10/input5.txt
new file mode 100644
index 0000000..2e5dcbb
--- /dev/null
+++ b/tests_res/day10/input5.txt
@@ -0,0 +1,10 @@
+.F----7F7F7F7F-7....
+.|F--7||||||||FJ....
+.||.FJ||||||||L7....
+FJL7L7LJLJ||LJ.L-7..
+L--J.L7...LJS7F-7L7.
+....F-J..F7FJ|L7L7L7
+....L7.F7||L7|.L7L7|
+.....|FJLJ|FJ|F7|.LJ
+....FJL-7.||.||||...
+....L---J.LJ.LJLJ...
\ No newline at end of file
diff --git a/tests_res/day10/input6.txt b/tests_res/day10/input6.txt
new file mode 100644
index 0000000..fbc0300
--- /dev/null
+++ b/tests_res/day10/input6.txt
@@ -0,0 +1,10 @@
+FF7FSF7F7F7F7F7F---7
+L|LJ||||||||||||F--J
+FL-7LJLJ||||||LJL-77
+F--JF--7||LJLJ7F7FJ-
+L---JF-JLJ.||-FJLJJ7
+|F|F-JF---7F7-L7L|7|
+|FFJF7L7F-JF7|JL---7
+7-L-JL7||F7|L7F-7F7|
+L.L7LFJ|||||FJL7||LJ
+L7JLJL-JLJLJL--JLJ.L
\ No newline at end of file