DEV Community

Cover image for AoC Day 25: Four-Dimensional Adventure
Ryan Palo
Ryan Palo

Posted on

2 1

AoC Day 25: Four-Dimensional Adventure

Merry Christmas everybody! We made it! One last puzzle to save Christmas. On Day 25, the reindeer is still sick despite our best efforts. In order to save him, we need to calibrate our device to constellations of 4-D points in space-time. But first, we need to figure out which points are in which constellations.

Good luck!

Top comments (1)

Collapse
 
neilgall profile image
Neil Gall • Edited

Last year's Christmas Day was a couple of bonus stars with no actual puzzle to solve so I was expecting the same. But no there's real work to do today, although thankfully part 1 is pretty straightforward.

Parsing

data class Point(val w: Int, val x: Int, val y: Int, val z: Int)

fun parse(input: String): List<Point> {
    val integer = or(
        INTEGER.map(String::toInt),
        isChar('-').next(INTEGER).map { s -> -s.toInt() }
    )

    val point = sequence(
        integer.followedBy(isChar(',')),
        integer.followedBy(isChar(',')),
        integer.followedBy(isChar(',')),
        integer,
        ::Point
    )

    return point.sepBy(WHITESPACES).parse(input.trim())
}

Part 1

Manhattan distance. We've done this before:

typealias Manhattan = Int

fun Point.distance(p: Point): Manhattan =
    Math.abs(w - p.w) + Math.abs(x - p.x) + Math.abs(y - p.y) + Math.abs(z - p.z)

You could model the constellations as a set of set of points but type inference gets a bit hairy in Kotlin so we'll make a wrapper. The inline classes feature coming in the future will make this possible with no runtime overhead. I'll add a few syntactic conveniences such as adding points to constellations and merging them.

data class Constellation(val points: Set<Point>) {
    constructor(p: Point): this(setOf(p))
}

fun Constellation.shouldContain(point: Point): Boolean = 
    points.any { p -> p.distance(point) <= 3 }

operator fun Constellation.plus(p: Point): Constellation =
    Constellation(points + p)

operator fun Constellation.plus(c: Constellation): Constellation = 
    Constellation(points + c.points)

fun Collection<Constellation>.merge(p: Point): Constellation =
    fold(Constellation(p), Constellation::plus)

With all that done, solving part 1 is a fold over the input points. For each point, partition the existing constellations into the ones it should join and the ones it should not. Merge the ones that the point should join and leave the others alone.

fun part1(input: Collection<Point>): Int {
    val constellations = input.fold(setOf<Constellation>()) { cs, p ->
        val (join, dontJoin) = cs.partition { c -> c.shouldContain(p) }
        (dontJoin + join.merge(p)).toSet()
    }
    return constellations.size
}

Part 2

I don't have enough stars to unlock part 2. Will try to get there before the end of the year!

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

Best practices for optimal infrastructure performance with Magento

Running a Magento store? Struggling with performance bottlenecks? Join us and get actionable insights and real-world strategies to keep your store fast and reliable.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️