From 736783e1b298b007e925b8521baa4079922754af Mon Sep 17 00:00:00 2001 From: Dmytro Spivakov Date: Sat, 12 Oct 2024 02:57:41 +0300 Subject: [PATCH] Day 5: part 1 - refactor and simplify --- day05/day05.go | 129 +++++++++++++++++--------------------------- day05/day05_test.go | 22 +++++++- day05/test_input11 | 1 + 3 files changed, 72 insertions(+), 80 deletions(-) diff --git a/day05/day05.go b/day05/day05.go index ac8f2ac..c08b921 100644 --- a/day05/day05.go +++ b/day05/day05.go @@ -132,110 +132,81 @@ func Solution1(filepath string) int { log.Fatalf("Failed to open the input file %v\n", filepath) } - processingOrder := []string{ - "soil", - "fertilizer", - "water", - "light", - "temperature", - "humidity", - "location", + // read seeds, handle the first two lines as an edge-case outside the main reading loop + var mappedInputs []int + scanner := bufio.NewScanner(file) + scanner.Scan() + seedRegex := regexp.MustCompile(`\d+`) + for _, seed := range seedRegex.FindAllString(scanner.Text(), -1) { + mappedInputs = append(mappedInputs, parseInt(seed)) } - - processStepToHeader := map[string]string{ - "soil": "seed-to-soil map:", - "fertilizer": "soil-to-fertilizer map:", - "water": "fertilizer-to-water map:", - "light": "water-to-light map:", - "temperature": "light-to-temperature map:", - "humidity": "temperature-to-humidity map:", - "location": "humidity-to-location map:", + scanner.Scan() + if scanner.Err() != nil { + log.Fatalf("Error during input file read: %v\n", scanner.Err().Error()) } - var seeds []int - processStepToRange := make(map[string][]Range) - rangeRegex := regexp.MustCompile(`(\d+)\s+(\d+)\s+(\d+)`) - seedRegex := regexp.MustCompile(`\d+`) - currentContext := "" - scanner := bufio.NewScanner(file) + var currentMappingSection []string for scanner.Scan() { currentLine := scanner.Text() - - // sections are separated by a blank line - if len(currentLine) == 0 { - currentContext = "" + // sections are ordered -> don't need to care about parsing headers + if strings.Contains(currentLine, "map:") { continue } - if strings.Contains(currentLine, "seeds:") { - for _, seed := range seedRegex.FindAllString(currentLine, -1) { - seeds = append(seeds, parseInt(seed)) - } - continue - } + if len(currentLine) == 0 { + mappedInputs = applySectionMapping(currentMappingSection, mappedInputs) + currentMappingSection = nil - contextChanged := false - for step, header := range processStepToHeader { - if strings.Contains(currentLine, header) { - currentContext = step - contextChanged = true - break - } - } - if contextChanged { continue } - rangeNumbers := rangeRegex.FindStringSubmatch(currentLine) - if rangeNumCount := len(rangeNumbers); rangeNumCount > 0 && rangeNumCount != 4 { - log.Fatalf("Failed to parse range row %v\n", rangeNumbers) + currentMappingSection = append(currentMappingSection, currentLine) + } + // last section edge-case, there's no trailing empty line to trigger mapping + mappedInputs = applySectionMapping(currentMappingSection, mappedInputs) + + result := mappedInputs[0] + for _, mappedInput := range mappedInputs { + if mappedInput < result { + result = mappedInput } + } + return result +} - processStepToRange[currentContext] = append( - processStepToRange[currentContext], +func applySectionMapping(sectionLines []string, inputs []int) (mappedInputs []int) { + rangeRegex := regexp.MustCompile(`(\d+)\s+(\d+)\s+(\d+)`) + + var mappingRanges []Range + for _, sectionLine := range sectionLines { + matches := rangeRegex.FindStringSubmatch(sectionLine) + + mappingRanges = append( + mappingRanges, Range{ - destStart: parseInt(rangeNumbers[1]), - srcStart: parseInt(rangeNumbers[2]), - length: parseInt(rangeNumbers[3]), + destStart: parseInt(matches[1]), + srcStart: parseInt(matches[2]), + length: parseInt(matches[3]), }, ) } - if scanner.Err() != nil { - log.Fatalf("Error during input file read: %v\n", scanner.Err().Error()) - } - - mappingInput := seeds - var outputs []int - for _, step := range processingOrder { - outputs = nil - - for _, input := range mappingInput { - found := false - for _, rangeStr := range processStepToRange[step] { - if mappedValue := rangeStr.findDest(input); mappedValue >= 0 { - outputs = append(outputs, mappedValue) - found = true - break - } - } - if !found { - outputs = append(outputs, input) + for _, input := range inputs { + found := false + for _, rangeStr := range mappingRanges { + if mappedInput := rangeStr.findDest(input); mappedInput >= 0 { + mappedInputs = append(mappedInputs, mappedInput) + found = true + break } } - mappingInput = make([]int, len(outputs)) - copy(mappingInput, outputs) - } - - result := outputs[0] - for _, output := range outputs { - if output < result { - result = output + if !found { + mappedInputs = append(mappedInputs, input) } } - return result + return mappedInputs } func parseInt(numStr string) int { diff --git a/day05/day05_test.go b/day05/day05_test.go index 16d3cd2..cb7a753 100644 --- a/day05/day05_test.go +++ b/day05/day05_test.go @@ -7,7 +7,7 @@ import ( func TestSolution1(t *testing.T) { cases := map[string]int{ - "test_input11": 35, + "test_input11": 3, } for inputFile, expectedResult := range cases { @@ -19,3 +19,23 @@ func TestSolution1(t *testing.T) { } } } + +func TestRangeFindDest(t *testing.T) { + rangeStr := Range{srcStart: 10, destStart: 20, length: 10} + cases := map[int]int{ + 10: 20, + 20: 30, + 15: 25, + 9: -1, + 31: -1, + } + + for srcStart, expectedDest := range cases { + result := rangeStr.findDest(srcStart) + if result != expectedDest { + t.Fatalf("Range.findDest() = %d, expecting %d\n", result, expectedDest) + } else { + fmt.Printf("Range.findDest() = %d, OK\n", result) + } + } +} diff --git a/day05/test_input11 b/day05/test_input11 index f756727..10555e7 100644 --- a/day05/test_input11 +++ b/day05/test_input11 @@ -31,3 +31,4 @@ temperature-to-humidity map: humidity-to-location map: 60 56 37 56 93 4 +3 35 1