Day 16: Parser Fun
This commit is contained in:
parent
553b52afa0
commit
031ef41462
|
@ -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",
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
|
|
1
input/2021/day16-sample1.txt
Normal file
1
input/2021/day16-sample1.txt
Normal file
|
@ -0,0 +1 @@
|
|||
D2FE28
|
1
input/2021/day16-sample2.txt
Normal file
1
input/2021/day16-sample2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
38006F45291200
|
1
input/2021/day16-sample3.txt
Normal file
1
input/2021/day16-sample3.txt
Normal file
|
@ -0,0 +1 @@
|
|||
EE00D40C823060
|
1
input/2021/day16-sample4.txt
Normal file
1
input/2021/day16-sample4.txt
Normal file
|
@ -0,0 +1 @@
|
|||
8A004A801A8002F478
|
1
input/2021/day16-sample5.txt
Normal file
1
input/2021/day16-sample5.txt
Normal file
|
@ -0,0 +1 @@
|
|||
620080001611562C8802118E34
|
1
input/2021/day16-sample6.txt
Normal file
1
input/2021/day16-sample6.txt
Normal file
|
@ -0,0 +1 @@
|
|||
C0015000016115A2E0802F182340
|
1
input/2021/day16-sample7.txt
Normal file
1
input/2021/day16-sample7.txt
Normal file
|
@ -0,0 +1 @@
|
|||
A0016C880162017C3686B18A3D4780
|
1
input/2021/day16.txt
Normal file
1
input/2021/day16.txt
Normal file
|
@ -0,0 +1 @@
|
|||
020D790050D26C13EC1348326D336ACE00EC299E6A8B929ED59C880502E00A526B969F62BF35CB4FB15B93A6311F67F813300470037500043E2D4218FA000864538E905A39CAF77386E35AB01802FC01BA00021118617C1F00043A3F4748A53CF66008D00481272D73308334EDB0ED304E200D4E94CF612A49B40036C98A7CF24A53CA94C6370FBDCC9018029600ACD529CA9A4F62ACD2B5F928802F0D2665CA7D6CC013919E78A3800D3CF7794A8FC938280473057394AFF15099BA23CDD37A08400E2A5F7297F916C9F97F82D2DFA734BC600D4E3BC89CCBABCBE2B77D200412599244D4C0138C780120CC67E9D351C5AB4E1D4C981802980080CDB84E034C5767C60124F3BC984CD1E479201232C016100662D45089A00087C1084F12A724752BEFEA9C51500566759BF9BE6C5080217910CD00525B6350E8C00E9272200DCE4EF4C1DD003952F7059BCF675443005680103976997699795E830C02E4CBCE72EFC6A6218C88C9DF2F3351FCEF2D83CADB779F59A052801F2BAACDAE7F52A8190073937FE1D700439234DBB4F7374DC0CC804CF1006A0D47B8A4200F445865170401F8251662D100909401AB8803313217C680004320D43F871308D140C010E0069E7EDD1796AFC8255800052E20043E0F42A8B6400864258E51088010B85910A0F4ECE1DFE069C0229AE63D0B8DC6F82529403203305C00E1002C80AF5602908400A20240100852401E98400830021400D30029004B6100294008400B9D0023240061C000D19CACCD9005F694AEF6493D3F9948DEB3B4CC273FFD5E9AD85CFDFF6978B80050392AC3D98D796449BE304FE7F0C13CD716656BD0A6002A67E61A400F6E8029300B300B11480463D004C401889B1CA31800211162204679621200FCAC01791CF6B1AFCF2473DAC6BF3A9F1700016A3D90064D359B35D003430727A7DC464E6401594A57C93A0084CC56A662B8C00AA424989F2A9112
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue