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
68 changes: 28 additions & 40 deletions 2015/src/main/scala/aoc2015/Day07.scala
Original file line number Diff line number Diff line change
@@ -1,81 +1,69 @@
package aoc2015

import nmcb.*

import scala.annotation.*

object Day07 extends AoC:

/** Modeling */

type Wire = String
type Env = Map[Wire, Int]

sealed trait Rule:
def args: List[Wire]
def ret: Wire
def run(env: Env): Option[Int]

case class Op2(op: Int => Int => Int, args: List[Wire], ret: Wire) extends Rule:

def run(env: Env): Option[Int] =
val List(lhs, rhs) = args.map(env.get)
for
v1 <- lhs
v2 <- rhs
yield
op(v1)(v2)

case class Op1(op: Int => Int, args: List[Wire], ret: Wire) extends Rule:

def run(env: Env): Option[Int] =
args.map(env.get).headOption.flatten.map(op)
def call(env: Env): Option[Int]

case class Val(value: Int, ret: Wire) extends Rule:
case class Op(op: Int => Int, ret: Wire, arg: Wire) extends Rule:
def call(env: Env): Option[Int] =
env.get(arg).map(op)

def args: List[Wire] =
List.empty
case class BinOp(op: Int => Int => Int, ret: Wire, lhs: Wire, rhs: Wire) extends Rule:
def call(env: Env): Option[Int] =
env.get(lhs).flatMap(lv => env.get(rhs).map(rv => op(lv)(rv)))

def run(env: Env): Option[Int] =
case class Val(value: Int, ret: Wire, args: Vector[Wire] = Vector.empty) extends Rule:
def call(env: Env): Option[Int] =
Some(value)

object Solver:
def solve(rules: Seq[Rule], wire: Wire, setWireB: Option[Int] = None): Int =

@tailrec def fold(rules: Seq[Rule], env: Env = Map.empty): Int =
def solve(rules: Vector[Rule], wire: Wire, setWireB: Option[Int] = None): Int =
@tailrec
def fold(rules: Seq[Rule], env: Env = Map.empty): Int =
env.get(wire) match
case Some(v) => v
case None => rules match
case Seq(rule, rest*) => rule.run(env) match
case Some(v) => fold(rest, env.updated(rule.ret, v))
case None => fold(rest :+ rule, env)
case _ => sys.error(s"undefined wire=$wire")
case rule +: rest => rule.call(env) match
case Some(v) => fold(rest, env.updated(rule.ret, v))
case None => fold(rest :+ rule, env)
case _ => sys.error(s"undefined wire=$wire")

val puzzleInput: Seq[Rule] = setWireB.map(v => Val(v, "b") +: rules.filterNot(_.ret == "b")).getOrElse(rules)
val puzzleInput: Vector[Rule] = setWireB.map(v => Val(v, "b") +: rules.filterNot(_.ret == "b")).getOrElse(rules)
fold(puzzleInput)

val rules: IndexedSeq[Rule] =
val rules: Vector[Rule] =
def parser(s: Wire): Rule =
s match
case s"$lhs AND $rhs -> $ret" if lhs.toIntOption.isDefined
=> Op1(rv => lhs.toInt & rv, List(rhs), ret)
=> Op(rv => lhs.toInt & rv, ret, rhs)
case s"$lhs AND $rhs -> $ret"
=> Op2(lv => rv => lv & rv, List(lhs, rhs), ret)
=> BinOp(lv => rv => lv & rv, ret, lhs, rhs)
case s"$lhs OR $rhs -> $ret"
=> Op2(lv => rv => lv | rv, List(lhs, rhs), ret)
=> BinOp(lv => rv => lv | rv, ret, lhs, rhs)
case s"$lhs RSHIFT $rhs -> $ret" if rhs.toIntOption.isDefined
=> Op1(lv => lv >> rhs.toInt, List(lhs), ret)
=> Op(lv => lv >> rhs.toInt, ret, lhs)
case s"$lhs RSHIFT $rhs -> $ret"
=> Op2(lv => rv => lv >> rv, List(lhs, rhs), ret)
=> BinOp(lv => rv => lv >> rv, ret, lhs, rhs)
case s"$lhs LSHIFT $rhs -> $ret" if rhs.toIntOption.isDefined
=> Op1(lv => lv << rhs.toInt, List(lhs), ret)
=> Op(lv => lv << rhs.toInt, ret, lhs)
case s"$lhs LSHIFT $rhs -> $ret"
=> Op2(lv => rv => lv << rv, List(lhs, rhs), ret)
=> BinOp(lv => rv => lv << rv, ret, lhs, rhs)
case s"NOT $rhs -> $ret"
=> Op1(rv => ~rv & 0x0000FFFF, List(rhs), ret)
=> Op(rv => ~rv & 0x0000FFFF, ret, rhs)
case s"$arg -> $ret" if arg.toIntOption.isDefined
=> Val(arg.toInt, ret)
case s"$rhs -> $ret"
=> Op1(identity, List(rhs), ret)
=> Op(identity, ret, rhs)

