Day 13: Transparent Origami
by @adpi2
Puzzle description
https://adventofcode.com/2021/day/13
Modeling the objects
The instructions of the manual are composed of dots and folds. We can model those two objects in Scala 3 with:
case class Dot(x: Int, y: Int)
enum Fold:
case Vertical(x: Int)
case Horizontal(y: Int)
A Dot
is made of two integers: x
and y
.
A Fold
is either Vertical
if it has an x
coordinate or Horizontal
if it has a y
coordinate.
Parsing
In order to parse dots and folds, we create two parse
functions in the companion objects of Dot
and Fold
.
object Dot:
def parse(line: String): Dot =
line match
case s"$x,$y" => Dot(x.toInt, y.toInt)
case _ => throw new Exception(s"Cannot parse '$line' to Dot")
object Fold:
def parse(line: String): Fold =
line match
case s"fold along x=$x" => Vertical(x.toInt)
case s"fold along y=$y" => Horizontal(y.toInt)
case _ => throw new Exception(s"Cannot parse '$line' to Fold")
Now, all the instructions can be parsed as follow:
def parseInstructions(input: String): (Set[Dot], List[Fold]) =
val sections = input.split("\n\n")
val dots = sections(0).linesIterator.map(Dot.parse).toSet
val folds = sections(1).linesIterator.map(Fold.parse).toList
(dots, folds)
Notice that we return a set of Dot
and a list of Fold
.
A set is different from a list because it cannot contain the same element twice. Indeed, inserting an element in a set that already contains it does not alter the set. It returns the same set in which the element is stored only once.
For example:
$ Set(1, 2, 3, 3) == Set(1, 2, 3)
true
$ List(1, 2, 3, 3) == List(1, 2, 3)
false
It is convenient to choose a Set
to store the Dot
s because it will merge all duplicates automatically.
This choice will make our program shorter and more efficient.
Folding
We want to compute the folds of all the dots.
To do so we can add a method apply
in the enum Fold
.
It takes an instance of Dot
as parameter and returns the folded Dot
.
def apply(dot: Dot): Dot =
this match
case Vertical(x: Int) => Dot(fold(along = x)(dot.x), dot.y)
case Horizontal(y : Int) => Dot(dot.x, fold(along = y)(dot.y))
In this method we call a function fold
that is not yet defined.
fold
is the mathematical formula that computes the folded value of an integer (value
) around another integer (along
).
def fold(along: Int)(value: Int): Int =
if value < along then value
else along - (value - along)
We can check this formula with some examples:
- Folding a dot at 2 along 7 does not move the dot because
2 < 7
. - Folding a dot at 9 along 7 (
......|.#
) moves the dot to7 - 2 = 5
(....#.|..
).
Solution of part 1
We are now ready to solve part 1:
def part1(input: String): Int =
val (dots, folds) = parseInstructions(input)
dots.map(folds.head.apply).size
Solution of part 2
To compute the solution of part 2 we apply all folds
sequentially, using the foldLeft
method.
To format the answer as if it was made of dots on a paper, we create a double array Array[Array[Char]]
of size (height, width)
initialized with .
. Then we iterate over all dots to put a #
at their position in the double array.
Finally we convert this double array to a String
with .map(_.mkString).mkString('\n')
.
def part2(input: String): String =
val (dots, folds) = parseInstructions(input)
val foldedDots = folds.foldLeft(dots)((dots, fold) => dots.map(fold.apply))
val (width, height) = (foldedDots.map(_.x).max + 1, foldedDots.map(_.y).max + 1)
val paper = Array.fill(height, width)('.')
for dot <- foldedDots do paper(dot.y)(dot.x) = '#'
paper.map(_.mkString).mkString("\n")
Run it locally
You can get this solution locally by cloning the scalacenter/scala-advent-of-code repository.
$ git clone https://github.com/scalacenter/scala-advent-of-code
$ cd scala-advent-of-code
You can run it with scala-cli.
$ scala-cli 2021 -M day13.part1
The answer is: 788
$ scala-cli 2021 -M day10.part2
The answer is:
#..#...##.###..#..#.####.#..#.###...##.
#.#.....#.#..#.#.#..#....#..#.#..#.#..#
##......#.###..##...###..#..#.###..#...
#.#.....#.#..#.#.#..#....#..#.#..#.#.##
#.#..#..#.#..#.#.#..#....#..#.#..#.#..#
#..#..##..###..#..#.####..##..###...###
You can replace the content of the input/day13
file with your own input from
adventofcode.com to get your own
solution.
Solutions from the community
Share your solution to the Scala community by editing this page.