Skip to content

Commit dfae621

Browse files
committed
2021 d13: add input, solutions and walkthrough
1 parent 23a0309 commit dfae621

File tree

4 files changed

+1254
-1
lines changed

4 files changed

+1254
-1
lines changed

2021/README.md

+179-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Table of Contents
1616
- [Day 10 - Syntax Scoring][d10]
1717
- [Day 11 - Dumbo Octopus][d11]
1818
- [Day 12 - Passage Pathing][d12]
19+
- [Day 13 - Transparent Origami][d13]
1920

2021

2122
Day 1 - Sonar Sweep
@@ -2390,6 +2391,176 @@ print('Part 2:', n)
23902391

23912392
As "easy" as that!
23922393

2394+
2395+
Day 13 - Transparent Origami
2396+
----------------------------
2397+
2398+
[Problem statement][d13-problem][Complete solution][d13-solution][Back to top][top]
2399+
2400+
### Part 1
2401+
2402+
Today we need to fold a sheet of paper a bunch of times. Interesting. We are
2403+
given a list of points in space (i.e. dots of ink on our paper sheet) in the
2404+
form `x,y`. Our coordinate system starts from the top-left corner of our paper
2405+
sheet, with X coordinates growing right, and Y coordinates growing *down*. After
2406+
this, we are given a list of directions of two possible forms:
2407+
2408+
- `fold along x=XXX` we need to "fold" our paper sheet along the axis `x=XXX`,
2409+
which means folding *up* the bottom half of the sheet.
2410+
- `fold along y=YYY` we need to "fold" our paper sheet along the axis `y=YYY`,
2411+
which means folding *left* the right half of the sheet.
2412+
2413+
Our sheet is transparent, so when folding, if two points end up being on top of
2414+
each other we will only see one. For the first part, we only need to perform the
2415+
first fold instruction, and then count the number of visible points.
2416+
2417+
Let's start by building our transparent paper sheet. Since it is transparent
2418+
and, as the problem statement says, we need to count any overlapping points as a
2419+
single point after folding, we can use a `set` of tuples `(x, y)` to represent
2420+
the sheet. This will make it easy to ignore overlaps.
2421+
2422+
For each line of input, [`.split()`][py-str-split] it in half and turn the two
2423+
numbers into a `tuple` of `int` with the help of [`map()`][py-builtin-map].
2424+
Then, add the tuple to the set. Since folding instructions are separated from
2425+
the list of coordinates by an empty line, when we found one we'll `break` and
2426+
stop processing coordinates.
2427+
2428+
```python
2429+
sheet = set()
2430+
2431+
for line in fin:
2432+
if line == '\n':
2433+
break
2434+
2435+
coords = tuple(map(int, line.split(',')))
2436+
sheet.add(coords)
2437+
```
2438+
2439+
Folding our paper sheet is nothing more than a [reflection][wiki-reflection]
2440+
along an axis that is only applied to the points past the axis. Since our
2441+
reflection axes can only be horizontal or vertical, and since we only need to
2442+
reflect points *past* the axis, the operation is quite simple. Reflecting a
2443+
point is nothing more than "moving it" as far on the opposite side of the axis
2444+
as its original distance from the axis.
2445+
2446+
- For a vertical reflection with axis `x=A`, the `x` coordinate of a point
2447+
becomes `A - (x - A)` or `2*A - x`.
2448+
- For an horizontal reflection with axis `y=A`, the `y` coordinate of a point
2449+
becomes `A - (y - A)` or `2*A - y`.
2450+
2451+
Let's write a `fold()` function to do this. For simplicity, this function will
2452+
take 3 arguments: the sheet, the distance of the reflection axis from the X or Y
2453+
axis, and a boolean value to indicate whether we are folding vertically or
2454+
horizontally. For each point of the sheet, depending on the folding direction,
2455+
we'll move the X or Y coordinate as defined above, add the "moved" point to a
2456+
new sheet.
2457+
2458+
```python
2459+
def fold(sheet, axis, vertical=False):
2460+
folded = set()
2461+
2462+
for x, y in sheet:
2463+
if vertical:
2464+
if x > axis:
2465+
x = axis - (x - axis)
2466+
elif y > axis:
2467+
y = axis - (y - axis)
2468+
2469+
folded.add((x, y))
2470+
2471+
return folded
2472+
```
2473+
2474+
Now we can parse the first folding instruction and perform the fold. To only get
2475+
one line of input we can either call [`next()`][py-builtin-next] on the input
2476+
file or use [`.readline()`][py-io-readline]. The axis coordinate can be
2477+
extracted by locating the `=` with [`.index()`][py-str-index], and the folding
2478+
direction can be determined by checking whether the instruction contains `'x'`
2479+
or not.
2480+
2481+
```python
2482+
line = next(fin)
2483+
axis = int(line[line.index('=') + 1:])
2484+
vertical = 'x' in line
2485+
sheet = fold(sheet, axis, vertical)
2486+
n_points = len(sheet)
2487+
2488+
print('Part 1:', n_points)
2489+
```
2490+
2491+
### Part 2
2492+
2493+
Predictably enough, now we need to apply *all* folding instructions. The point
2494+
visible on the final folded paper sheet will line up to form a sequence of
2495+
letters, which is our answer.
2496+
2497+
We already have all we need for folding... it's only a matter of wrapping it
2498+
inside a loop:
2499+
2500+
```python
2501+
for line in fin:
2502+
axis = int(line[line.index('=') + 1:])
2503+
sheet = fold(sheet, axis, 'x' in line)
2504+
```
2505+
2506+
After applying all folds, we can print out the resulting sheet and read the
2507+
letters by hand. After determining the maximum X and Y coordinates for the
2508+
points in the sheet, we can use a trivial double `for` loop to iterate over all
2509+
the coordinates from `(0, 0)` to the maximum X and Y, printing one `#`
2510+
symbol for every point that is present in the sheet, and one space for every
2511+
point that is not.
2512+
2513+
We can use [`max()`][py-builtin-max] along with a generator expression to get
2514+
the maximum values for the X and Y coordinates in our `sheet`. Note that we need
2515+
to first iterate over Y and then over X to get the sheet printed in the proper
2516+
direction!
2517+
2518+
```python
2519+
def print_sheet(sheet):
2520+
maxx = max(p[0] for p in sheet)
2521+
maxy = max(p[1] for p in sheet)
2522+
2523+
out = ''
2524+
for y in range(maxy + 1):
2525+
for x in range(maxx + 1):
2526+
out += '#' if (x, y) in sheet else ' '
2527+
out += '\n'
2528+
2529+
print(out, end='')
2530+
```
2531+
2532+
We can also use [`itemgetter()`][py-operator-itemgetter] to extract the
2533+
coordinates from our points when calculating the minimum and maximum:
2534+
2535+
```diff
2536+
def print_sheet(sheet):
2537+
- maxx = max(p[0] for p in sheet)
2538+
- maxy = max(p[1] for p in sheet)
2539+
+ maxx = max(map(itemgetter(0), sheet))
2540+
+ maxy = max(map(itemgetter(1), sheet))
2541+
...
2542+
```
2543+
2544+
Okay, let's print it!
2545+
2546+
```python
2547+
print('Part 2:')
2548+
print_sheet(sheet)
2549+
```
2550+
2551+
```
2552+
Part 2:
2553+
### ## # # ### # # # # # #
2554+
# # # # # # # # # # # # # #
2555+
# # # #### # # ## # ## #
2556+
### # ## # # ### # # # # # #
2557+
# # # # # # # # # # # # #
2558+
# ### # # # # # # #### # # ####
2559+
```
2560+
2561+
Cool puzzle! Today was also my first day of the year on the leaderboard (rank 37
2562+
for P1). Phew, that took some time :')
2563+
23932564
---
23942565

