Day 16: Parser Fun

This commit is contained in:
Alexander Gehrke 2021-12-17 20:10:22 +01:00
parent 553b52afa0
commit 031ef41462
11 changed files with 117 additions and 2 deletions

View file

@ -14,6 +14,8 @@ lazy val root = project
"org.typelevel" %% "cats-core" % "2.6.1",
"org.typelevel" %% "cats-effect" % "3.1.1",
"org.typelevel" %% "kittens" % "3.0.0-M1",
"org.tpolecat" %% "atto-core" % "0.9.5",
)
)

View file

@ -0,0 +1 @@
D2FE28

View file

@ -0,0 +1 @@
38006F45291200

View file

@ -0,0 +1 @@
EE00D40C823060

View file

@ -0,0 +1 @@
8A004A801A8002F478

View file

@ -0,0 +1 @@
620080001611562C8802118E34

View file

@ -0,0 +1 @@
C0015000016115A2E0802F182340

View file

@ -0,0 +1 @@
A0016C880162017C3686B18A3D4780

1
input/2021/day16.txt Normal file
View file

@ -0,0 +1 @@
020D790050D26C13EC1348326D336ACE00EC299E6A8B929ED59C880502E00A526B969F62BF35CB4FB15B93A6311F67F813300470037500043E2D4218FA000864538E905A39CAF77386E35AB01802FC01BA00021118617C1F00043A3F4748A53CF66008D00481272D73308334EDB0ED304E200D4E94CF612A49B40036C98A7CF24A53CA94C6370FBDCC9018029600ACD529CA9A4F62ACD2B5F928802F0D2665CA7D6CC013919E78A3800D3CF7794A8FC938280473057394AFF15099BA23CDD37A08400E2A5F7297F916C9F97F82D2DFA734BC600D4E3BC89CCBABCBE2B77D200412599244D4C0138C780120CC67E9D351C5AB4E1D4C981802980080CDB84E034C5767C60124F3BC984CD1E479201232C016100662D45089A00087C1084F12A724752BEFEA9C51500566759BF9BE6C5080217910CD00525B6350E8C00E9272200DCE4EF4C1DD003952F7059BCF675443005680103976997699795E830C02E4CBCE72EFC6A6218C88C9DF2F3351FCEF2D83CADB779F59A052801F2BAACDAE7F52A8190073937FE1D700439234DBB4F7374DC0CC804CF1006A0D47B8A4200F445865170401F8251662D100909401AB8803313217C680004320D43F871308D140C010E0069E7EDD1796AFC8255800052E20043E0F42A8B6400864258E51088010B85910A0F4ECE1DFE069C0229AE63D0B8DC6F82529403203305C00E1002C80AF5602908400A20240100852401E98400830021400D30029004B6100294008400B9D0023240061C000D19CACCD9005F694AEF6493D3F9948DEB3B4CC273FFD5E9AD85CFDFF6978B80050392AC3D98D796449BE304FE7F0C13CD716656BD0A6002A67E61A400F6E8029300B300B11480463D004C401889B1CA31800211162204679621200FCAC01791CF6B1AFCF2473DAC6BF3A9F1700016A3D90064D359B35D003430727A7DC464E6401594A57C93A0084CC56A662B8C00AA424989F2A9112

View file

@ -1,6 +1,108 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import de.qwertyuiop.aoc.lib.{*, given}
import cats.*, cats.implicits.given
import atto.*, Atto.*
def day16(using InputSource): Unit = ???
def day16(using InputSource): Unit =
val pkg = input().head
println(BITSParser(pkg) | BITSParser.versionSum)
println(BITSParser(pkg) | BITSParser.evaluate)
def day16test: Unit =
import BITSParser.*
val tests = List(
("C200B40A82", 3),
("04005AC33890", 54),
("880086C3E88112", 7),
("CE00C43D881120", 9),
("D8005AC2A8F0", 1),
("F600BC2D8F", 0),
("9C005AC2F8F0", 0),
("9C0141080250320F1802104A08",1 ),
)
tests.foreach((hex, expected) => {val pkg = BITSParser(hex); println((expected == evaluate(pkg), hex, expected, evaluate(pkg), show(pkg)))})
object BITSParser:
def apply(hex: String): Payload =
val bin = hex.toVector.reverse.map(c => Integer.parseInt(c.toString,16).toBinaryString.padLeft(4,'0')).reverse.mkString
pkg.parseOnly(bin).option.getOrElse(sys.error("Couldn't parse input"))
final case class Header(version: Long, typ: Long)
sealed trait Payload:
def header: Header
final case class Operator(header: Header, operands: List[Payload]) extends Payload
final case class Literal(header: Header, value: Long) extends Payload
val bit: Parser[Boolean] = (char('0') || char('1')).map(_.isRight)
def bits(digits: Int) = take(digits).map(BigInt(_, 2).toLong)
val header = (bits(3), bits(3)).mapN(Header.apply)
val literal: Parser[Long] = (many(char('1') ~> take(4)), (char('0') ~> take(4)))
.mapN((init, last) => BigInt(init.mkString + last, 2).toLong)
def literal(header: Header): Parser[Literal] = literal.map(Literal(header, _))
val subpackageSized: Parser[List[Payload]] =
for
len <- bits(15)
payload <- take(len.toInt)
yield many(pkg).parseOnly(payload).option.getOrElse(List())
val subpackageCounted: Parser[List[Payload]] =
for
len <- bits(11)
payload <- manyN(len.toInt, pkg)
yield payload
def operator(header: Header) =
for
usePackageCount <- bit
payload <- if usePackageCount then subpackageCounted else subpackageSized
yield Operator(header, payload)
val pkg =
for
h <- header
result <- if h.typ == 4 then literal(h)
else operator(h)
yield result
def versionSum(payload: Payload): Long = payload match
case Operator(Header(vers,_), operands) => vers + operands.map(versionSum).sum
case Literal(Header(vers,_),_) => vers
extension (b: Boolean)
def toLong: Long = if b then 1L else 0L
def evaluate(payload: Payload): Long = payload match
case Operator(Header(_, op), subPayload) =>
val subValues = subPayload.map(evaluate)
op match
case 0 => subValues.sum
case 1 => subValues.product
case 2 => subValues.min
case 3 => subValues.max
case 5 => (subValues(0) > subValues(1)).toLong
case 6 => (subValues(0) < subValues(1)).toLong
case 7 => (subValues(0) == subValues(1)).toLong
case u => sys.error(s"Invalid operator encountered: $u")
case Literal(_, v) => v
def show(payload: Payload): String = payload match
case Operator(Header(_, op), subPayload) =>
val subValues = subPayload.map(show)
op match
case 0 => s"sum($subValues)"
case 1 => s"product($subValues)"
case 2 => s"min($subValues)"
case 3 => s"max($subValues)"
case 5 => s"${subValues(0)} > ${subValues(1)}"
case 6 => s"${subValues(0)} < ${subValues(1)}"
case 7 => s"${subValues(0)} == ${subValues(1)}"
case u => sys.error(s"Invalid operator encountered: $u")
case Literal(_, v) => v.toString

View file

@ -25,6 +25,9 @@ extension (s: String)
def splitNN(regex: String): List[String] =
s.split(regex).nn.map(_.nn).toList
def padLeft(size: Int, elem: Char): String =
elem.toString * (size - s.length) + s
extension [K,V,W](map: Map[K,V])
def mapValuesS(f: V => W): Map[K, W] = map.view.mapValues(f).toMap