diff --git a/aoc/src/main/scala/aoc/y2019/Day04.scala b/aoc/src/main/scala/aoc/y2019/Day04.scala new file mode 100644 index 0000000..a03255b --- /dev/null +++ b/aoc/src/main/scala/aoc/y2019/Day04.scala @@ -0,0 +1,40 @@ +package aoc.y2019 + +import aoc.Day + +object Day04 extends Day { + + import fastparse._, SingleLineWhitespace._ + + def parseNumber[_: P]: P[Int] = P(CharIn("0-9").rep(1).!).map(_.toInt) + def parseRange[_: P]: P[Range.Inclusive] = P(parseNumber ~ "-" ~ parseNumber).map { + case (a, b) => a to b + } + + def validPassword(password: Int, range: Range, repetitionBounded: Boolean): Boolean = { + lazy val isSixDigits = password.toString.length == 6 + lazy val isWithinRange = range.contains(password) + lazy val hasRepetition = + if(!repetitionBounded) + password.toString.toSeq.groupBy(identity).exists(_._2.length >= 2) + else + password.toString.toSeq.groupBy(identity).exists(_._2.length == 2) + lazy val isIncreasing = password.toString.toSeq.sorted == password.toString.toSeq + + isSixDigits && isWithinRange && hasRepetition && isIncreasing + } + + def numberValidPasswords(rangeString: String, repetitionBounded: Boolean): Int = { + val range = parse(rangeString, parseRange(_)).get.value + range.count(validPassword(_, range, repetitionBounded)) + } + + override def part1(input: String): String = { + numberValidPasswords(input, repetitionBounded = false).toString + } + + override def part2(input: String): String = { + numberValidPasswords(input, repetitionBounded = true).toString + } + +} diff --git a/aoc/src/main/scala/aoc/y2019/package.scala b/aoc/src/main/scala/aoc/y2019/package.scala index 1bf5377..e677d39 100644 --- a/aoc/src/main/scala/aoc/y2019/package.scala +++ b/aoc/src/main/scala/aoc/y2019/package.scala @@ -6,6 +6,7 @@ package object y2019 extends Year { "1" -> Day01, "2" -> Day02, "3" -> Day03, + "4" -> Day04, ) }