23952566
*Copyright © 2021 Marco Bonelli. This document is licensed under the [Creative Commons BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) license.*
@@ -2409,6 +2580,7 @@ As "easy" as that!
24092580
[d10]: #day-10---syntax-scoring
24102581
[d11]: #day-11---dumbo-octopus
24112582
[d12]: #day-12---passage-pathing
2583+
[d13]: #day-13---transparent-origami
24122584

24132585
[d01-problem]: https://adventofcode.com/2021/day/1
24142586
[d02-problem]: https://adventofcode.com/2021/day/2
@@ -2422,6 +2594,7 @@ As "easy" as that!
24222594
[d10-problem]: https://adventofcode.com/2021/day/10
24232595
[d11-problem]: https://adventofcode.com/2021/day/11
24242596
[d12-problem]: https://adventofcode.com/2021/day/12
2597+
[d13-problem]: https://adventofcode.com/2021/day/13
24252598

24262599
[d01-solution]: solutions/day01.py
24272600
[d02-solution]: solutions/day02.py
@@ -2435,6 +2608,7 @@ As "easy" as that!
24352608
[d10-solution]: solutions/day10.py
24362609
[d11-solution]: solutions/day11.py
24372610
[d12-solution]: solutions/day12.py
2611+
[d13-solution]: solutions/day13.py
24382612

