Last active
December 31, 2023 15:45
-
-
Save pjg11/33a5760a80b17f9aa62bec5cbd3434f6 to your computer and use it in GitHub Desktop.
Solutions for Advent Of Code 2023
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -eu | |
| echo '--- Day 1: Trebuchet?! ---' | |
| echo -n 'Part 1: ' | |
| # Combine first and last instance of digit as a two-digit number and find | |
| # sum. Sample input: | |
| # | |
| # 1abc2 -> 12 | |
| # pqr3stu8vwx -> 38 | |
| # a1b2c3d4e5f -> 15 | |
| # treb7uchet -> 77 | |
| # = 142 | |
| perl -ne 'print join(" ",m/[1-9]/g) . "\n"' day1 \ | |
| | gawk -F ' ' '{ s += $1$NF } END { print s }' | |
| echo -n 'Part 2: ' | |
| # Digits spelled out with letters also count as valid digits. | |
| # Sample input: | |
| # | |
| # two1nine -> 29 | |
| # eightwothree -> 83 | |
| # abcone2threexyz -> 13 | |
| # xtwone3four -> 24 | |
| # 4nineeightseven2 -> 42 | |
| # zoneight234 -> 14 | |
| # 7pqrstsixteen -> 76 | |
| # = 281 | |
| perl -ne 'BEGIN { my %numbers = ("one" => 1, "two" => 2, "three" => 3, "four" => 4, "five" => 5, "six" => 6, "seven" => 7, "eight" => 8, "nine" => 9) };' \ | |
| -ne 'while(m/(?=(one|two|three|four|five|six|seven|eight|nine|[1-9]))/g){ print $numbers{$1} ? $numbers{$1} : $1 ; print " " } ; print "\n"' day1 \ | |
| | gawk -F ' ' '{ s += $1$NF } END { print s }' | |
| # Gotcha: the string "eightwo" in the example is supposed to match as both | |
| # 8 and 2, which was the tough part. More so in bash, as regular grep | |
| # doesn't work this way, and I found a perl solution through | |
| # https://stackoverflow.com/a/19576079 . Positive lookaheads are generally | |
| # not printed as a match, but Perl prints it, which helped. | |
| echo | |
| echo '--- Day 2: Cube Conundrum ---' | |
| # Elf will reach into the bag, grab a handful of random cubes, show them to | |
| # you, and then put them back in the bag. He'll do this a few times per | |
| # game. Find some information based on the number of cubes for each color. | |
| # The matches aren't necessarily in order, so the checking was a little | |
| # confusing. I decided to calculate the max red, green and blue match on | |
| # each line, and compare with that. The output ended up being useful for | |
| # part 2 as well, so saving it as a variable to prevent recomputing for | |
| # both parts. | |
| # | |
| # The matching relies on capture groups, where matches within () are | |
| # considered as one group. In this case, $1 contains matches for red, | |
| # $2 for green and $3 for blue. | |
| max=$(perl -ne 'my $r = $g = $b = 0; while(m/([0-9]+(?= red))|([0-9]+(?= green))|([0-9]+(?= blue))/g){ if($1 > $r){ $r = $1 }; if($2 > $g){ $g = $2 }; if($3 > $b){ $b = $3 }; }; print "$r $g $b\n"' day2) | |
| echo -n 'Part 1: ' | |
| # Calculate sum of game numbers which are possible with 12 red, 13 green, | |
| # and 14 blue cubes. | |
| # Sample input: | |
| # | |
| # Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green | |
| # Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue | |
| # Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red | |
| # Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red | |
| # Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green | |
| # | |
| # Games 3 and 4 are not possible, so the sum is 1+2+5 = 8. | |
| gawk -F' ' '( $1 <= 12 && $2 <= 13 && $3 <= 14 ) { s+= NR } END { print s }' <<< "$max" | |
| echo -n 'Part 2: ' | |
| # Find the fewest number of cubes of each color that could have been in the | |
| # bag to make the game possible. Using the same sample input as above: | |
| # | |
| # Game 1: 4 red, 2 green, 6 blue | |
| # Game 2: 1 red, 3 green, and 4 blue | |
| # Game 3: 20 red, 13 green, and 6 blue | |
| # Game 4: 14 red, 3 green, and 15 blue | |
| # Game 5: 6 red, 3 green, and 2 blue | |
| # | |
| # Multiply the numbers for each game and add those together. | |
| # (4*2*6) + (1*3*4) + (20*13*6) + (14*3*15) + (6*3*2) = 2286 | |
| gawk '{ s += $1*$2*$3 } END { print s }' <<< "$max" | |
| echo | |
| echo '--- Day 3: Gear Ratios ---' | |
| echo -n 'Part 1: ' | |
| # Add up all part numbers, aka numbers adjacent to a symbol | |
| # Sample input: | |
| # | |
| # 467..114.. | |
| # ...*...... | |
| # ..35..633. | |
| # ......#... | |
| # 617*...... | |
| # .....+.58. | |
| # ..592..... | |
| # ......755. | |
| # ...$.*.... | |
| # .664.598.. | |
| # | |
| # 114 and 58 are not part numbers, so sum is | |
| # 467 + 35 + 633 + 617 + 592 + 755 + 664 + 598 = 4361 | |
| python3 -c 'import re; f = open("day3", "r"); lines = f.read().splitlines(); print(sum([int(m.group()) for i,l in enumerate(lines) for m in re.finditer("[0-9]+", l) if re.search("#|\$|%|&|\*|\+|-|/|=|@", "".join(map(lambda x: x[m.start()-1 if m.start()-1 > 0 else None : m.end()+1 if m.end()+1 < len(lines[i]) else None ],lines[i-1 if i-1 > 0 else None : i+2 if i+2 < len(lines) else None])))])); f.close()' | |
| echo -n 'Part 2: ' | |
| # Find gears, aka a '*' symbol that is adjacent to exactly two numbers. | |
| # Multiply the two adjacent numbers for each gear and add all the products. | |
| # Using the same sample input as above: | |
| # Gear 1: 467 * 35 -> 16345 | |
| # Gear 2: 755 * 598 -> 451490 | |
| # sum = 467835 | |
| python3 -c 'import re; from functools import reduce; f = open("day3", "r"); lines = f.read().splitlines(); print(sum([k[0] * k[1] for k in [list(map(lambda x: int(x.group()), filter(lambda x: m.start() >= x.start()-1 and m.start() <= x.end(), reduce(lambda x, y: list(x) + list(y), map(lambda x: re.finditer("[0-9]+", x), lines[i-1 if i-1 > 0 else None : i+2 if i+2 < len(lines) else None]))))) for i,l in enumerate(lines) for m in re.finditer("\*", l)] if len(k) == 2])); f.close()' | |
| echo | |
| echo '--- Day 4: Scratchcards ---' | |
| # Each card in the input has two lists of numbers separated by a vertical | |
| # bar (|): a list of winning numbers and then a list of numbers you have. | |
| # | |
| # Both parts require the number of winning numbers that match. | |
| matches=$(gawk -F'[:|]' '{ gsub(" +", "|", $2); gsub(/^\|/, "\\y(", $2); gsub(/\|$/, ")\\y", $2); str = $3; c = 0; while(match(str, $2, arr)) { c++; str = substr(str, RSTART + (RLENGTH ? RLENGTH : 1)); } print c }' day4) | |
| echo -n 'Part 1: ' | |
| # Figure out which of the numbers you have appear in the list of winning | |
| # numbers. The first match = one point, each match after the first doubles | |
| # the point value of that card. | |
| # Sample input: | |
| # | |
| # Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 | |
| # Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 | |
| # Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 | |
| # Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 | |
| # Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 | |
| # Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11 | |
| # | |
| # Card 1: 48, 83, 17, 86 -> 8 | |
| # Card 2: 32, 61 -> 2 | |
| # Card 3: 1, 21 -> 2 | |
| # Card 4: 84 -> 1 | |
| # Card 5: None -> 0 | |
| # Card 6: None -> 0 | |
| # = 13 | |
| gawk '{ if ($0 >= 1) { sum += 1*(2**($0-1)); } }; END { print sum }' <<< "$matches" | |
| echo -n 'Part 2: ' | |
| # Number of winning numbers in each card results in winning copies of | |
| # scratchcards below the winning card. The copies win further copies, until none | |
| # of the copies cause you to win more cards. The sum of cards (original and | |
| # copies) is the solution for the challenge. | |
| # Using the same sample input as above: | |
| # | |
| # - Card 1 (one original) has four matching numbers, so you win one copy each of | |
| # the next four cards: cards 2, 3, 4, and 5. | |
| # - Card 2 (one original and one copy) as two matching numbers, so you win two | |
| # copies each of cards 3 and 4. | |
| # - Card 3 (one original and three copies) has two matching numbers, so you win | |
| # four copies each of cards 4 and 5. | |
| # - Card 4 (one original and seven copies) have one matching number, so you win | |
| # eight copies of card 5. | |
| # - Card 5 (one original and thirteen copies) have no matching numbers and win | |
| # no more cards. | |
| # - Card 6 (one original) has no matching numbers and wins no more cards. | |
| # | |
| # Total is 1 + 2 + 4 + 8 + 14 + 1 = 30 | |
| # https://www.youtube.com/watch?v=7tgaQTX02zw helped with figuring out the logic | |
| gawk '{ m = $0; cards[NR]++; for(; m > 0; m--) cards[NR+m] += cards[NR] } END { for (i=1; i<=NR; i++) sum += cards[i]; print sum }' <<< "$matches" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment