-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay19.sc
executable file
·77 lines (67 loc) · 2.9 KB
/
Day19.sc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/env scala-cli
//> using scala "3.2.1"
import scala.util.chaining.*
final case class Resources(ore: Int, clay: Int, obsidian: Int, geode: Int):
private def binaryOp(that: Resources, op: (Int, Int) => Int): Resources =
Resources(op(ore, that.ore), op(clay, that.clay), op(obsidian, that.obsidian), op(geode, that.geode))
def +(that: Resources): Resources = binaryOp(that, _ + _)
def -(that: Resources): Resources = binaryOp(that, _ - _)
def valid: Boolean = ore >= 0 && clay >= 0 && obsidian >= 0 && geode >= 0
object Resources:
val zero = Resources(0, 0, 0, 0)
final case class Blueprint(
id: Int,
oreCost: Resources,
clayCost: Resources,
obsidianCost: Resources,
geodeCost: Resources
):
private val recipes: List[Resources] = List(oreCost, clayCost, obsidianCost, geodeCost)
private val maxOreBots: Int = recipes.map(_.ore).max
private val maxClayBots: Int = recipes.map(_.clay).max
private val maxObsidianBots: Int = recipes.map(_.obsidian).max
private def theoreticalMax(remainingTime: Int, bots: Resources, resources: Resources): Int =
resources.geode + (remainingTime * bots.geode) + ((remainingTime-1) * remainingTime) / 2
def score(time: Int, initialBots: Resources, initialResources: Resources): Int =
def impl(currentMax: Int, t: Int, bots: Resources, resources: Resources): Int =
if t == 0 then resources.geode
else if currentMax >= theoreticalMax(t, bots, resources) then -1
else
val steps = List(
(bots.copy(ore = bots.ore + 1), resources - oreCost),
(bots.copy(clay = bots.clay + 1), resources - clayCost),
(bots.copy(obsidian = bots.obsidian + 1), resources - obsidianCost),
(bots.copy(geode = bots.geode + 1), resources - geodeCost),
(bots, resources),
)
.filter(_._2.valid)
.filter(_._1.ore <= maxOreBots)
.filter(_._1.clay <= maxClayBots)
.filter(_._1.obsidian <= maxObsidianBots)
.map { case (bs, rs) => (bs, rs + bots) }
steps.foldLeft(currentMax) { case (currMax, (bs, rs)) => Math.max(currMax, impl(currMax, t-1, bs, rs)) }
impl(-1, time, initialBots, initialResources)
object Blueprint:
def parse(str: String): Blueprint =
val parts = str.split(" ")
Blueprint(
id = parts(1).takeWhile(_ != ':').toInt,
oreCost = Resources(parts(6).toInt, 0, 0, 0),
clayCost = Resources(parts(12).toInt, 0, 0, 0),
obsidianCost = Resources(parts(18).toInt, parts(21).toInt, 0, 0),
geodeCost = Resources(parts(27).toInt, 0, parts(30).toInt, 0)
)
val source = io.Source.fromFile("data/19.txt")
val input = try source.mkString finally source.close()
val bps = input
.split("\n")
.map(Blueprint.parse)
bps
.map(bp => bp.id * bp.score(24, Resources.zero.copy(ore = 1), Resources.zero))
.sum
.tap(println)
bps
.take(3)
.map(_.score(32, Resources.zero.copy(ore = 1), Resources.zero))
.product
.tap(println)