Advent of Code 2023 day 18
12/18/2023, 5:00 AMMichael de Kaste
12/18/2023, 5:49 AMobject Day18 : Challenge() {
val parsed = input.lines().map { line -> line.split(" ") }
override fun part1() = parsed.map { (a, b, _) -> a[0] to b.toInt() }.solve()
override fun part2() = parsed.map { (_, _, c) -> c.toDigLine() }.solve()
fun List<Pair<Char, Int>>.solve() = runningFold(0 to 0) { point, (dir, amount) -> point.move(dir, amount) }
.zipWithNext { (y1, x1), (_, x2) -> (x2 - x1) * y1.toLong() }
.sum().absoluteValue + sumOf { it.second } / 2 + 1
private fun Pair<Int, Int>.move(direction: Char, amount: Int) = when (direction) {
'U' -> first - amount to second
'R' -> first to second + amount
'D' -> first + amount to second
else -> first to second - amount
}
private fun String.toDigLine() = when (get(lastIndex - 1)) {
'0' -> 'R'
'1' -> 'D'
'2' -> 'L'
else -> 'U'
} to substring(2..6).toInt(16)
}
Jakub Gwóźdź
12/18/2023, 6:02 AMMarcin Wisniowski
12/18/2023, 6:08 AMMarcin Wisniowski
12/18/2023, 6:10 AMMichael de Kaste
12/18/2023, 6:12 AMJakub Gwóźdź
12/18/2023, 6:20 AMwhen
is exhaustive in such cases, so no ugly else->error("should never happen")
🙂Michael de Kaste
12/18/2023, 6:22 AMJakub Gwóźdź
12/18/2023, 6:25 AMMichael de Kaste
12/18/2023, 6:27 AMMichael de Kaste
12/18/2023, 6:28 AMelizarov
12/18/2023, 6:33 AMephemient
12/18/2023, 6:37 AMephemient
12/18/2023, 6:37 AMelizarov
12/18/2023, 6:37 AMJakub Gwóźdź
12/18/2023, 6:38 AMephemient
12/18/2023, 6:38 AMelizarov
12/18/2023, 6:40 AMWerner Altewischer
12/18/2023, 6:49 AMMichael de Kaste
12/18/2023, 6:51 AMMichael de Kaste
12/18/2023, 6:59 AMMarcin Wisniowski
12/18/2023, 7:00 AMJonathan Kolberg
12/18/2023, 7:02 AMMarcin Wisniowski
12/18/2023, 7:03 AMJonathan Kolberg
12/18/2023, 7:03 AMelizarov
12/18/2023, 7:05 AMJonathan Kolberg
12/18/2023, 7:07 AMNeil Banman
12/18/2023, 8:06 AMLong
mafia strikes again! I saw it coming, and yet I didn't think I needed to convert so early. Like @Marcin Wisniowski I was burned by my Int-based Point class.ephemient
12/18/2023, 8:22 AMInt
, your accumulators just need to be Long
(that's what I did)Neil Banman
12/18/2023, 8:24 AMTomasz Linkowski
12/18/2023, 8:30 AMNeil Banman
12/18/2023, 8:32 AMdaugian
12/18/2023, 8:37 AMcalc
function? It looks so simple 🤪ephemient
12/18/2023, 8:37 AMR 2 (#000020)
D 2 (#000021)
L 2 (#000022)
U 2 (#000023)
the path:
.....
.┏━┓.
.┃.┃.
.┗━┛.
.....
the desired area:
.....
.███.
.███.
.███.
.....
the area if you calculate just by multiplying coordinate differences:
.....
.██..
.██..
.....
.....
(or
.....
.▗▄▖.
.▐█▌.
.▝▀▘.
.....
if you think about it as being centered in the middle of the pixels)
thus you need to adjust for the half perimeter + 1, or the extra half/quarter pixels, depending on how you look at itMichael de Kaste
12/18/2023, 8:43 AMMichael de Kaste
12/18/2023, 8:47 AMPaul Woitaschek
12/18/2023, 9:14 AMephemient
12/18/2023, 9:14 AMborder
includes Point.Zero
at both the start and end, so ceil(border.size / 2f).toInt()
is the (border without duplicates).size / 2 + 1
. then see the above for why that's needed for adjustmentsMarcin Wisniowski
12/18/2023, 9:59 AMBufferedImage
, opened the resulting image in an image editor, clicked with the bucket fill tool to fill the shape, then dragged that to some "count pixels" website which told me how many black pixels are there. 😄ephemient
12/18/2023, 10:29 AMPetr Sýkora
12/18/2023, 10:46 AMephemient
12/18/2023, 11:10 AMjvm summary:
Benchmark Mode Cnt Score Error Units
Day18Bench.part1 avgt 5 119.370 ± 2.064 us/op
Day18Bench.part2 avgt 5 123.276 ± 2.386 us/op
Pawel Matysek
12/18/2023, 11:43 AMphldavies
12/18/2023, 11:46 AMBenchmark Mode Cnt Score Error Units
BenchDay18.part1 avgt 3 46.068 ± 15.653 us/op
BenchDay18.part2 avgt 3 36.800 ± 8.228 us/op
Having shoelace already in mind from previous day helped this one - only catch was to include ~half the perimeter.phldavies
12/18/2023, 11:46 AMPaul Woitaschek
12/18/2023, 11:51 AMMovses Elbakian
12/18/2023, 12:07 PMMichael de Kaste
12/18/2023, 12:19 PMephemient
12/18/2023, 1:43 PM@JvmName("plusPairInt")
operator fun Pair<Int, Int>.plus(other: Pair<Int, Int>): Pair<Int, Int> =
this.first + other.first to this.second + other.second
@JvmName("plusPairLong")
operator fun Pair<Long, Long>.plus(other: Pair<Long, Long>): Pair<Long, Long> =
this.first + other.first to this.second + other.second
// etc.
instead, then it'll be static dispatchephemient
12/18/2023, 1:45 PMMovses Elbakian
12/18/2023, 1:46 PMMichael de Kaste
12/18/2023, 1:48 PMFredrik Rødland
12/18/2023, 1:57 PMvar area = 0L
var y = 0L
data.forEach { (d, t) ->
when (d) {
R -> area += y * t
D -> y += t
L -> area -= y * t
U -> y -= t
}
}
(I used the shoelace formula myself)Jakub Gwóźdź
12/18/2023, 2:04 PMy
and when going left/right you increase/decrease the area by y
at that moment .
Generally it's calculating the integral of function (which is the area below/inside the shape)ephemient
12/18/2023, 2:05 PMdaugian
12/18/2023, 2:07 PMJakub Gwóźdź
12/18/2023, 2:07 PMdaugian
12/18/2023, 2:08 PMMichael de Kaste
12/18/2023, 2:09 PMephemient
12/18/2023, 2:10 PMMichael de Kaste
12/18/2023, 2:10 PMephemient
12/18/2023, 2:10 PMephemient
12/18/2023, 2:12 PMFredrik Rødland
12/18/2023, 2:39 PMdaugian
12/18/2023, 4:14 PMdaugian
12/18/2023, 4:15 PMCharles Flynn
12/18/2023, 5:15 PMRiccardo Lippolis
12/18/2023, 5:57 PMjava.awt.Polygon
again after day 10, I decided to apply the things I learned from you guys that day regarding the shoelace formula and actually got a proper solution today! So thank you for teaching me! 🤓Ozioma Ogbe
12/19/2023, 3:54 PM%use kandy
import org.jetbrains.kotlinx.dataframe.api.dataFrameOf
import java.io.File
val input = File("input1.txt").readText().trim()
public enum class Direction {
UP, DOWN, LEFT, RIGHT
}
data class Point(val x: Long, val y: Long)
fun String.commandToDirection(): Direction {
return when (this) {
"U" -> Direction.UP
"D" -> Direction.DOWN
"L" -> Direction.LEFT
"R" -> Direction.RIGHT
else -> error("Unknown direction $this")
}
}
fun executeCommand(
currentPoint: Point,
command: Pair<Direction, Long>
): Pair<Direction, Point> {
fun mapit(it: Long) = when (command.first) {
Direction.UP -> currentPoint.copy(x = currentPoint.x + it)
Direction.DOWN -> currentPoint.copy(x = currentPoint.x - it)
Direction.LEFT -> currentPoint.copy(y = currentPoint.y - it)
Direction.RIGHT -> currentPoint.copy(y = currentPoint.y + it)
}
return command.first to mapit(command.second)
}
val lines = input.trim().split("\n")
val visitedPoints = mutableSetOf<Point>()
visitedPoints.add(Point(0, 0))
val commands = lines.map {
val line = it.split(" ").last().drop(1).dropLast(1)
val arr = arrayOf("R", "D", "L", "U")
arr[line.last().digitToInt()].commandToDirection() to line.dropLast(1).drop(1).toLong(16)
// val line = it.split(" ")
// line[0].commandToDirection() to line[1].toLong()
}
//println(commands )
val points = mutableListOf<Point>()
points.add(Point(0, 0))
var add = false
commands.fold(commands.first().first to points.last()) { acc, command ->
val r = executeCommand(acc.second, command)
points.add(r.second)
r
}
val positiveOrientedPoints =
points.map { it.copy(x = it.x + min) }
fun polygonArea(): Long {
val pp = listOf(points.first()) + points.reversed()
return abs(pp.mapIndexed { index, point ->
if (index == 0) {
0L
} else {
val prevPoint = pp[index - 1]
((point.y * prevPoint.x) - (prevPoint.y * point.x)).toLong()
}
}.sum() / 2)
}
println("Area ${commands.map { it.second }.sum() / 2 + 1 + polygonArea()}")
plot {
layout {
size = (800 to 800)
}
path {
y(points.map { it.x })
x(points.map { it.y })
}
}
Ozioma Ogbe
12/19/2023, 3:55 PM