# Advent of Code 2021 - Day 13, in Kotlin - Transparent Origami

Kotlin solutions to parts 1 and 2 of Advent of Code 2021, Day 13: 'Transparent Origami'

Posted on

When I saw the title of today’s puzzle (“Transparent Origami”) I thought we would be folding up some crabs, octopuses, or whales. Instead, we’re pretending we’ve bought a cool game from the 1980s and need to look up a code in the decoder ring to play it. Except this time it’s a sub-mounted thermal camera.

I like to think there’s a nonzero chance that Eric Wastl decided to have us fold paper to confuse the functional programmers out there who use the fold function a lot.

If you’d rather just view code, the GitHub Repository is here .

Problem Input

Since our input is a single file with two different parts, we’ll write two functions to do the parsing. We could write a single function that returns a `Pair`, but I think this just looks cleaner.

``````class Day13(input: List<String>) {

private val paper: Set<Point2d> = parsePoints(input)
private val instructions: List<Point2d> = parseInstructions(input)

private fun parsePoints(input: List<String>): Set<Point2d> =
input.takeWhile { it.isNotEmpty() }
.map { it.split(",") }
.map { Point2d(it.first().toInt(), it.last().toInt()) }
.toSet()

private fun parseInstructions(input: List<String>): List<Point2d> =
input.takeLastWhile { it.isNotEmpty() }
.map { it.split("=") }
.map {
if (it.first().endsWith("y")) {
Point2d(0, it.last().toInt())
} else {
Point2d(it.last().toInt(), 0)
}
}
}
``````

The first function, `parsePoints` reads input until it finds an empty line and then stops using `takeWhile`. For every line it finds, we `split` the coordinates on a comma and then `map` the results into a `Point2d`, which we wrote a few days ago and have used a few times now.

The second `parseInstructions` will skip lines in the input until it finds an empty line, and then lets us have every one after that. I haven’t used the `takeLastWhile` function too many times, but it’s very handy for situations like this. Every instruction row gets `split` on the equals sign, so we end up with a list like this: `["fold along x", "655"]`. We could parse the first part a bit further to isolate the `x` or `y` from the instruction, but since it is always at the end of the `String`, we can just use `endsWith` to figure it out. Depending on whether we are parsing an x instruction or a y instruction, we’ll once again use `Point2d` to represent this. There is a catch - we can’t represent an instruction that deals with 0. But since I don’t have any in my input, I assume that’s generally safe.

#### ⭐ Day 13, Part 1

The puzzle text can be found here.

Before we start in on our solution, I want to talk about terminology. As we’ve seen from days past, Kotlin has a function called `fold` that allows us to operate on a sequence of values and carry some state along. And the puzzle for today involves folding. Because we’ll legitimately use the `fold` function in part two, and I don’t want there to be any confusion (this is as much for my sanity as it is for yours, I assure you), anything involving the puzzle’s concept of folding paper will be called `crease`. So if you see `fold`, think function. If you see `crease`, think bending paper.

Let’s start with the math we’ll have to do when calculating where a `Point2d` ends up when we crease the paper. We’ll implement it as an extension function in `Int`:

``````// In Day13

private fun Int.creaseAt(crease: Int): Int =
if (this < crease) this else (crease * 2) - this
``````

Another way to think of that calculation would be `crease - (this - crease)`. Calculate the different between the point being moved and its crease point, and then subtract that amount from the crease point to mirror the point on the other side of the crease.

Now that we have this, we can actually perform the crease of an entire `Set<Point2d>`. We’ll do this with another extension function called `crease`.

``````// In Day13

private fun Set<Point2d>.crease(instruction: Point2d): Set<Point2d> =
if (instruction.x != 0) this.map { it.copy(x = it.x.creaseAt(instruction.x)) }.toSet()
else this.map { it.copy(y = it.y.creaseAt(instruction.y)) }.toSet()
``````

This function takes an `instruction` and figures out if we’re talking about the x or y axis to crease on. Depending on which one it is, we make a `copy` of each `Point2d`, specifying the new x or y value as needed. This calls the `creaseAt` extension function we wrote above.

Now we can solve part one:

``````// In Day13

fun solvePart1(): Int =
paper.crease(instructions.first()).size
``````

Because part one only calls for the number of points (the size of our paper!) after applying the first crease instruction, we can use `instructions.first()` to grab that and pass it to our `crease` function.

Star earned! Onward!

#### ⭐ Day 13, Part 2

The puzzle text can be found here.

There have been a couple of puzzles in the past where the instructions ask for us to interpret some printed data. If we really wanted to, we could write some code to interpret our data and output the text instead of printing it and interpreting ourselves, but I’m not going to do that. Not every problem we solve as programmers involves programming.

In order to print our `Set<Point2d>`, we’ll need to know both the maximum x and y values. Similar to how we’ve parsed data in the past, we’ll set up two ranges along y and then x, and `print` an “on” character if we find the point in the set, and an “off” character otherwise. I picked hash and space respectively, but you can pick whatever you think looks best.

``````// In Day13

private fun Set<Point2d>.printout() {
(0..this.maxOf { it.y }).forEach { y ->
(0..this.maxOf { it.x }).forEach { x ->
print(if (Point2d(x, y) in this) "#" else " ")
}
println()
}
}
``````

I found that backing up from my computer and reading the results from about a meter away made them more clear.

Now we can actually use the `fold` function (read the note above, we’re using a “functional fold” here, not creasing a piece of paper. I mean, we are, but still…). Anyway.

``````// In Day13

fun solvePart2(): Unit =
instructions.fold(paper) { paper, instruction -> paper.crease(instruction) }.printout()
``````

We `fold` over our `instructions` and seed our calculation with the start state of the `paper`. For each new `instruction`, we `crease` the `paper`. At the end, we have a fully creased `Set<Point2d>`, so we call `printout` on it.

Star (very confusingly) earned!

Congratulations for making it this far, there are now fewer 2021 Advent of Code puzzles ahead of us than there are behind us.