no this isn't about alexandria ocasio-cortez

day 2, day 3

evan.jarrett.net 09607325 fecd7a2b

verified
+425 -33
+8 -11
cmd/one/main.go
··· 2 2 3 3 import ( 4 4 "fmt" 5 + "log/slog" 5 6 "strconv" 6 7 "strings" 7 8 ··· 31 32 func (d *DayOne) Part1() (int, error) { 32 33 position := 50 33 34 zeroCount := 0 34 - fmt.Printf("The dial starts by pointing at %d.\n", position) 35 + slog.Debug("The dial starts by pointing at", "position", position) 35 36 36 37 for _, rot := range d.rotations { 37 38 newPosition, _, err := Rotate(position, rot) 38 39 if err != nil { 39 40 return 0, err 40 41 } 41 - fmt.Printf("The dial is rotated %d to point at %d.\n", rot, newPosition) 42 + slog.Debug("The dial is rotated", "rotation", rot, "newPosition", newPosition) 42 43 position = newPosition 43 44 if position == 0 { 44 45 zeroCount++ 45 46 } 46 47 } 47 48 48 - fmt.Printf("Final position: %d\n", position) 49 + slog.Debug("Final position", "position", position) 49 50 return zeroCount, nil 50 51 } 51 52 52 53 func (d *DayOne) Part2() (int, error) { 53 54 position := 50 54 55 zeroCount := 0 55 - fmt.Printf("The dial starts by pointing at %d.\n", position) 56 + slog.Debug("The dial starts by pointing at", "position", position) 56 57 57 58 for _, rot := range d.rotations { 58 59 newPosition, passes, err := Rotate(position, rot) 59 60 if err != nil { 60 61 return 0, err 61 62 } 62 - fmt.Printf("The dial is rotated %d to point at %d.", rot, newPosition) 63 + slog.Debug("The dial is rotated", "rotation", rot, "newPosition", newPosition, "passes", passes) 63 64 if passes > 0 { 64 - if newPosition != 0 || passes > 2 { 65 - fmt.Printf(" during this rotation, it points at 0 %d times.", passes) 66 - } 67 65 zeroCount += passes 68 66 } 69 - fmt.Println() 70 - fmt.Printf("count is now: %d\n", zeroCount) 67 + slog.Debug("count updated", "zeroCount", zeroCount) 71 68 position = newPosition 72 69 } 73 70 74 - fmt.Printf("Final position: %d\n", position) 71 + slog.Debug("Final position", "position", position) 75 72 return zeroCount, nil 76 73 } 77 74
+57 -2
cmd/one/main_test.go
··· 1 1 package main 2 2 3 3 import ( 4 + "os" 4 5 "testing" 6 + 7 + "tangled.org/evan.jarrett.net/aoc2025/internal" 5 8 ) 6 9 7 10 const testInput = `L68 ··· 54 57 input string 55 58 want int 56 59 }{ 57 - {"R555", 6}, 60 + {"R555", 6}, 58 61 {"L432", 4}, 59 62 {"L555", 6}, 60 63 } ··· 84 87 position int 85 88 amount int 86 89 wantPos int 87 - wantCount int 90 + wantCount int 88 91 }{ 89 92 // Starting at 0, no crossing 90 93 {"from 0, R50", 0, 50, 50, 0}, ··· 122 125 }) 123 126 } 124 127 } 128 + 129 + // getRealInput loads the real puzzle input, caching it to a file to avoid 130 + // repeated network calls during benchmarks. 131 + func getRealInput(b *testing.B) string { 132 + b.Helper() 133 + const cacheFile = "testdata/input.txt" 134 + 135 + // Try to read from cache first 136 + if data, err := os.ReadFile(cacheFile); err == nil { 137 + return string(data) 138 + } 139 + 140 + // Fetch from AOC and cache it 141 + input, err := internal.GetInput(1) 142 + if err != nil { 143 + b.Skipf("Could not fetch real input: %v", err) 144 + } 145 + 146 + // Cache for future runs 147 + _ = os.MkdirAll("testdata", 0755) 148 + _ = os.WriteFile(cacheFile, []byte(input), 0644) 149 + 150 + return input 151 + } 152 + 153 + func BenchmarkPart1(b *testing.B) { 154 + input := getRealInput(b) 155 + 156 + d := &DayOne{} 157 + if err := d.ParseInput(input); err != nil { 158 + b.Fatalf("ParseInput failed: %v", err) 159 + } 160 + 161 + b.ResetTimer() 162 + for i := 0; i < b.N; i++ { 163 + d.Part1() 164 + } 165 + } 166 + 167 + func BenchmarkPart2(b *testing.B) { 168 + input := getRealInput(b) 169 + 170 + d := &DayOne{} 171 + if err := d.ParseInput(input); err != nil { 172 + b.Fatalf("ParseInput failed: %v", err) 173 + } 174 + 175 + b.ResetTimer() 176 + for i := 0; i < b.N; i++ { 177 + d.Part2() 178 + } 179 + }
+75
cmd/three/main.go
··· 1 + package main 2 + 3 + import ( 4 + "log/slog" 5 + "strings" 6 + 7 + "tangled.org/evan.jarrett.net/aoc2025/internal/puzzle" 8 + ) 9 + 10 + type DayThree struct { 11 + batteries []string 12 + } 13 + 14 + func (d *DayThree) ParseInput(input string) error { 15 + for line := range strings.SplitSeq(strings.TrimSpace(input), "\n") { 16 + line = strings.TrimSpace(line) 17 + if line == "" { 18 + continue 19 + } 20 + d.batteries = append(d.batteries, line) 21 + } 22 + return nil 23 + } 24 + 25 + // exactly 2 26 + func (d *DayThree) Part1() (int, error) { 27 + sum := 0 28 + for _, battery := range d.batteries { 29 + first := 0 30 + second := 0 31 + for i, ch := range battery { 32 + digit := int(ch - '0') 33 + if digit > first && i < len(battery)-1 { 34 + first = digit 35 + second = 0 // reset our second digit since it needs to come after the first 36 + } else if digit > second { 37 + second = digit 38 + } 39 + } 40 + combined := first*10 + second 41 + slog.Debug("joltage", "largest", combined, "first", first, "second", second) 42 + sum += combined 43 + } 44 + return sum, nil 45 + } 46 + 47 + // exactly 12 48 + func (d *DayThree) Part2() (int, error) { 49 + max := 12 50 + sum := 0 51 + for _, battery := range d.batteries { 52 + arr := make([]int, max) 53 + for i, ch := range battery { 54 + digit := int(ch - '0') 55 + for j := range max { 56 + if digit > arr[j] && i < len(battery)-(max-1-j) { 57 + arr[j] = digit 58 + clear(arr[j+1:]) 59 + break 60 + } 61 + } 62 + } 63 + combined := 0 64 + for _, digit := range arr { 65 + combined = combined*10 + digit 66 + } 67 + slog.Debug("joltage", "largest", combined) 68 + sum += combined 69 + } 70 + return sum, nil 71 + } 72 + 73 + func main() { 74 + puzzle.Run(3, &DayThree{}) 75 + }
+51
cmd/three/main_test.go
··· 1 + package main 2 + 3 + import ( 4 + "testing" 5 + ) 6 + 7 + const testInput = `987654321111111 8 + 811111111111119 9 + 234234234234278 10 + 818181911112111 11 + ` 12 + 13 + func TestPart1(t *testing.T) { 14 + // slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ 15 + // Level: slog.LevelDebug, 16 + // }))) 17 + d := &DayThree{} 18 + if err := d.ParseInput(testInput); err != nil { 19 + t.Fatalf("ParseInput failed: %v", err) 20 + } 21 + 22 + got, err := d.Part1() 23 + if err != nil { 24 + t.Fatalf("Part1 failed: %v", err) 25 + } 26 + 27 + want := 357 28 + if got != want { 29 + t.Errorf("Part1() = %d, want %d", got, want) 30 + } 31 + } 32 + 33 + func TestPart2(t *testing.T) { 34 + // slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ 35 + // Level: slog.LevelDebug, 36 + // }))) 37 + d := &DayThree{} 38 + if err := d.ParseInput(testInput); err != nil { 39 + t.Fatalf("ParseInput failed: %v", err) 40 + } 41 + 42 + got, err := d.Part2() 43 + if err != nil { 44 + t.Fatalf("Part2 failed: %v", err) 45 + } 46 + 47 + want := 3121910778619 // TODO: set expected value 48 + if got != want { 49 + t.Errorf("Part2() = %d, want %d", got, want) 50 + } 51 + }
+85
cmd/two/main.go
··· 1 + package main 2 + 3 + import ( 4 + "log/slog" 5 + "strconv" 6 + "strings" 7 + 8 + "tangled.org/evan.jarrett.net/aoc2025/internal/puzzle" 9 + ) 10 + 11 + type ProductId struct { 12 + firstId int 13 + lastId int 14 + } 15 + 16 + type DayTwo struct { 17 + productIds []ProductId 18 + } 19 + 20 + func (d *DayTwo) ParseInput(input string) error { 21 + for productId := range strings.SplitSeq(strings.TrimSpace(input), ",") { 22 + productId = strings.TrimSpace(productId) 23 + if productId == "" { 24 + continue 25 + } 26 + parts := strings.Split(productId, "-") 27 + first, _ := strconv.Atoi(parts[0]) 28 + last, _ := strconv.Atoi(parts[1]) 29 + 30 + product := ProductId{ 31 + firstId: first, 32 + lastId: last, 33 + } 34 + 35 + d.productIds = append(d.productIds, product) 36 + } 37 + return nil 38 + } 39 + 40 + func findPattern(productId int, j int) int { 41 + s := strconv.Itoa(productId) 42 + n := len(s) 43 + if n%j == 0 { 44 + piece := s[:n/j] 45 + if strings.Repeat(piece, j) == s { 46 + slog.Debug("found invalid ID", "id", s) 47 + return productId 48 + } 49 + } 50 + return 0 51 + } 52 + 53 + // find ids that have a repeating pattern. 55 = 5 twice, 6464 = 64 twice. 54 + func (d *DayTwo) Part1() (int, error) { 55 + sum := 0 56 + for _, product := range d.productIds { 57 + for i := product.firstId; i <= product.lastId; i++ { 58 + sum += findPattern(i, 2) 59 + } 60 + } 61 + 62 + return sum, nil 63 + } 64 + 65 + func (d *DayTwo) Part2() (int, error) { 66 + sum := 0 67 + for _, product := range d.productIds { 68 + for i := product.firstId; i <= product.lastId; i++ { 69 + s := strconv.Itoa(i) 70 + n := len(s) 71 + for j := 2; j <= n; j++ { 72 + if result := findPattern(i, j); result > 0 { 73 + sum += result 74 + break 75 + } 76 + } 77 + } 78 + } 79 + 80 + return sum, nil 81 + } 82 + 83 + func main() { 84 + puzzle.Run(2, &DayTwo{}) 85 + }
+96
cmd/two/main_test.go
··· 1 + package main 2 + 3 + import ( 4 + "os" 5 + "testing" 6 + 7 + "tangled.org/evan.jarrett.net/aoc2025/internal" 8 + ) 9 + 10 + const testInput = `11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124` 11 + 12 + func TestPart1(t *testing.T) { 13 + d := &DayTwo{} 14 + if err := d.ParseInput(testInput); err != nil { 15 + t.Fatalf("ParseInput failed: %v", err) 16 + } 17 + 18 + got, err := d.Part1() 19 + if err != nil { 20 + t.Fatalf("Part1 failed: %v", err) 21 + } 22 + 23 + want := 1227775554 24 + if got != want { 25 + t.Errorf("Part1() = %d, want %d", got, want) 26 + } 27 + } 28 + 29 + func TestPart2(t *testing.T) { 30 + d := &DayTwo{} 31 + if err := d.ParseInput(testInput); err != nil { 32 + t.Fatalf("ParseInput failed: %v", err) 33 + } 34 + 35 + got, err := d.Part2() 36 + if err != nil { 37 + t.Fatalf("Part2 failed: %v", err) 38 + } 39 + 40 + want := 4174379265 41 + if got != want { 42 + t.Errorf("Part2() = %d, want %d", got, want) 43 + } 44 + } 45 + 46 + // getRealInput loads the real puzzle input, caching it to a file to avoid 47 + // repeated network calls during benchmarks. 48 + func getRealInput(b *testing.B) string { 49 + b.Helper() 50 + const cacheFile = "testdata/input.txt" 51 + 52 + // Try to read from cache first 53 + if data, err := os.ReadFile(cacheFile); err == nil { 54 + return string(data) 55 + } 56 + 57 + // Fetch from AOC and cache it 58 + input, err := internal.GetInput(2) 59 + if err != nil { 60 + b.Skipf("Could not fetch real input: %v", err) 61 + } 62 + 63 + // Cache for future runs 64 + _ = os.MkdirAll("testdata", 0755) 65 + _ = os.WriteFile(cacheFile, []byte(input), 0644) 66 + 67 + return input 68 + } 69 + 70 + func BenchmarkPart1(b *testing.B) { 71 + input := getRealInput(b) 72 + 73 + d := &DayTwo{} 74 + if err := d.ParseInput(input); err != nil { 75 + b.Fatalf("ParseInput failed: %v", err) 76 + } 77 + 78 + b.ResetTimer() 79 + for i := 0; i < b.N; i++ { 80 + d.Part1() 81 + } 82 + } 83 + 84 + func BenchmarkPart2(b *testing.B) { 85 + input := getRealInput(b) 86 + 87 + d := &DayTwo{} 88 + if err := d.ParseInput(input); err != nil { 89 + b.Fatalf("ParseInput failed: %v", err) 90 + } 91 + 92 + b.ResetTimer() 93 + for i := 0; i < b.N; i++ { 94 + d.Part2() 95 + } 96 + }
+53 -20
newday.sh
··· 2 2 3 3 set -e 4 4 5 - if [ -z "$1" ]; then 6 - echo "Usage: $0 <day>" 7 - echo "Example: $0 5" 5 + # Determine current day based on EST timezone (puzzles unlock at midnight EST) 6 + EST_DAY=$(TZ="America/New_York" date +%-d) 7 + EST_MONTH=$(TZ="America/New_York" date +%-m) 8 + 9 + if [ "$EST_MONTH" -ne 12 ]; then 10 + echo "Error: Advent of Code runs in December" 8 11 exit 1 9 12 fi 10 13 11 - DAY=$1 12 - 13 - if ! [[ "$DAY" =~ ^[0-9]+$ ]] || [ "$DAY" -lt 1 ] || [ "$DAY" -gt 12 ]; then 14 - echo "Error: Day must be a number between 1 and 12" 14 + if [ "$EST_DAY" -gt 12 ]; then 15 + echo "Error: This event only has 12 days (current EST day: $EST_DAY)" 15 16 exit 1 16 17 fi 17 18 18 - # Check if the puzzle is available (unlocks at midnight EST / 11pm CST) 19 - EST_DAY=$(TZ="America/New_York" date +%-d) 20 - EST_MONTH=$(TZ="America/New_York" date +%-m) 21 - EST_YEAR=$(TZ="America/New_York" date +%Y) 22 - 23 - if [ "$EST_YEAR" -eq 2025 ] && [ "$EST_MONTH" -eq 12 ]; then 24 - if [ "$DAY" -gt "$EST_DAY" ]; then 25 - echo "Error: Day $DAY puzzle not yet available." 26 - echo "Puzzles unlock at midnight EST (11pm CST)." 27 - echo "Current EST date: December $EST_DAY, 2025" 28 - exit 1 29 - fi 30 - fi 19 + DAY=$EST_DAY 31 20 32 21 declare -a WORDS=("" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine" "ten" 33 22 "eleven" "twelve") ··· 87 76 puzzle.Run($DAY, &Day${DAY_WORD_CAP}{}) 88 77 } 89 78 EOF 79 + cat > "$CMD_DIR/main_test.go" << EOF 80 + package main 81 + 82 + import ( 83 + "testing" 84 + ) 85 + 86 + const testInput = \`\` 87 + 88 + func TestPart1(t *testing.T) { 89 + d := &Day${DAY_WORD_CAP}{} 90 + if err := d.ParseInput(testInput); err != nil { 91 + t.Fatalf("ParseInput failed: %v", err) 92 + } 93 + 94 + got, err := d.Part1() 95 + if err != nil { 96 + t.Fatalf("Part1 failed: %v", err) 97 + } 98 + 99 + want := 0 // TODO: set expected value 100 + if got != want { 101 + t.Errorf("Part1() = %d, want %d", got, want) 102 + } 103 + } 104 + 105 + func TestPart2(t *testing.T) { 106 + d := &Day${DAY_WORD_CAP}{} 107 + if err := d.ParseInput(testInput); err != nil { 108 + t.Fatalf("ParseInput failed: %v", err) 109 + } 110 + 111 + got, err := d.Part2() 112 + if err != nil { 113 + t.Fatalf("Part2 failed: %v", err) 114 + } 115 + 116 + want := 0 // TODO: set expected value 117 + if got != want { 118 + t.Errorf("Part2() = %d, want %d", got, want) 119 + } 120 + } 121 + EOF 90 122 echo "Created $CMD_DIR/main.go" 123 + echo "Created $CMD_DIR/main_test.go" 91 124 fi