24392613
[d03-orginal]: original_solutions/day03.py
24402614
[d07-orginal]: original_solutions/day07.py
@@ -2458,6 +2632,7 @@ As "easy" as that!
24582632
[py-builtin-map]: https://docs.python.org/3/library/functions.html#map
24592633
[py-builtin-max]: https://docs.python.org/3/library/functions.html#max
24602634
[py-builtin-min]: https://docs.python.org/3/library/functions.html#min
2635+
[py-builtin-next]: https://docs.python.org/3/library/functions.html#next
24612636
[py-builtin-sorted]: https://docs.python.org/3/library/functions.html#sorted
24622637
[py-builtin-sum]: https://docs.python.org/3/library/functions.html#sum
24632638
[py-builtin-zip]: https://docs.python.org/3/library/functions.html#zip
@@ -2468,20 +2643,22 @@ As "easy" as that!
24682643
[py-frozenset]: https://docs.python.org/3/library/stdtypes.html#frozenset
24692644
[py-functools]: https://docs.python.org/3/library/functools.html
24702645
[py-functools-partial]: https://docs.python.org/3/library/functools.html#functools.partial
2646+
[py-io-readline]: https://docs.python.org/3/library/io.html#io.IOBase.readline
24712647
[py-itertools-count]: https://docs.python.org/3/library/itertools.html#itertools.count
24722648
[py-itertools-product]: https://docs.python.org/3/library/itertools.html#itertools.product
24732649
[py-itertools-repeat]: https://docs.python.org/3/library/itertools.html#itertools.repeat
24742650
[py-itertools-starmap]: https://docs.python.org/3/library/itertools.html#itertools.starmap
24752651
[py-itertools-chain]: https://docs.python.org/3/library/itertools.html#itertools.chain
24762652
[py-list-sort]: https://docs.python.org/3/library/stdtypes.html#list.sort
2653+
[py-operator-itemgetter]: https://docs.python.org/3/library/operator.html#operator.itemgetter
24772654
[py-set-intersection]: https://docs.python.org/3/library/stdtypes.html#frozenset.intersection
24782655
[py-statistics-median-low]: https://docs.python.org/3/library/statistics.html#statistics.median_low
2656+
[py-str-index]: https://docs.python.org/3/library/stdtypes.html#str.index
24792657
[py-str-maketrans]: https://docs.python.org/3/library/stdtypes.html#str.maketrans
24802658
[py-str-rstrip]: https://docs.python.org/3/library/stdtypes.html#str.rstrip
24812659
[py-str-split]: https://docs.python.org/3/library/stdtypes.html#str.split
24822660
[py-str-splitlines]: https://docs.python.org/3/library/stdtypes.html#str.splitlines
24832661
[py-str-translate]: https://docs.python.org/3/library/stdtypes.html#str.translate
2484-
24852662
[algo-bfs]: https://en.wikipedia.org/wiki/Breadth-first_search
24862663
[algo-dfs]: https://en.wikipedia.org/wiki/Depth-first_search
24872664
[algo-quicksort]: https://en.wikipedia.org/wiki/Quicksort
@@ -2497,6 +2674,7 @@ As "easy" as that!
24972674
[wiki-median]: https://en.wikipedia.org/wiki/Median
24982675
[wiki-pushdown-automata]: https://en.wikipedia.org/wiki/Pushdown_automaton
24992676
[wiki-queue]: https://en.wikipedia.org/wiki/Queue_(abstract_data_type)
2677+
[wiki-reflection]: https://en.wikipedia.org/wiki/Reflection_(mathematics)
25002678
[wiki-seven-segment-display]: https://en.wikipedia.org/wiki/Seven-segment_display
25012679
[wiki-stack]: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)
25022680
[wiki-triangular-number]: https://en.wikipedia.org/wiki/Triangular_number

0 commit comments

Comments
 (0)