lines.map(parser)

Expand Down
44 changes: 20 additions & 24 deletions 2015/src/main/scala/aoc2015/Day16.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,37 @@ import nmcb.*
object Day16 extends AoC:

case class Sue(nr: Int, properties: Map[String,Int]):
def matches1: Boolean =
def matches1(that: Map[String, Int]): Boolean =
properties.forall((name, count) =>
Found.compounds.get(name).contains(count))
that.get(name).contains(count))

def matches2: Boolean =
def matches2(that: Map[String, Int]): Boolean =
properties.forall((name, count) =>
name match
case "cats" | "trees" =>
Found.compounds.get(name).exists(_ < count)
case "pomeranians" | "goldfish" =>
Found.compounds.get(name).exists(_ > count)
case _ =>
Found.compounds.get(name).contains(count))
case "cats" | "trees" => that.get(name).exists(_ < count)
case "pomeranians" | "goldfish" => that.get(name).exists(_ > count)
case _ => that.get(name).contains(count))

object Sue:
def fromString(s: String): Sue =
s match
case s"Sue $nr: $n1: $c1, $n2: $c2, $n3: $c3" =>
Sue(nr.toInt, Map(n1 -> c1.toInt, n2 -> c2.toInt, n3 -> c3.toInt))

object Found:
val compounds: Map[String,Int] =
Map( "children" -> 3
, "cats" -> 7
, "samoyeds" -> 2
, "pomeranians" -> 3
, "akitas" -> 0
, "vizslas" -> 0
, "goldfish" -> 5
, "trees" -> 3
, "cars" -> 2
, "perfumes" -> 1
)
val compounds: Map[String,Int] =
Map( "children" -> 3
, "cats" -> 7
, "samoyeds" -> 2
, "pomeranians" -> 3
, "akitas" -> 0
, "vizslas" -> 0
, "goldfish" -> 5
, "trees" -> 3
, "cars" -> 2
, "perfumes" -> 1
)

val sues: List[Sue] = lines.map(Sue.fromString).toList

lazy val answer1: Int = sues.filter(_.matches1).head.nr
lazy val answer2: Int = sues.filter(_.matches2).head.nr
lazy val answer1: Int = sues.find(_.matches1(compounds)).getOrElse(sys.error("not found")).nr
lazy val answer2: Int = sues.find(_.matches2(compounds)).getOrElse(sys.error("not found")).nr
27 changes: 13 additions & 14 deletions 2018/src/main/scala/aoc2018/Day24.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ object Day24 extends AoC:
import Army.*

type AttackKind = String

case class Attribute(hitPower: Int, initiative: Int)
case class Defense(immune: Set[AttackKind], weak: Set[AttackKind])
case class Attack(damage: Int, kind: AttackKind)
Expand All @@ -31,18 +31,18 @@ object Day24 extends AoC:
else
attacker.effectivePower

