Skip to content

Instantly share code, notes, and snippets.

@JavadocMD
Created December 21, 2022 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JavadocMD/083eb9fa6aa921d7669e12768c1f6fc1 to your computer and use it in GitHub Desktop.
Save JavadocMD/083eb9fa6aa921d7669e12768c1f6fc1 to your computer and use it in GitHub Desktop.
A solution to Advent of Code 2022 Day 21.
/*
Copyright 2022 Tyler Coles (javadocmd.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import scala.io.Source
object Day21:
enum Operator:
case Add, Sub, Mul, Div
def eval(lhs: Long, rhs: Long): Long = this match
case Add => lhs + rhs
case Sub => lhs - rhs
case Mul => lhs * rhs
case Div => lhs / rhs
def solveLeft(result: Long, operand: Long): Long = this match
case Add => result - operand
case Sub => result + operand
case Mul => result / operand
case Div => result * operand
def solveRight(result: Long, operand: Long): Long = this match
case Add => result - operand
case Sub => operand - result
case Mul => result / operand
case Div => operand / result
object Operator:
def unapply(s: String): Option[Operator] = s match
case "+" => Some(Add)
case "-" => Some(Sub)
case "*" => Some(Mul)
case "/" => Some(Div)
case _ => None
import Operator._
enum Monkey:
case Opn(op: Operator, lhs: Monkey, rhs: Monkey)
case Num(value: Long)
case Hmn // human, for part 2
def eval: Long = this match
case Num(value) => value
case Opn(op, lhs, rhs) => op.eval(lhs.eval, rhs.eval)
case Hmn => throw new Exception("unhandled case: Human")
def involvesHuman: Boolean = this match
case Hmn => true
case Num(_) => false
case Opn(_, lhs, rhs) => lhs.involvesHuman || rhs.involvesHuman
def solve(result: Long): Long = this match
case Hmn => result
case Opn(op, Num(operand), term) => term.solve(op.solveRight(result, operand))
case Opn(op, term, Num(operand)) => term.solve(op.solveLeft(result, operand))
case _ => throw new Exception(s"unhandled case $this")
import Monkey._
type Input = Map[String, String] // a map of monkey IDs to their operation description (still a String)
def parse(xs: Iterator[String]): Input = xs.map { case s"$id: $rest" => (id -> rest) }.toMap
object LongP:
def unapply(s: String): Option[Long] = try Some(s.toLong)
catch { case _: NumberFormatException => None }
def interpretMonkeys(ids: Input, withHuman: Boolean): Monkey =
def recurse(id: String): Monkey = (id, ids(id)) match
case ("humn", _) if withHuman => Hmn
case (_, s"${LongP(value)}") => Num(value)
case (_, s"$left ${Operator(op)} $right") =>
val lhs = recurse(left)
val rhs = recurse(right)
(lhs, rhs) match
case (Num(x), Num(y)) => Num(op.eval(x, y))
case (lhs, rhs) => Opn(op, lhs, rhs)
recurse("root")
def part1(input: Input) = interpretMonkeys(input, withHuman = false).eval
def part2(input: Input) =
val Opn(_, left, right) = interpretMonkeys(input, withHuman = true): @unchecked
if left.involvesHuman ^ right.involvesHuman == false then
throw new Exception("unexpected case: only one side can involve the human")
val (tree, value) = if left.involvesHuman then (left, right.eval) else (right, left.eval)
tree.solve(value)
final def main(args: Array[String]): Unit =
val input = parse(Source.fromResource("Day21.input.txt").getLines)
val result1 = part1(input)
println(result1)
val result2 = part2(input)
println(result2)
@JavadocMD
Copy link
Author

Scala 3.2.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment