@@ -8,7 +8,7 @@ Table of Contents
8
8
- [ Day 2 - Rock Paper Scissors] [ d02 ]
9
9
- [ Day 3 - Rucksack Reorganization] [ d03 ]
10
10
- [ Day 4 - Camp Cleanup] [ d04 ]
11
-
11
+ - [ Day 5 - Supply Stacks ] [ d05 ]
12
12
13
13
Day 1 - Calorie Counting
14
14
------------------------
@@ -340,6 +340,7 @@ print('Part 2:', group_total)
340
340
341
341
Easy peasy! Daily puzzle solved once again.
342
342
343
+
343
344
Day 4 - Camp Cleanup
344
345
--------------------
345
346
@@ -471,6 +472,179 @@ print('Part 2:', overlap)
471
472
472
473
Et voilà! 8 out of 50 stars.
473
474
475
+
476
+ Day 5 - Supply Stacks
477
+ ---------------------
478
+
479
+ [ Problem statement] [ d05-problem ] — [ Complete solution] [ d05-solution ] — [ Back to top] [ top ]
480
+
481
+ ### Part 1
482
+
483
+ Do you like simulations? Today we're gonna need to simulate a crane moving
484
+ crates stacked on top of each other. We start with a few stacks of crates, which
485
+ is given as input in the following form:
486
+
487
+ ``` none
488
+ [D]
489
+ [N] [C]
490
+ [Z] [M] [P]
491
+ 1 2 3
492
+ ```
493
+
494
+ This represents the initial configuration of N stacks of crates (in the above
495
+ example we have N = 3, but in the real input N = 10). Following the initial
496
+ configuration is an empty line followed by a list of instructions, one per line,
497
+ of the form ` move <n> from <i> to <j> ` , meaning "move the top ` n ` crates from
498
+ stack ` i ` to stack ` j ` , one at a time.
499
+
500
+ After executing all instructions, we need to answer with a string of letters
501
+ representing the topmost crate of each stack in order. For example, if the above
502
+ configuration was the final one, we would answer ` NDP ` .
503
+
504
+ Today's input is particularly annoying to parse: we are given stacks in fancy
505
+ ASCII art columns, and we need to somehow turn each one into a string or list in
506
+ order to work with it. The easiest way to approach this is probably to just read
507
+ the entirety of the first few lines of input, stopping at the first empty line,
508
+ and then * transpose* them to obtain a list of columns. In other words, do
509
+ something like this:
510
+
511
+ ``` none
512
+ +----------> ' [[ '
513
+ |+---------> ' NZ1'
514
+ ||+--------> ' ]] '
515
+ |||+-------> ' '
516
+ ||||+------> '[[[ '
517
+ |||||+-----> 'DCM2'
518
+ ||||||+----> ...
519
+ |||||||
520
+ ' [D] '
521
+ '[N] [C] '
522
+ '[Z] [M] [P]'
523
+ ' 1 2 3 '
524
+ ```
525
+
526
+ After reading the initial ASCII art and stopping at the first empty line:
527
+
528
+ ``` python
529
+ fin = open (... )
530
+ raw = []
531
+
532
+ for line in fin:
533
+ if line == ' \n ' :
534
+ break
535
+ raw.append(line)
536
+
537
+ # raw = [' [D] \n',
538
+ # '[N] [C] \n',
539
+ # '[Z] [M] [P]\n',
540
+ # ' 1 2 3 \n']
541
+ ```
542
+
543
+ We can transpose the ` raw ` list with the help of [ ` zip() ` ] [ py-builtin-zip ] plus
544
+ an [ unpacking operator] [ py-unpacking ] :
545
+
546
+ ``` python
547
+ columns = list (zip (* raw))
548
+ # [(' ', '[', '[', ' ', '\n'),
549
+ # (' ', 'N', 'Z', '1', '\n'),
550
+ # ... ]
551
+ ```
552
+
553
+ This seemingly esoteric single-line transposition works because ` zip() ` yields
554
+ tuples consisting of the i-th element of each line in ` raw ` , i.e. it effectively
555
+ yields columns.
556
+
557
+ We went from strings to tuples, but that's no problem for now. The next thing to
558
+ do is skip all the useless columns (those consisting of only spaces and square
559
+ brackets) and keep the rest, turning good columns into strings through
560
+ [ ` str.join() ` ] [ py-str-join ] and discarding leading whitespace with
561
+ [ ` str.lstrip() ` ] [ py-str-lstrip ] .
562
+
563
+ Fortunately, all we need to do to identify good columns is
564
+ [ ` enumerate() ` ] [ py-builtin-enumerate ] , skip the first column and then only keep
565
+ one every 4, which can be achieved using the modulo (` % ` ) operator.
566
+
567
+ ``` python
568
+ # Indexes in the instructions are 1-based, fill stacks[0] with some useless
569
+ # value so that later we can do stacks[i] instead of stacks[i - 1].
570
+ stacks = [None ]
571
+
572
+ for i, column in enumerate (zip (* raw)):
573
+ if i % 4 == 1 :
574
+ # column = (' ', 'N', 'Z', '1', '\n')
575
+ column = ' ' .join(column[:- 1 ]) # -> ' NZ'
576
+ column = column.lstrip() # -> 'NZ'
577
+ stacks.append(column)
578
+
579
+ # Make a copy to use for part 2
580
+ original = stacks[:]
581
+ ```
582
+
583
+ Now that we * finally* have the initial stacks parsed, let's also parse the
584
+ instructions. This is quite simple: iterate over input lines, split them and
585
+ extract the three numbers at positions ` 1 ` , ` 3 ` and ` 5 ` :
586
+
587
+ ``` python
588
+ moves = []
589
+
590
+ for line in fin:
591
+ line = line.split()
592
+ moves.append((int (line[1 ]), int (line[3 ]), int (line[5 ])))
593
+ ```
594
+
595
+ We have the instructions parsed, now let's simply follow them:
596
+
597
+ ``` python
598
+ for n, i, j in moves:
599
+ for _ in range (n):
600
+ crate = stacks[i][0 ] # Extract top of stacks[i]
601
+ stacks[i] = stacks[1 :] # Remove it from stacks[i]
602
+ stacks[j] = crate + stacks[j] # Add it to top of stacks[j]
603
+ ```
604
+
605
+ We optimize the above operation by extracting all ` n ` crates at once and then
606
+ reversing their order doing ` crate[::-1] ` , a common Python trick to reverse an
607
+ indexable iterable through [ slicing] [ py-slicing ] :
608
+
609
+ ``` python
610
+ for n, i, j in moves:
611
+ chunk = stacks[i][:n][::- 1 ]
612
+ stacks[i] = stacks[i][n:]
613
+ stacks[j] = chunk + stacks[j]
614
+ ```
615
+
616
+ Finally, we can extract the topmost element of each stack using a simple
617
+ [ generator expression] [ py-generator-expr ] and ` .join() ` the result into a
618
+ string:
619
+
620
+ ``` python
621
+ top = ' ' .join(s[0 ] for s in stacks[1 :]) # Skip stacks[0], which is None
622
+ print (' Part 1:' , top)
623
+ ```
624
+
625
+ ### Part 2
626
+
627
+ For part two, we need to follow the same list of instructions as part 1, but
628
+ this time moving * all* of the topmost ` n ` crates from a given stack to another
629
+ at once, meaning that their final order on top of the second stack will * not* be
630
+ reversed.
631
+
632
+ Well, given the code we already wrote, this is really child's play: we can use
633
+ the same code as part 1, removing the reversing operation (` [::-1] ` ):
634
+
635
+ ``` python
636
+ # Restore initial state from the copy we made earlier
637
+ stacks = original
638
+
639
+ for n, i, j in moves:
640
+ chunk = stacks[i][:n] # <- removed [::-1] from here
641
+ stacks[i] = stacks[i][n:]
642
+ stacks[j] = chunk + stacks[j]
643
+
644
+ top = ' ' .join(s[0 ] for s in stacks[1 :])
645
+ print (' Part 2:' , top)
646
+ ```
647
+
474
648
---
475
649
476
650
* Copyright © ; 2022 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.*
@@ -482,36 +656,44 @@ Et voilà! 8 out of 50 stars.
482
656
[ d02 ] : #day-2---rock-paper-scissors
483
657
[ d03 ] : #day-3---rucksack-reorganization
484
658
[ d04 ] : #day-4---camp-cleanup
659
+ [ d05 ] : #day-5---supply-stacks
485
660
486
661
[ d01-problem ] : https://adventofcode.com/2022/day/1
487
662
[ d02-problem ] : https://adventofcode.com/2022/day/2
488
663
[ d03-problem ] : https://adventofcode.com/2022/day/3
489
664
[ d04-problem ] : https://adventofcode.com/2022/day/4
665
+ [ d05-problem ] : https://adventofcode.com/2022/day/5
490
666
491
667
[ d01-solution ] : solutions/day01.py
492
668
[ d02-solution ] : solutions/day02.py
493
669
[ d03-solution ] : solutions/day03.py
494
670
[ d04-solution ] : solutions/day04.py
671
+ [ d05-solution ] : solutions/day05.py
495
672
496
673
[ d02-alternative ] : misc/day02/mathematical.py
497
674
498
675
[ py-cond-expr ] : https://docs.python.org/3/reference/expressions.html#conditional-expressions
676
+ [ py-generator-expr ] : https://www.python.org/dev/peps/pep-0289/
499
677
[ py-list-comprehension ] : https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
500
678
[ py-slicing ] : https://docs.python.org/3/glossary.html#term-slice
501
679
[ py-tuple ] : https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
680
+ [ py-unpacking ] : https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists
502
681
[ py-with ] : https://peps.python.org/pep-0343/
503
682
504
-
505
- [ py-builtin-map ] : https://docs.python.org/3/library/functions.html#map
506
- [ py-builtin-max ] : https://docs.python.org/3/library/functions.html#max
507
- [ py-builtin-min ] : https://docs.python.org/3/library/functions.html#min
508
- [ py-builtin-ord ] : https://docs.python.org/3/library/functions.html#ord
509
- [ py-list ] : https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
510
- [ py-list-sort ] : https://docs.python.org/3/library/stdtypes.html#list.sort
511
- [ py-str-maketrans ] : https://docs.python.org/3/library/stdtypes.html#str.maketrans
512
- [ py-str-split ] : https://docs.python.org/3/library/stdtypes.html#str.split
513
- [ py-str-rstrip ] : https://docs.python.org/3/library/stdtypes.html#str.split
514
- [ py-str-translate ] : https://docs.python.org/3/library/stdtypes.html#str.translate
683
+ [ py-builtin-enumerate ] : https://docs.python.org/3/library/functions.html#enumerate
684
+ [ py-builtin-map ] : https://docs.python.org/3/library/functions.html#map
685
+ [ py-builtin-max ] : https://docs.python.org/3/library/functions.html#max
686
+ [ py-builtin-min ] : https://docs.python.org/3/library/functions.html#min
687
+ [ py-builtin-ord ] : https://docs.python.org/3/library/functions.html#ord
688
+ [ py-builtin-zip ] : https://docs.python.org/3/library/functions.html#zip
689
+ [ py-list ] : https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
690
+ [ py-list-sort ] : https://docs.python.org/3/library/stdtypes.html#list.sort
691
+ [ py-str-join ] : https://docs.python.org/3/library/stdtypes.html#str.join
692
+ [ py-str-lstrip ] : https://docs.python.org/3/library/stdtypes.html#str.lstrip
693
+ [ py-str-maketrans ] : https://docs.python.org/3/library/stdtypes.html#str.maketrans
694
+ [ py-str-rstrip ] : https://docs.python.org/3/library/stdtypes.html#str.rstrip
695
+ [ py-str-split ] : https://docs.python.org/3/library/stdtypes.html#str.split
696
+ [ py-str-translate ] : https://docs.python.org/3/library/stdtypes.html#str.translate
515
697
516
698
[ algo-quickselect ] : https://en.wikipedia.org/wiki/Quickselect
517
699
0 commit comments