def groups(lines: Vector[AttackKind], boost: Int): Vector[Group] =
def groups(chunks: Vector[Vector[String]], boost: Int): Vector[Group] =

def parseGroup(army: Army, boost: Int, line: AttackKind): Group =
val GroupLine = """(\d+) units each with (\d+) hit points (?:.*)with an attack that does (\d+) (\w+) damage at initiative (\d+)""".r
val ImmuneLine = """immune to (.+?)[;|\)]""".r.unanchored
val WeakLine = """weak to (.+?)[;|\)]""".r.unanchored
def parseGroup(army: Army, boost: Int)(line: String): Group =
val GroupLine = """(\d+) units each with (\d+) hit points .*with an attack that does (\d+) (\w+) damage at initiative (\d+)""".r
val ImmuneLine = """immune to (.+?)[;|)]""".r.unanchored
val WeakLine = """weak to (.+?)[;|)]""".r.unanchored

val immune = line match
val immune: Set[String] = line match
case ImmuneLine(items) => items.split(", ").toSet
case _ => Set.empty

val weak = line match
val weak: Set[String] = line match
case WeakLine(items) => items.split(", ").toSet
case _ => Set.empty

Expand All @@ -54,12 +54,11 @@ object Day24 extends AoC:
Group(army, attribute, defense, attack)(units.toInt)


val index = lines.indexOf("")
val immune = lines.slice(1, index).map(parseGroup(Immune, boost, _))
val infection = lines.drop(index + 2).map(parseGroup(Infection, 0, _))
val immune = chunks(0).tail.map(parseGroup(Immune, boost))
val infection = chunks(1).tail.map(parseGroup(Infection, 0))
immune ++ infection

type Outcome = (Army,Int)
type Outcome = (Army, Int)

extension (outcome: Outcome)
def winner: Army = outcome._1
Expand Down Expand Up @@ -104,9 +103,9 @@ object Day24 extends AoC:
def solve2(): Int =
@tailrec
def go(boost: Int): Int =
val outcome = fight(groups(lines, boost = boost))
val outcome = fight(groups(chunks, boost = boost))
if outcome.winner == Immune then outcome.units else go(boost + 1)
go(1)

lazy val answer1: Int = fight(groups(lines, boost = 0)).units
lazy val answer1: Int = fight(groups(chunks, boost = 0)).units
lazy val answer2: Int = solve2()
9 changes: 5 additions & 4 deletions 2019/src/main/scala/aoc2019/Day01.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package aoc2019

import nmcb.*

import scala.annotation.tailrec

object Day01 extends AoC:

val masses: Vector[Int] =lines.map(_.toInt)

def massToFuel(mass: Int): Int =
(mass / 3) - 2

Expand All @@ -20,5 +19,7 @@ object Day01 extends AoC:
go(requirement - 1, total + requirement)
go(mass)

lazy val answer1 = masses.map(massToFuel).sum
lazy val answer2 = masses.map(massToFuelRequirement).sum
val masses: Vector[Int] = lines.map(_.toInt)

lazy val answer1: Int = masses.map(massToFuel).sum
lazy val answer2: Int = masses.map(massToFuelRequirement).sum
11 changes: 6 additions & 5 deletions 2019/src/main/scala/aoc2019/Day02.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package aoc2019

import nmcb.*

object Day02 extends AoC:

import cpu.*

val program: Mem = Mem.parse(input)

def experiment(program: Mem): Long =
val permutations =
for
Expand All @@ -17,12 +16,14 @@ object Day02 extends AoC:

val (noun, verb) =
permutations
.dropWhile: (noun,verb) =>
val patch = program.updated(1,noun).updated(2,verb)
.dropWhile: (noun, verb) =>
val patch = program.set(1, noun).set(2, verb)
CPU(patch).execFinal.mem(0) != 19690720
.head

100 * noun + verb

lazy val answer1: Value = CPU(program.updated(1,12).updated(2,2)).execFinal.mem(0)
val program: Mem = Mem.parse(input)

