Skip to content

Commit

Permalink
Day 10
Browse files Browse the repository at this point in the history
  • Loading branch information
lenguyenthanh committed Dec 10, 2023
1 parent d365586 commit 0211966
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 3 deletions.
7 changes: 4 additions & 3 deletions Day03.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package aoc

import cats.effect.*
import cats.parse.Numbers.digits
import cats.parse.Parser as P
import cats.parse.Rfc5234.crlf
import fs2.Stream

object Day03 extends AOCApp(2023, 3):
Expand Down Expand Up @@ -66,6 +63,10 @@ object Day03 extends AOCApp(2023, 3):
engines.foldLeft(empty)(_ add _)

object Parser:
import cats.parse.Numbers.digits
import cats.parse.Parser as P
import cats.parse.Rfc5234.crlf

lazy val ignore = P.char('.') | crlf
lazy val number = (ignore.rep0.with1 *> (P.caret.with1 ~ digits) <* ignore.rep0).map: (caret, value) =>
Engine.Number(Point(caret.col, caret.line), value.length, value.toLong)
Expand Down
118 changes: 118 additions & 0 deletions Day10.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package aoc

import cats.effect.*
import cats.syntax.all.*
import fs2.Stream

object Day10 extends AOCApp(2023, 10):

def part1(input: Stream[IO, String]): IO[String] =
sovle(input, _.solveP1())

def part2(input: Stream[IO, String]): IO[String] =
sovle(input, _.solveP2())

def sovle(input: Stream[IO, String], f: Solution => Any): IO[String] =
input
.map(Parser.parse)
.map(f)
.map(_.toString)
.compile
.lastOrError

enum Direction:
case VP, HP, NE, NW, SE, SW

case class Point(x: Int, y: Int)

case class Solution(start: Point, map: Map[Point, Direction]):
lazy val possibilities: List[Map[Point, Direction]] =
Direction.values.toList.map(map.updated(start, _))

def solveP1(): Int =
possibilities.collectFirstSome(findPath).get.size / 2

def solveP2(): Int =
val (map, ls) = possibilities.collectFirstSome(x => findPath(x).map(x -> _)).get
val path = map.filter(x => ls.contains(x._1))
val size = 140
val all = for
x <- List.range(0, size)
y <- List.range(0, size)
yield Point(x, y)
all.count(isInside(path, size))

def isInside(path: Map[Point, Direction], size: Int)(p: Point): Boolean =
if path.contains(p) then false
else
val n = List
.range(p.x + 1, size)
.foldLeft(0 -> none[Direction]): (pr, xd) =>
val (acc, begin) = pr
path.get(Point(xd, p.y)) match
case None => acc -> none
case Some(d) =>
if d == Direction.NE || d == Direction.SE then acc -> d.some
else if d == Direction.HP then acc + begin.fold(1)(_ => 0) -> begin
else if d == Direction.NW || d == Direction.SW then
begin match
case None => acc -> begin
case Some(x) =>
if x == Direction.NE && d == Direction.NW then acc -> none
else if x == Direction.NE && d == Direction.SW then acc + 1 -> none
else if x == Direction.SE && d == Direction.SW then acc -> none
else if x == Direction.SE && d == Direction.NW then acc + 1 -> none
else acc -> begin
else acc + 1 -> none
n._1 % 2 == 1

def findPath(m: Map[Point, Direction]) =
val next = connectedTo(start, m(start)).head
loop(m, next, start, Set(start, next))

@annotation.tailrec
private def loop(map: Map[Point, Direction], next: Point, current: Point, acc: Set[Point]): Option[Set[Point]] =
map.get(next) match
case None => None
case Some(x) =>
connectedTo(next, x).find(_ != current) match
case None => None
case Some(n) if n == start =>
if connectedTo(start, map(start)).contains(next) then acc.some
else none
case Some(n) if acc.contains(n) => None
case Some(n) => loop(map, n, next, acc + n)

def connectedTo(point: Point, direction: Direction): List[Point] =
direction match
case Direction.VP => List(Point(point.x, point.y + 1), Point(point.x, point.y - 1))
case Direction.HP => List(Point(point.x + 1, point.y), Point(point.x - 1, point.y))
case Direction.NE => List(Point(point.x, point.y - 1), Point(point.x + 1, point.y))
case Direction.NW => List(Point(point.x, point.y - 1), Point(point.x - 1, point.y))
case Direction.SE => List(Point(point.x + 1, point.y), Point(point.x, point.y + 1))
case Direction.SW => List(Point(point.x - 1, point.y), Point(point.x, point.y + 1))

object Parser:
import cats.parse.Parser as P
import cats.parse.Rfc5234.lf

lazy val ignore = P.char('.').backtrack | lf
lazy val direction = P.charIn('|', '-', 'L', 'J', 'F', '7').map(charToDirection)
lazy val startOrDirection = P.char('S').backtrack.as(().asLeft) | direction.map(_.asRight)
lazy val tile = (ignore.rep0.with1 *> (P.caret.with1 ~ startOrDirection) <* ignore.rep0).map: (caret, value) =>
Point(caret.col, caret.line) -> value

lazy val tiles = tile.rep0.map: tiles =>
val start = tiles.collectFirst { case (p, Left(_)) => p }.get
val map = tiles.collect { case (p, Right(d)) => p -> d }.toMap
Solution(start, map)

lazy val charToDirection: Char => Direction = _ match
case '|' => Direction.VP
case '-' => Direction.HP
case 'L' => Direction.NE
case 'J' => Direction.NW
case 'F' => Direction.SE
case '7' => Direction.SW

def parse(str: String) = tiles.parseAll(str).toOption.get

0 comments on commit 0211966

Please sign in to comment.