day 10 puzzle 2
This commit is contained in:
parent
ee3a02060b
commit
8d55a3c7b3
16
README.md
16
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
|
I will try and do some problems (probably not all of them) in Scala
|
||||||
|
|
||||||
## Progress:
|
## Progress:
|
||||||
#### Stars: 18 / 50
|
#### Stars: 20 / 50
|
||||||
| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|
| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|
||||||
|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|
|
|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:------------------:|
|
||||||
| | | | | 1<br>:star::star: | 2<br>:star::star: | 3<br>:star::star: |
|
| | | | | 1<br>:star::star: | 2<br>:star::star: | 3<br>:star::star: |
|
||||||
| 4<br>:star::star: | 5<br>:star::star: | 6<br>:star::star: | 7<br>:star::star: | 8<br>:star::star: | 9<br>:star::star: | 10<br>:star: |
|
| 4<br>:star::star: | 5<br>:star::star: | 6<br>:star::star: | 7<br>:star::star: | 8<br>:star::star: | 9<br>:star::star: | 10<br>:star::star: |
|
||||||
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
|
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
|
||||||
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
|
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
|
||||||
| 25 | | | | | | |
|
| 25 | | | | | | |
|
77
src/day10/Painter.scala
Normal file
77
src/day10/Painter.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
147
src/day10/Puzzle2.scala
Normal file
147
src/day10/Puzzle2.scala
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,11 @@
|
|||||||
package day10
|
package day10
|
||||||
|
|
||||||
class Walker {
|
class Walker {
|
||||||
private var x: Int = 0
|
protected var x: Int = 0
|
||||||
private var y: Int = 0
|
protected var y: Int = 0
|
||||||
private var lastX: Int = -1
|
protected var lastX: Int = -1
|
||||||
private var lastY: Int = -1
|
protected var lastY: Int = -1
|
||||||
private var distance: Int = 0
|
protected var distance: Int = 0
|
||||||
|
|
||||||
val OFFSETS: Array[(Int, Int)] = Array(
|
|
||||||
(1, 0), (0, 1), (-1, 0), (0, -1)
|
|
||||||
)
|
|
||||||
|
|
||||||
def this(startX: Int, startY: Int) = {
|
def this(startX: Int, startY: Int) = {
|
||||||
this()
|
this()
|
||||||
@ -41,7 +37,7 @@ class Walker {
|
|||||||
|
|
||||||
private def getNextPos(grid: Array[Array[Byte]], width: Int, height: Int): (Int, Int) = {
|
private def getNextPos(grid: Array[Array[Byte]], width: Int, height: Int): (Int, Int) = {
|
||||||
val curTile: Byte = grid(y)(x)
|
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 x2: Int = x + dx
|
||||||
val y2: Int = y + dy
|
val y2: Int = y + dy
|
||||||
if (x2 != lastX || y2 != lastY) {
|
if (x2 != lastX || y2 != lastY) {
|
||||||
@ -58,3 +54,9 @@ class Walker {
|
|||||||
throw new Exception("Dead-end path")
|
throw new Exception("Dead-end path")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Walker {
|
||||||
|
val OFFSETS: Array[(Int, Int)] = Array(
|
||||||
|
(1, 0), (0, 1), (-1, 0), (0, -1)
|
||||||
|
)
|
||||||
|
}
|
24
tests/day10/Puzzle2Test.scala
Normal file
24
tests/day10/Puzzle2Test.scala
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
9
tests_res/day10/input3.txt
Normal file
9
tests_res/day10/input3.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
...........
|
||||||
|
.S-------7.
|
||||||
|
.|F-----7|.
|
||||||
|
.||.....||.
|
||||||
|
.||.....||.
|
||||||
|
.|L-7.F-J|.
|
||||||
|
.|..|.|..|.
|
||||||
|
.L--J.L--J.
|
||||||
|
...........
|
9
tests_res/day10/input4.txt
Normal file
9
tests_res/day10/input4.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
..........
|
||||||
|
.S------7.
|
||||||
|
.|F----7|.
|
||||||
|
.||....||.
|
||||||
|
.||....||.
|
||||||
|
.|L-7F-J|.
|
||||||
|
.|..||..|.
|
||||||
|
.L--JL--J.
|
||||||
|
..........
|
10
tests_res/day10/input5.txt
Normal file
10
tests_res/day10/input5.txt
Normal file
@ -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...
|
10
tests_res/day10/input6.txt
Normal file
10
tests_res/day10/input6.txt
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user