11import Day06 .Cell .*
22import Day06 .Direction .*
33import cats .data .Store
4- import cats .derived .*
54import cats .syntax .all .*
65
76object Day06 :
@@ -13,7 +12,7 @@ object Day06:
1312 def right : Pos = Pos (row, col + 1 )
1413 def down : Pos = Pos (row + 1 , col)
1514
16- def next (dir : Direction ): Pos = dir match {
15+ def next (direction : Direction ): Pos = direction match {
1716 case Left => left
1817 case Up => up
1918 case Right => right
@@ -42,19 +41,53 @@ object Day06:
4241 enum Tile :
4342 case Empty , Obstruction
4443
44+ object Tile :
45+ def parse (c : Char ): Option [Tile ] = c match {
46+ case '.' => Empty .some
47+ case '#' => Obstruction .some
48+ case _ => None
49+ }
50+
4551 enum Cell :
4652 case TileCell (tile : Tile )
4753 case GuardCell (direction : Direction )
4854
49- type LabWithNoGuardStore = Store [Pos , Option [Tile ]]
55+ def toTile : Tile = this match {
56+ case TileCell (tile) => tile
57+ case GuardCell (_) => Tile .Empty
58+ }
59+
60+ object Cell :
61+ def parse (c : Char ): Option [Cell ] =
62+ Tile .parse(c).map(TileCell .apply).orElse(Direction .parse(c).map(GuardCell .apply))
5063
5164 type LabStore = Store [Pos , Option [(Tile , Guard )]]
5265
53- def extractGuard (store : LabStore ): Option [Guard ] = store.extract.map(_._2)
66+ val unguardedLabStore : LabStore = {
67+ val outOfBoundsPos = Pos (row = - 1 , col = - 1 )
68+ Store (
69+ {
70+ case Pos (row = 0 , col = 0 ) => (Tile .Empty , Guard (outOfBoundsPos, Direction .Left )).some
71+ case _ => None
72+ },
73+ s = outOfBoundsPos
74+ )
75+ }
76+
77+ extension (store : LabStore )
78+
79+ def getGuard : Option [Guard ] = store.extract.map(_._2)
80+
81+ def afterGuardStep : LabStore =
82+ (for {
83+ oldGuard <- store.getGuard
84+ newGuard <- oldGuard.afterStep(store)
85+ newStore = store.seek(newGuard.pos).map(_.map((tile, _) => (tile, newGuard)))
86+ } yield newStore).getOrElse(unguardedLabStore)
5487
5588 case class Guard (pos : Pos , direction : Direction ):
5689
57- def updated (store : LabStore ): Option [Guard ] =
90+ def afterStep (store : LabStore ): Option [Guard ] =
5891 val nextPos = pos.next(direction)
5992 store
6093 .peek(nextPos)
@@ -67,36 +100,28 @@ object Day06:
67100
68101 val toVectors : Vector [Vector [Cell ]] = cells.map(_.toVector).toVector
69102
70- val outOfBoundsPos : Pos = Pos (row = - 1 , col = - 1 )
71-
72- val toStoreWithNoGuard : LabWithNoGuardStore =
103+ val toStore : LabStore =
73104 Store (
74- p =>
75- toVectors
76- .get(p.row)
77- .flatMap(_.get(p.col).map {
78- case TileCell (tile) => tile
79- case GuardCell (_) => Tile .Empty
80- }),
105+ p => toVectors.get(p.row).flatMap(_.get(p.col).map(_.toTile)).map((_, guard)),
81106 s = guard.pos
82107 )
83108
84- def withGuard (store : LabWithNoGuardStore ): LabStore = store.map(_.map((_, guard)))
85-
86- def step (store : LabStore ): LabStore =
87- extractGuard(store).fold(ifEmpty = store) { oldGuard =>
88- oldGuard
89- .updated(store)
90- .map(newGuard => store.seek(newGuard.pos).map(_.map((tile, _) => (tile, newGuard))))
91- .getOrElse(store.seek(outOfBoundsPos)) // TODO: improve! 🔥🔥🔥
92- }
93-
94- def allDistinctGuardPositions (store : LabWithNoGuardStore ): List [Pos ] =
109+ def allDistinctGuardPositionsCount : Int =
95110 List
96- .unfold(init = withGuard(store))(currentStore =>
97- extractGuard(currentStore).map(guard => (guard.pos, step(currentStore)))
98- )
111+ .unfold(init = toStore)(store => store.getGuard.map(guard => (guard.pos, store.afterGuardStep)))
99112 .distinct
113+ .length
100114
101115 object Lab :
102- def parse (rows : List [String ]): Option [Lab ] = ??? // rows.map(_.toList)
116+
117+ def parse (rows : List [String ]): Option [Lab ] = for {
118+ cells <- rows.traverse(_.toList.traverse(Cell .parse))
119+ (guardPos, direction) <- findGuard(cells)
120+ } yield Lab (cells, Guard (guardPos, direction))
121+
122+ def findGuard (cells : List [List [Cell ]]): Option [(Pos , Direction )] =
123+ cells
124+ .map(_.zipWithIndex)
125+ .zipWithIndex
126+ .map((row, rowIndex) => row.map((cell, colIndex) => (Pos (rowIndex, colIndex), cell)))
127+ .collectFirstSome(_.collectFirst { case (pos, GuardCell (direction)) => (pos, direction) })
0 commit comments