Skip to content

Commit 497ccea

Browse files
committed
finish day18 and day 19
1 parent 03330f8 commit 497ccea

File tree

5 files changed

+119
-96
lines changed

5 files changed

+119
-96
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ dependencies {
5050
implementation(kotlin("reflect"))
5151

5252
implementation("org.slf4j:slf4j-api:2.0.5")
53-
implementation("ch.qos.logback:logback-classic:1.4.5")
54-
implementation("ch.qos.logback:logback-core:1.4.5")
53+
implementation("ch.qos.logback:logback-classic:1.4.12")
54+
implementation("ch.qos.logback:logback-core:1.4.12")
5555
implementation("io.github.microutils:kotlin-logging-jvm:3.0.4")
5656
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
5757
implementation("org.mariuszgromada.math:MathParser.org-mXparser:5.1.0")

src/main/kotlin/tr/emreone/adventofcode/days/Day18.kt

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,20 @@ import tr.emreone.kotlin_utils.extensions.formatted
88
import tr.emreone.kotlin_utils.math.*
99
import java.util.LinkedList
1010
import kotlin.collections.forEach
11+
import kotlin.math.abs
1112

1213
class Day18 : Day(18, 2023, "Lavaduct Lagoon") {
1314

14-
/*
15-
private val p2Instructions = input.map {
16-
val (_, _, c) = it.split(' ')
17-
val steps = c.drop(2).dropLast(2).toInt(16)
18-
val dir = when (c.dropLast(1).last()) {
19-
'0' -> RIGHT
20-
'1' -> DOWN
21-
'2' -> LEFT
22-
else -> UP
23-
}
24-
dir to steps
25-
}
26-
*/
27-
2815
override fun part1(): Int {
2916
val instructions = inputAsList.map {
3017
val (d, s, _) = it.split(' ')
31-
val steps = s.toInt()
3218
val dir = when (d) {
33-
"R" -> Direction4.EAST
34-
"D" -> Direction4.SOUTH
35-
"L" -> Direction4.WEST
19+
"R" -> Direction4.EAST
20+
"D" -> Direction4.SOUTH
21+
"L" -> Direction4.WEST
3622
else -> Direction4.NORTH
3723
}
24+
val steps = s.toInt()
3825
dir to steps
3926
}
4027

@@ -66,7 +53,7 @@ class Day18 : Day(18, 2023, "Lavaduct Lagoon") {
6653
map.formatted(area = bigger) { x, c ->
6754
when {
6855
x in seen -> "~"
69-
else -> "#"
56+
else -> "#"
7057
}
7158
}
7259

@@ -75,25 +62,45 @@ class Day18 : Day(18, 2023, "Lavaduct Lagoon") {
7562
return bigger.size - seen.size
7663
}
7764

78-
/*
7965
override fun part2(): Long {
80-
val boundary = p2Instructions.sumOf { it.second.toLong() }
81-
val corners = p2Instructions.runningFold(origin) { acc, instruction ->
66+
val instructions = inputAsList.map {
67+
val (_, _, c) = it.split(' ')
68+
// c is the color in braces in hex with 6 digits
69+
// e.g. c = (#159ABC)
70+
val dir = when (c.dropLast(1).last()) {
71+
'0' -> Direction4.EAST
72+
'1' -> Direction4.SOUTH
73+
'2' -> Direction4.WEST
74+
else -> Direction4.NORTH
75+
}
76+
val steps = c
77+
.drop(2) // remove (#
78+
.dropLast(2) // remove x)
79+
.toInt(16)
80+
dir to steps
81+
}
82+
83+
val boundary = instructions.sumOf { it.second.toLong() }
84+
val corners = instructions.runningFold(origin) { acc, instruction ->
8285
acc + (instruction.first * instruction.second)
8386
}
8487
require(corners.first() == corners.last()) { "not a closed loop" }
8588

86-
// area by Shoelace algorithm https://en.wikipedia.org/wiki/Shoelace_formula
89+
// area by Shoelace algorithm
90+
// https://en.wikipedia.org/wiki/Shoelace_formula
8791
// https://youtu.be/0KjG8Pg6LGk?si=qC_1iX1YhQlGvI1o
88-
val area = abs(corners.zipWithNext().sumOf { (ci, cj) ->
89-
ci.x.toLong() * cj.y - cj.x.toLong() * ci.y
90-
}) / 2
92+
val area = abs(
93+
corners
94+
.zipWithNext()
95+
.sumOf { (ci, cj) ->
96+
ci.x.toLong() * cj.y - cj.x.toLong() * ci.y
97+
}
98+
) / 2
9199

92100
// according to Pick's theorem: A = i + b / 2 - 1
93101
// https://en.wikipedia.org/wiki/Pick%27s_theorem
94102
val inside = area - boundary / 2 + 1
95103
return inside + boundary
96104
}
97-
*/
98105

99106
}

src/main/kotlin/tr/emreone/adventofcode/days/Day19.kt

Lines changed: 81 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,101 +2,118 @@ package tr.emreone.adventofcode.days
22

33
import tr.emreone.kotlin_utils.automation.Day
44
import tr.emreone.kotlin_utils.automation.extractAllIntegers
5-
import tr.emreone.kotlin_utils.math.Coords
6-
import tr.emreone.kotlin_utils.math.Direction4
7-
import tr.emreone.kotlin_utils.math.plus
85

9-
class Day19 : Day(19, 2023, "Aplenty") {
6+
enum class Category(name: String) {
7+
X("Extremely cool looking"),
8+
M("Musical"),
9+
A("Aerodynamic"),
10+
S("Shiny")
11+
}
1012

11-
private val workflows = inputAsGroups[0].map {
12-
val name = it.substringBefore('{')
13-
val rules = it.substringAfter('{').dropLast(1).split(',').map { r ->
14-
if (':' in r) {
15-
val (c, n) = r.split(':')
16-
val category = when (c[0]) {
17-
'x' -> 0
18-
'm' -> 1
19-
'a' -> 2
20-
's' -> 3
21-
else -> error(r)
22-
}
23-
when (c[1]) {
24-
'>' -> Rule.GreaterThan(category, c.first { it.isDigit() }.digitToInt(), n)
25-
'<' -> Rule.LessThan(category, c.first { it.isDigit() }.digitToInt(), n)
26-
else -> error(r)
27-
}
28-
}
29-
else
30-
Rule.Unconditional(r)
31-
}
32-
Workflow(name, rules)
33-
}.associateBy { it.name }.show()
13+
typealias PotentialPart = Map<Category, IntRange>
3414

35-
private val parts = inputAsGroups[1].map { it.extractAllIntegers() }.show()
15+
fun PotentialPart.patch(category: Category, newRange: IntRange?): PotentialPart? {
16+
return newRange?.let { patched ->
17+
val parts = this.toMutableMap()
18+
parts[category] = patched
3619

37-
data class Workflow(val name: String, val rules: List<Rule>)
20+
parts.toMap()
21+
}
22+
}
23+
24+
class Day19 : Day(19, 2023, "Aplenty") {
3825

3926
sealed interface Rule {
4027
val next: String
41-
fun matches(part: List<Int>): Boolean
28+
fun matches(part: Map<Category, Int>): Boolean
4229
fun split(parts: PotentialPart): Pair<PotentialPart?, PotentialPart?>
4330

44-
data class LessThan(val category: Int, val value: Int, override val next: String) : Rule {
45-
override fun matches(part: List<Int>) = part[category] < value
31+
data class LessThan(val category: Category, val value: Int, override val next: String) : Rule {
32+
override fun matches(part: Map<Category, Int>) = part[category]!! < value
4633

4734
override fun split(parts: PotentialPart): Pair<PotentialPart?, PotentialPart?> {
48-
val relevant = parts[category]
35+
val relevant = parts[category]!!
4936
val (matching, notMatching) =
5037
when {
51-
value in relevant ->
38+
value in relevant ->
5239
(relevant.first..<value) to (value..relevant.last)
5340

5441
value > relevant.last ->
5542
relevant to null
5643

57-
else -> null to null
44+
else -> null to null
5845
}
5946
return parts.patch(category, matching) to parts.patch(category, notMatching)
6047
}
6148
}
6249

63-
data class GreaterThan(val category: Int, val value: Int, override val next: String) : Rule {
64-
override fun matches(part: List<Int>) = part[category] > value
50+
data class GreaterThan(val category: Category, val value: Int, override val next: String) : Rule {
51+
override fun matches(part: Map<Category, Int>) = part[category]!! > value
6552

6653
override fun split(parts: PotentialPart): Pair<PotentialPart?, PotentialPart?> {
67-
val relevant = parts[category]
54+
val relevant = parts[category]!!
6855
val (matching, notMatching) =
6956
when {
70-
value in relevant ->
57+
value in relevant ->
7158
((value + 1)..relevant.last) to (relevant.first..value)
7259

7360
value < relevant.first ->
7461
relevant to null
7562

76-
else -> null to null
63+
else -> null to null
7764
}
7865
return parts.patch(category, matching) to parts.patch(category, notMatching)
7966
}
8067
}
8168

8269
data class Unconditional(override val next: String) : Rule {
83-
override fun matches(part: List<Int>) = true
70+
override fun matches(part: Map<Category, Int>) = true
8471

8572
override fun split(parts: PotentialPart): Pair<PotentialPart?, PotentialPart?> =
8673
parts to null
8774
}
75+
}
76+
77+
data class Workflow(val name: String, val rules: List<Rule>)
8878

89-
fun List<IntRange>.patch(category: Int, newRange: IntRange?): List<IntRange>? =
90-
newRange?.let { patched ->
91-
mapIndexed { index, range ->
92-
patched.takeIf { index == category } ?: range
79+
private val workflows = inputAsGroups[0]
80+
.map {
81+
val (name, ruleString) = it.split('{')
82+
val rules = ruleString.dropLast(1)
83+
.split(',')
84+
.map { r ->
85+
if (':' in r) {
86+
val (c, next) = r.split(':')
87+
val category = when (c[0]) {
88+
'x' -> Category.X
89+
'm' -> Category.M
90+
'a' -> Category.A
91+
's' -> Category.S
92+
else -> error(r)
93+
}
94+
when (c[1]) {
95+
'>' -> Rule.GreaterThan(category, c.split('>')[1].toInt(), next)
96+
'<' -> Rule.LessThan(category, c.split('<')[1].toInt(), next)
97+
else -> error(r)
98+
}
99+
} else {
100+
Rule.Unconditional(r)
101+
}
93102
}
94-
}
95-
}
96103

97-
override fun part1(): Int {
104+
Workflow(name, rules)
105+
}
106+
.associateBy { it.name }
107+
.show()
98108

99-
fun List<Int>.isAccepted(): Boolean {
109+
private val partRatings = inputAsGroups[1]
110+
.map {
111+
Category.entries.zip(it.extractAllIntegers()).toMap()
112+
}
113+
.show()
114+
115+
override fun part1(): Int {
116+
fun Map<Category, Int>.isAccepted(): Boolean {
100117
var wf = "in"
101118
while (true) {
102119
if (wf == "R") return false
@@ -105,38 +122,37 @@ class Day19 : Day(19, 2023, "Aplenty") {
105122
}
106123
}
107124

108-
return parts.filter { it.isAccepted() }.sumOf { it.sum() }
125+
return partRatings
126+
.filter { it.isAccepted() }
127+
.sumOf { it.values.sum() }
109128
}
110129

111130
override fun part2(): Long {
112-
val potentialPart = listOf(
113-
1..4000,
114-
1..4000,
115-
1..4000,
116-
1..4000,
131+
val potentialParts = mapOf(
132+
Category.X to 1..4000,
133+
Category.M to 1..4000,
134+
Category.A to 1..4000,
135+
Category.S to 1..4000,
117136
)
118137

119138
fun countAccepted(wf: Workflow, parts: PotentialPart?): Long =
120139
wf.rules.fold(parts to 0L) { (remaining, count), rule ->
121140
if (remaining != null) {
122141
val (matching, notMatching) = rule.split(remaining)
123142
notMatching to count + when (rule.next) {
124-
"A" -> matching?.combinations() ?: 0
125-
"R" -> 0
143+
"A" -> matching?.combinations() ?: 0
144+
"R" -> 0
126145
else -> countAccepted(workflows[rule.next]!!, matching)
127146
}
128-
}
129-
else
147+
} else
130148
null to count
131149
}.second
132150

133-
return countAccepted(workflows["in"]!!, potentialPart)
151+
return countAccepted(workflows["in"]!!, potentialParts)
134152
}
135153

136154
private fun PotentialPart.combinations(): Long =
137-
this.map { r -> r.last - r.first + 1L }
138-
.reduce { acc, i -> acc * i }
139-
155+
this.values.fold(1) { acc, i ->
156+
acc * (i.last - i.first + 1L)
157+
}
140158
}
141-
142-
typealias PotentialPart = List<IntRange>

src/test/kotlin/tr/emreone/adventofcode/days/Day18Test.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ internal class Day18Test {
1010
fun `execute_tests`() {
1111
solve<Day18>(false) {
1212
Resources.resourceAsList("day18_example.txt")
13-
.joinToString("\n") part1 62
13+
.joinToString("\n") part1 62 part2 952_408_144_115L
1414
}
1515
}
1616

src/test/kotlin/tr/emreone/adventofcode/days/Day19Test.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ internal class Day19Test {
1010
fun `execute_tests`() {
1111
solve<Day19>(false) {
1212
Resources.resourceAsList("day19_example.txt")
13-
.joinToString("\n") part1 0
13+
.joinToString("\n") part1 19_114 part2 167_409_079_868_000L
1414
}
1515
}
1616

0 commit comments

Comments
 (0)