diff --git a/2025/cmd/root.go b/2025/cmd/root.go index ea61025..308d359 100644 --- a/2025/cmd/root.go +++ b/2025/cmd/root.go @@ -10,10 +10,12 @@ import ( "github.com/spf13/cobra" "cpatino.com/advent-of-code/2025/day01" + "cpatino.com/advent-of-code/2025/day03" ) var days = map[int]func(string) (any, any){ 1: day01.Run, + 3: day03.Run, } var rootCmd = &cobra.Command{ diff --git a/2025/day02/day02.go b/2025/day02/day02.go new file mode 100755 index 0000000..67e2781 --- /dev/null +++ b/2025/day02/day02.go @@ -0,0 +1,123 @@ +package day02 + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func Part1(input string) int { + total := 0 + input = strings.TrimSpace(input) + if input == "" { + return 0 + } + + for _, rng := range strings.Split(input, ",") { + rng = strings.TrimSpace(rng) + if rng == "" { + continue + } + + parts := strings.Split(rng, "-") + if len(parts) != 2 { + continue + } + + start, end := 0, 0 + _, err1 := fmt.Sscanf(parts[0], "%d", &start) + _, err2 := fmt.Sscanf(parts[1], "%d", &end) + if err1 != nil || err2 != nil { + continue + } + + for id := start; id <= end; id++ { + idStr := fmt.Sprintf("%d", id) + for k := 1; k <= len(idStr)/2; k++ { + if len(idStr) == 2*k && idStr[:k] == idStr[k:] { + total += id + break + } + } + + } + } + + return total +} + +func Part2(input string) int { + total := 0 + input = strings.TrimSpace(input) + if input == "" { + return 0 + } + + for _, rng := range strings.Split(input, ",") { + rng = strings.TrimSpace(rng) + if rng == "" { + continue + } + + parts := strings.Split(rng, "-") + if len(parts) != 2 { + continue + } + + start, end := 0, 0 + _, err1 := fmt.Sscanf(parts[0], "%d", &start) + _, err2 := fmt.Sscanf(parts[1], "%d", &end) + if err1 != nil || err2 != nil { + continue + } + + for id := start; id <= end; id++ { + idStr := fmt.Sprintf("%d", id) + for k := 1; k <= len(idStr)/2; k++ { + if len(idStr)%k != 0 { + continue + } + + chunk := idStr[:k] + valid := true + for i := k; i < len(idStr); i += k { + if idStr[i:i+k] != chunk { + valid = false + break + } + } + + if valid && len(idStr)/k >= 2 { + total += id + break + } + } + } + } + return total +} + +func Run(filename string) (any, any) { + file, err := os.Open(filename) + if err != nil { + panic(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + var lines []string + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + input := strings.Join(lines, "") + + if err := scanner.Err(); err != nil { + panic(err) + } + + part1 := Part1(input) + part2 := Part2(input) + + return part1, part2 +} diff --git a/2025/day02/day02_test.go b/2025/day02/day02_test.go new file mode 100755 index 0000000..7c9e6e3 --- /dev/null +++ b/2025/day02/day02_test.go @@ -0,0 +1,34 @@ +package day02 + +import ( + "os" + "testing" +) + +type TestCase struct { + FileName string + Part1 any + Part2 any +} + +func TestDay02(t *testing.T) { + tests := []TestCase{ + {"test.txt", 1227775554, 4174379265}, + {"input.txt", 40398804950, 65794984339}, + } + + for _, test := range tests { + if _, err := os.Stat(test.FileName); os.IsNotExist(err) { + t.Fatalf("test file does not exist: %v", test.FileName) + } + + part1, part2 := Run(test.FileName) + if test.Part1 != nil && part1 != test.Part1 { + t.Fatalf("%s: unexpected part1:\nwant:\t%v\ngot:\t%v", test.FileName, test.Part1, part1) + } + + if test.Part2 != nil && part2 != test.Part2 { + t.Fatalf("%s: unexpected part2:\nwant:\t%v\ngot:\t%v", test.FileName, test.Part2, part2) + } + } +} diff --git a/2025/day02/test.txt b/2025/day02/test.txt new file mode 100755 index 0000000..b5e6998 --- /dev/null +++ b/2025/day02/test.txt @@ -0,0 +1,3 @@ +11-22,95-115,998-1012,1188511880-1188511890,222220-222224, +1698522-1698528,446443-446449,38593856-38593862,565653-565659, +824824821-824824827,2121212118-2121212124