From 2d5190c04463ae461813c34d1fb907c0caab0402 Mon Sep 17 00:00:00 2001 From: Mindaugas Bernotas Date: Sat, 6 Dec 2025 16:50:12 +0200 Subject: [PATCH] Solve day 5 --- day5/example.txt | 11 +++++ day5/main.go | 123 ++++++++++++++++++++++++++++++++++++++++++++++ day5/main_test.go | 68 +++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 day5/example.txt create mode 100644 day5/main.go create mode 100644 day5/main_test.go diff --git a/day5/example.txt b/day5/example.txt new file mode 100644 index 0000000..2e9078d --- /dev/null +++ b/day5/example.txt @@ -0,0 +1,11 @@ +3-5 +10-14 +16-20 +12-18 + +1 +5 +8 +11 +17 +32 diff --git a/day5/main.go b/day5/main.go new file mode 100644 index 0000000..58bf932 --- /dev/null +++ b/day5/main.go @@ -0,0 +1,123 @@ +package main + +import ( + "bufio" + "cmp" + "fmt" + "io" + "log" + "os" + "slices" + "strconv" + "strings" +) + +func main() { + f := os.Stdin + if len(os.Args) > 1 && os.Args[1] != "-" { + var err error + f, err = os.Open(os.Args[1]) + if err != nil { + log.Fatal(err) + } + defer f.Close() + } + + fresh, available, err := readIngredients(f) + if err != nil { + log.Fatal(err) + } + + var availableFresh int + for _, id := range available { + for _, r := range fresh { + if r.Includes(id) { + availableFresh++ + break + } + } + } + + var freshIds int + compactFresh := compactedRanges(fresh) + for _, r := range compactFresh { + freshIds += r.to - r.from + 1 + } + + fmt.Println("available fresh:", availableFresh) + fmt.Println("total fresh:", freshIds) +} + +type idsRange struct { + from, to int +} + +func (r idsRange) Includes(n int) bool { + return r.from <= n && n <= r.to +} + +func readIngredients(r io.Reader) (fresh []idsRange, available []int, err error) { + seenEmptyLine := false + sc := bufio.NewScanner(r) + for sc.Scan() { + if sc.Text() == "" { + seenEmptyLine = true + continue + } + if seenEmptyLine { + id, err := strconv.ParseUint(sc.Text(), 10, 0) + if err != nil { + return nil, nil, err + } + available = append(available, int(id)) + } else { + r, err := parseRange(sc.Text()) + if err != nil { + return nil, nil, err + } + fresh = append(fresh, r) + } + } + return fresh, available, nil +} + +func parseRange(s string) (idsRange, error) { + before, after, ok := strings.Cut(s, "-") + if !ok { + return idsRange{}, fmt.Errorf("range %q: missing hyphen", s) + } + from, err := strconv.ParseUint(before, 10, 0) + if err != nil { + return idsRange{}, fmt.Errorf("range from: %w", err) + } + to, err := strconv.ParseUint(after, 10, 0) + if err != nil { + return idsRange{}, fmt.Errorf("range to: %w", err) + } + return idsRange{ + from: int(from), + to: int(to), + }, nil +} + +func compactedRanges(ranges []idsRange) []idsRange { + ranges = slices.Clone(ranges) + + slices.SortFunc(ranges, func(a, b idsRange) int { + if x := cmp.Compare(a.from, b.from); x != 0 { + return x + } + return cmp.Compare(a.to, b.to) + }) + + i := 0 + for j := 1; j < len(ranges); j++ { + if !ranges[i].Includes(ranges[j].from) { + i++ + ranges[i] = ranges[j] + continue + } + ranges[i].to = max(ranges[i].to, ranges[j].to) + } + return ranges[:i+1] +} diff --git a/day5/main_test.go b/day5/main_test.go new file mode 100644 index 0000000..1e2a657 --- /dev/null +++ b/day5/main_test.go @@ -0,0 +1,68 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_compactedRanges(t *testing.T) { + tests := []struct { + name string // description of this test case + ranges []idsRange + want []idsRange + }{ + { + name: "single", + ranges: []idsRange{ + {0, 10}, + }, + want: []idsRange{ + {0, 10}, + }, + }, + { + name: "identical", + ranges: []idsRange{ + {0, 10}, + {0, 10}, + {0, 10}, + {20, 30}, + {20, 30}, + }, + want: []idsRange{ + {0, 10}, + {20, 30}, + }, + }, + { + name: "simple", + ranges: []idsRange{ + {0, 10}, + {2, 11}, + {20, 30}, + }, + want: []idsRange{ + {0, 11}, + {20, 30}, + }, + }, + { + name: "swallows", + ranges: []idsRange{ + {0, 10}, + {2, 8}, + {20, 30}, + }, + want: []idsRange{ + {0, 10}, + {20, 30}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, compactedRanges(tt.ranges)) + }) + } +}