Skip to content

Instantly share code, notes, and snippets.

@pjg11
Last active December 31, 2023 15:45
Show Gist options
  • Select an option

  • Save pjg11/33a5760a80b17f9aa62bec5cbd3434f6 to your computer and use it in GitHub Desktop.

Select an option

Save pjg11/33a5760a80b17f9aa62bec5cbd3434f6 to your computer and use it in GitHub Desktop.
Solutions for Advent Of Code 2023
#!/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