Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 47 additions & 55 deletions 2021/src/main/scala/aoc2021/Day08.scala
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
package aoc2021

import nmcb.*

import scala.annotation.tailrec

object Day08 extends AoC:

val puzzle =
lines
.map: line =>
val input = line.split("\\|")(0).trim.split(" ").toList
val output = line.split("\\|")(1).trim.split(" ").toList
(input, output)
.toList


val leds0 = Set(0,1,2,4,5,6)
val leds1 = Set(2,5)
val leds2 = Set(0,2,3,4,6)
val leds3 = Set(0,2,3,5,6)
val leds4 = Set(1,2,3,5)
val leds5 = Set(0,1,3,5,6)
val leds6 = Set(0,1,3,4,5,6)
val leds7 = Set(0,2,5)
val leds8 = Set(0,1,2,3,4,5,6)
val leds9 = Set(0,1,2,3,5,6)
type Encoding = (input: Vector[String], output: Vector[String])

val encodings: Vector[Encoding] =
lines.collect: line =>
val input = line.split("\\|")(0).trim.split(" ").toVector
val output = line.split("\\|")(1).trim.split(" ").toVector
(input = input, output = output)

type Wiring = String

def decode(wiring: Wiring)(digit: String): Option[Int] =

val leds0 = Set(0, 1, 2, 4, 5, 6)
val leds1 = Set(2, 5)
val leds2 = Set(0, 2, 3, 4, 6)
val leds3 = Set(0, 2, 3, 5, 6)
val leds4 = Set(1, 2, 3, 5)
val leds5 = Set(0, 1, 3, 5, 6)
val leds6 = Set(0, 1, 3, 4, 5, 6)
val leds7 = Set(0, 2, 5)
val leds8 = Set(0, 1, 2, 3, 4, 5, 6)
val leds9 = Set(0, 1, 2, 3, 5, 6)

val leds = digit.map(code => wiring.indexOf(code)).toSet
if (leds == leds0) Some(0)
else if (leds == leds1) Some(1)
Expand All @@ -41,33 +42,33 @@ object Day08 extends AoC:
else if (leds == leds9) Some(9)
else None

def valid(wiring: Wiring)(input: List[String]): Boolean =
def valid(wiring: Wiring)(input: Vector[String]): Boolean =

@tailrec
def loop(todo: List[String], digitsFound: Set[Int] = Set.empty): Boolean =
def loop(todo: Vector[String], digitsFound: Set[Int] = Set.empty): Boolean =

val allDigits: Set[Int] = Set(0,1,2,3,4,5,6,7,8,9)
val allDigits: Set[Int] = Set(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

def inDigitsFound(digit: Int): Boolean =
digitsFound.contains(digit)

def notInDigitsLeftToValidate(digit: Int): Boolean =
!(allDigits -- digitsFound).contains(digit)

todo match
case Nil => digitsFound == allDigits
case encoding :: encodings => decode(wiring)(encoding) match
case None => false
case Some(digit) if inDigitsFound(digit) => false
todo.runtimeChecked match
case Vector() => digitsFound == allDigits
case encoding +: encodings => decode(wiring)(encoding) match
case None => false
case Some(digit) if inDigitsFound(digit) => false
case Some(digit) if notInDigitsLeftToValidate(digit) => false
case Some(digit) => loop(encodings, digitsFound + digit)
case Some(digit) => loop(encodings, digitsFound + digit)

loop(input)


override lazy val answer1: Int =
puzzle
.map: (_,output) =>
def solve1(encodings: Vector[Encoding]): Int =
encodings
.map: (_, output) =>
output.count: digit =>
val is1 = digit.length == 2
val is7 = digit.length == 3
Expand All @@ -76,28 +77,19 @@ object Day08 extends AoC:
is1 || is4 || is7 || is8
.sum

override lazy val answer2: Int =
def solve2(encodings: Vector[Encoding]): Int =
@tailrec
def search(wirings: List[String])(input: List[String]): String =
val wiring = wirings.head
if valid(wiring)(input) then
wiring
else
search(wirings.tail)(input)

val init: List[Wiring] =
"abcdefg".permutations.toList

val numbers: List[Int] =
puzzle.zipWithIndex.map:
case ((input,output),idx) =>
val wiring: Wiring =
search(init)(input)
val number: Int =
output
.foldLeft(""): (num,digit) =>
num + decode(wiring)(digit).get
.toInt
number

numbers.sum
def search(wirings: Vector[String], input: Vector[String]): String =
wirings.runtimeChecked match
case wiring +: rest if valid(wiring)(input) => wiring
case _ +: rest => search(rest, input)
val wirings = "abcdefg".permutations.toVector
encodings
.map: (input, output) =>
val wiring: Wiring = search(wirings, input)
output.foldLeft("")((num, digit) => num + decode(wiring)(digit).get).toInt
.sum

override lazy val answer1: Int = solve1(encodings)
override lazy val answer2: Int = solve2(encodings)

55 changes: 31 additions & 24 deletions 2021/src/main/scala/aoc2021/Day09.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aoc2021

import nmcb.*
import nmcb.predef.*
import nmcb.pos.*

import scala.annotation.tailrec
Expand All @@ -9,32 +10,38 @@ object Day09 extends AoC:

val floor: Grid[Int] = Grid.fromLines(lines).map(_.toString.toInt)

type PosHeight = (Pos, Int)

extension (floor: Grid[Int])

def neighbouringHeights(p: Pos): Set[(Pos, Int)] =
def neighbouringHeights(p: Pos): Set[PosHeight] =
p.adjoint4.flatMap(p => floor.peekOption(p).map(h => p -> h))

def upstream(p: Pos): Set[Pos] =
neighbouringHeights(p).filter((_, nh) => nh < 9 && nh > floor.peek(p)).map((n, _) => n)

def heights: List[(Pos, Int)] =
floor.elements.foldLeft(List.empty[(Pos,Int)]):
case (result, (p, h)) if floor.neighbouringHeights(p).forall((_, nh) => h < nh) => (p, h) :: result
case (result, _) => result


override lazy val answer1: Int =
floor.heights.map((_, h) => h + 1).sum


override lazy val answer2: Int =
@tailrec
def loop(todo: Set[Pos], acc: Set[Pos] = Set.empty): Int =
if todo.isEmpty then
acc.size
else
val current = todo.head
val upstream = floor.upstream(current)
loop(upstream ++ todo.tail, acc + current)

floor.heights.map((p, _) => loop(Set(p))).sorted.reverse.take(3).product
neighbouringHeights(p).filter(ph => ph.right < 9 && ph.right > floor.peek(p)).map(_.element)

def posHeights: Vector[PosHeight] =
floor.elements.foldLeft(Vector.empty[PosHeight]):
case (result, ph) if floor.neighbouringHeights(ph.left).forall(nh => ph.right < nh.right) => ph +: result
case (result, _) => result

def solve1(floor: Grid[Int]): Int =
floor.posHeights.map(ph => ph.right + 1).sum

def solve2(floor: Grid[Int]): Int =
def basinSize(p: Pos): Int =
@tailrec
def loop(todo: Set[Pos], result: Set[Pos] = Set.empty): Int =
if todo.isEmpty then
result.size
else
val current = todo.head
val next = todo.tail
val upstream = floor.upstream(current)
loop(upstream ++ next, result + current)
loop(Set(p))
floor.positions.map(basinSize).toVector.sorted.reverse.take(3).product


override lazy val answer2: Int = solve2(floor)
override lazy val answer1: Int = solve1(floor)