-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 2023-10 WIP * 2023-10 WIP - as finished with 2 stars * Refactoring * Improve Dijkstra All * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring * Refactoring --------- Co-authored-by: Juris <[email protected]>
- Loading branch information
Showing
14 changed files
with
514 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
..... | ||
.S-7. | ||
.|.|. | ||
.L-J. | ||
..... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
..F7. | ||
.FJ|. | ||
SJ.L7 | ||
|F--J | ||
LJ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
........... | ||
.S-------7. | ||
.|F-----7|. | ||
.||.....||. | ||
.||.....||. | ||
.|L-7.F-J|. | ||
.|..|.|..|. | ||
.L--J.L--J. | ||
........... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Large diffs are not rendered by default.
Oops, something went wrong.
140 changes: 140 additions & 0 deletions
140
scala2/src/main/scala/jurisk/adventofcode/y2023/Advent10.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package jurisk.adventofcode.y2023 | ||
|
||
import cats.implicits._ | ||
import jurisk.adventofcode.y2023.pipe.Pipe._ | ||
import jurisk.adventofcode.y2023.pipe.{CoordsWithDirection, Pipe} | ||
import jurisk.algorithms.pathfinding.{Bfs, Dijkstra} | ||
import jurisk.geometry.Direction2D.{E, N, S, W} | ||
import jurisk.geometry.{Coords2D, Field2D} | ||
import jurisk.utils.CollectionOps.IterableOps | ||
import jurisk.utils.FileInput._ | ||
import jurisk.utils.Parsing.StringOps | ||
|
||
object Advent10 { | ||
final case class Input( | ||
animalAt: Coords2D, | ||
field: Field2D[Pipe], | ||
) { | ||
def at(coords: Coords2D): Pipe = | ||
field.atOrElse(coords, Empty) | ||
} | ||
|
||
object Input { | ||
def parse(s: String): Input = { | ||
val chars: Field2D[Char] = Field2D.parseFromString(s, identity) | ||
|
||
val animalAt = chars.filterCoordsByValue(_ == 'S').singleResultUnsafe | ||
|
||
val mapping = Map( | ||
'|' -> N_S, | ||
'-' -> E_W, | ||
'L' -> N_E, | ||
'J' -> N_W, | ||
'7' -> S_W, | ||
'F' -> S_E, | ||
'.' -> Empty, | ||
'S' -> Empty, | ||
) | ||
|
||
val field: Field2D[Pipe] = Field2D.parseFromString(s, mapping.apply) | ||
|
||
// Find a suitable pipe to replace the `S` animal cell | ||
val animalPipe = Pipe.NonEmpty.filter { candidate => | ||
candidate.connections.forall { direction => | ||
field | ||
.atOrElse(animalAt + direction, Empty) | ||
.connections | ||
.contains(direction.invert) | ||
} | ||
}.singleElementUnsafe | ||
|
||
val updatedField = field.updatedAtUnsafe(animalAt, animalPipe) | ||
|
||
Input(animalAt, updatedField) | ||
} | ||
} | ||
|
||
def parse(input: String): Input = Input.parse(input) | ||
|
||
// Find distance to all nodes we can get to while going on the track, take the maximum | ||
def part1(data: Input): Int = | ||
Dijkstra | ||
.dijkstraAll( | ||
data.animalAt, | ||
(c: Coords2D) => connectedNeighbours(data.field, c).map(x => (x, 1)), | ||
) | ||
.map { case (coord @ _, (parent @ _, distance)) => | ||
distance | ||
} | ||
.max | ||
|
||
def part2(data: Input): Int = { | ||
// All the track coordinates | ||
val trackCoords = Dijkstra | ||
.dijkstraAll( | ||
data.animalAt, | ||
(c: Coords2D) => connectedNeighbours(data.field, c).map(x => (x, 1)), | ||
) | ||
.keySet | ||
|
||
// The field with only track cells left, others are Empty | ||
val onlyTrack = data.field.mapByCoordsWithValues { case (c, v) => | ||
if (trackCoords.contains(c)) v else Empty | ||
} | ||
|
||
// We don't know which direction is inside and which is outside for the animal coordinates, | ||
// but we can figure it out for the top left coordinates. This depends on the order in which `allCoords` | ||
// returns coordinates. | ||
val topLeftFullCoord = onlyTrack.allCoords | ||
.find(x => onlyTrack.atOrElse(x, Empty) != Empty) | ||
.getOrElse("Failed to find".fail) | ||
val topLeftFullValue = onlyTrack.atOrElse(topLeftFullCoord, Empty) | ||
val topLeftStartDirection = topLeftFullValue match { | ||
case Pipe.Empty => "Unexpected".fail | ||
case Pipe.N_S => S | ||
case Pipe.E_W => W | ||
case Pipe.N_E => N | ||
case Pipe.N_W => W | ||
case Pipe.S_W => S | ||
case Pipe.S_E => E | ||
} | ||
|
||
val start = CoordsWithDirection( | ||
coords = topLeftFullCoord, | ||
direction = topLeftStartDirection, | ||
) | ||
|
||
// Which direction was the animal facing on each track segment? | ||
val trackCoordsWithAnimalDirection = | ||
Bfs.bfsReachable[CoordsWithDirection]( | ||
start, | ||
x => x.nextOnTrack(data.field) :: Nil, | ||
) | ||
|
||
// Which cells were on the right of the track, as the animal was walking around it? | ||
val rightCoordinateSeeds = trackCoordsWithAnimalDirection | ||
.flatMap(x => x.coordsToTheRight(data.field).toSet) | ||
.toSet | ||
.diff(trackCoords) | ||
|
||
// Let's flood-fill from the coordinates which we know are on the right side of the track | ||
val floodFilled = rightCoordinateSeeds.flatMap { c => | ||
Bfs.bfsReachable[Coords2D]( | ||
c, | ||
x => data.field.adjacent4(x).toSet.diff(trackCoords).toList, | ||
) | ||
} | ||
|
||
floodFilled.size | ||
} | ||
|
||
def parseFile(fileName: String): Input = | ||
parse(readFileText(fileName)) | ||
|
||
def main(args: Array[String]): Unit = { | ||
val realData: Input = parseFile("2023/10.txt") | ||
|
||
println(s"Part 1: ${part1(realData)}") | ||
println(s"Part 2: ${part2(realData)}") | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
scala2/src/main/scala/jurisk/adventofcode/y2023/pipe/CoordsWithDirection.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package jurisk.adventofcode.y2023.pipe | ||
|
||
import jurisk.adventofcode.y2023.pipe.Pipe.Empty | ||
import jurisk.geometry.Direction2D._ | ||
import jurisk.geometry.{Coords2D, Direction2D, Field2D, Rotation} | ||
import jurisk.utils.CollectionOps.IterableOps | ||
|
||
final case class CoordsWithDirection( | ||
coords: Coords2D, | ||
direction: CardinalDirection2D, | ||
) { | ||
// Next coordinate & direction if we walk along the track | ||
def nextOnTrack(field: Field2D[Pipe]): CoordsWithDirection = { | ||
val nextCoords = coords + direction | ||
val nextSquare = field.atOrElse(nextCoords, Empty) | ||
|
||
val nextDirection = nextSquare.connections | ||
.filterNot(_ == direction.invert) | ||
.singleElementUnsafe | ||
|
||
CoordsWithDirection( | ||
coords = nextCoords, | ||
direction = nextDirection, | ||
) | ||
} | ||
|
||
// Coordinates to the right of these `coords`, if facing in `direction` | ||
def coordsToTheRight(field: Field2D[Pipe]): List[Coords2D] = { | ||
val diffs: List[Direction2D] = field.atOrElse(coords, Empty) match { | ||
case Pipe.Empty => Nil | ||
|
||
case Pipe.N_S | Pipe.E_W => | ||
direction.rotate(Rotation.Right90) :: Nil | ||
|
||
case Pipe.N_E => | ||
direction match { | ||
case Direction2D.E => W :: SW :: S :: Nil | ||
case _ => Nil | ||
} | ||
case Pipe.N_W => | ||
direction match { | ||
case Direction2D.N => S :: SE :: E :: Nil | ||
case _ => Nil | ||
} | ||
case Pipe.S_W => | ||
direction match { | ||
case Direction2D.W => N :: NE :: E :: Nil | ||
case _ => Nil | ||
} | ||
case Pipe.S_E => | ||
direction match { | ||
case Direction2D.S => N :: NW :: W :: Nil | ||
case _ => Nil | ||
} | ||
} | ||
|
||
diffs.map(x => coords + x) | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
scala2/src/main/scala/jurisk/adventofcode/y2023/pipe/Pipe.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package jurisk.adventofcode.y2023.pipe | ||
|
||
import jurisk.geometry.Direction2D.{CardinalDirection2D, E, N, S, W} | ||
import jurisk.geometry.{Coords2D, Field2D} | ||
|
||
sealed trait Pipe { | ||
def symbol: Char | ||
def connections: Set[CardinalDirection2D] | ||
} | ||
|
||
case object Pipe { | ||
def connectedNeighbours( | ||
field: Field2D[Pipe], | ||
coords: Coords2D, | ||
): List[Coords2D] = | ||
field | ||
.atOrElse(coords, Empty) | ||
.connections | ||
.filter { direction => | ||
field | ||
.atOrElse(coords + direction, Empty) | ||
.connections | ||
.contains(direction.invert) | ||
} | ||
.map { direction => | ||
coords + direction | ||
} | ||
.toList | ||
|
||
val NonEmpty: List[Pipe] = N_S :: E_W :: N_E :: N_W :: S_W :: S_E :: Nil | ||
val All: List[Pipe] = Empty :: NonEmpty | ||
|
||
case object Empty extends Pipe { | ||
override def symbol: Char = '░' | ||
override def connections: Set[CardinalDirection2D] = Set.empty | ||
} | ||
|
||
case object N_S extends Pipe { | ||
override def symbol: Char = '┃' | ||
override def connections: Set[CardinalDirection2D] = Set(N, S) | ||
} | ||
|
||
case object E_W extends Pipe { | ||
override def symbol: Char = '━' | ||
override def connections: Set[CardinalDirection2D] = Set(E, W) | ||
} | ||
|
||
case object N_E extends Pipe { | ||
override def symbol: Char = '┗' | ||
override def connections: Set[CardinalDirection2D] = Set(N, E) | ||
} | ||
|
||
case object N_W extends Pipe { | ||
override def symbol: Char = '┛' | ||
override def connections: Set[CardinalDirection2D] = Set(N, W) | ||
} | ||
|
||
case object S_W extends Pipe { | ||
override def symbol: Char = '┓' | ||
override def connections: Set[CardinalDirection2D] = Set(S, W) | ||
} | ||
|
||
case object S_E extends Pipe { | ||
override def symbol: Char = '┏' | ||
override def connections: Set[CardinalDirection2D] = Set(S, E) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.