lazy val answer1: Value = CPU(program.set(1, 12).set(2, 2)).execFinal.mem(0)
lazy val answer2: Value = experiment(program)
17 changes: 8 additions & 9 deletions 2019/src/main/scala/aoc2019/Day04.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ import nmcb.*

object Day04 extends AoC:

def window(number: Int, size: Int): Iterator[IndexedSeq[Int]] =
number.toString.map(_.asDigit).sliding(size)

def solve1(first: Int, last: Int): Int =
(first to last).count: number =>
def window = number.toString.map(_.asDigit).sliding(2)

val rule1 = window.forall { case Seq(a,b) => a <= b }
val rule2 = window.exists { case Seq(a,b) => a == b }
val rule1 = window(number, 2).forall { case Seq(a,b) => a <= b }
val rule2 = window(number, 2).exists { case Seq(a,b) => a == b }
rule1 && rule2

def solve2(first: Int, last: Int): Int =
(first to last).count: number =>
def window(size: Int) = number.toString.map(_.asDigit).sliding(size)

val rule1 = window(2).forall { case Seq(a,b) => a <= b }
val rule2 = window(2).collect { case Seq(a,b) if a == b => a }.toSet
val rule3 = window(3).collect { case Seq(a,b,c) if a == b && b == c => a }.toSet
val rule1 = window(number, 2).forall { case Seq(a,b) => a <= b }
val rule2 = window(number, 2).collect { case Seq(a,b) if a == b => a }.toSet
val rule3 = window(number, 3).collect { case Seq(a,b,c) if a == b && b == c => a }.toSet
rule1 && (rule2 -- rule3).nonEmpty

lazy val answer1: Int = solve1(235741, 706948)
Expand Down
35 changes: 17 additions & 18 deletions 2019/src/main/scala/aoc2019/Day06.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aoc2019

import nmcb.*

import scala.annotation.tailrec

object Day06 extends AoC:
Expand All @@ -9,41 +10,39 @@ object Day06 extends AoC:

case class Planet(name: Name, center: Name)

val planets: List[Planet] =
lines
.map:
case s"$center)$name" => Planet(name, center)
.toList
val planets: Vector[Planet] =
lines.map:
case s"$center)$name" => Planet(name, center)

def find(p: Planet => Boolean): Planet =
planets.find(p).getOrElse(sys.error("boom"))
def orbits(p: Planet): List[Name] =

def orbits(p: Planet): Vector[Name] =
@tailrec
def go(p: Planet, acc: List[Name] = List.empty): List[Name] =
def loop(p: Planet, acc: Vector[Name] = Vector.empty): Vector[Name] =
if (p.center == "COM")
p.name :: acc
p.name +: acc
else
val next = find(_.name == p.center)
go(next, next.name :: acc)
loop(next, next.name +: acc)

go(p)
loop(p)

def pathToCom(n: Name): List[Name] =
def pathToCom(n: Name): Vector[Name] =
@tailrec
def go(n: Name, acc: List[Name] = List.empty): List[Name] =
def loop(n: Name, acc: Vector[Name] = Vector.empty): Vector[Name] =
val c = find(_.name == n).center
if (c == "COM")
acc
else
go(c, c :: acc)
go(n)
loop(c, c +: acc)
loop(n)

@tailrec
def path(l: List[Name], r: List[Name]): List[Name] =
def path(l: Vector[Name], r: Vector[Name]): Vector[Name] =
(l,r) match
case (l1 :: l2 :: _ , r1 :: r2 :: _) if l1 == r1 && l2 == r2 => path(l.tail, r.tail)
case (l1 :: _ , r1 :: _ ) if l1 == r1 => l.reverse ++ r.tail
case (l1 +: l2 +: _ , r1 +: r2 +: _) if l1 == r1 && l2 == r2 => path(l.tail, r.tail)
case (l1 +: _ , r1 +: _ ) if l1 == r1 => l.reverse ++ r.tail
case _ => sys.error(s"unmatched l=$l, r=$r")

lazy val answer1: Int = planets.map(orbits).map(_.length).sum
Expand Down
Loading