generated from devries/aoc_template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
410 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package day05p1 | ||
|
||
import ( | ||
"io" | ||
"regexp" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
|
||
"aoc/utils" | ||
) | ||
|
||
func Solve(r io.Reader) any { | ||
lines := utils.ReadLines(r) | ||
|
||
// Get seeds from first line | ||
parts := strings.Fields(lines[0]) | ||
|
||
values := make([]int64, len(parts)-1) | ||
|
||
for i := 0; i < len(values); i++ { | ||
v, err := strconv.ParseInt(parts[i+1], 10, 64) | ||
utils.Check(err, "Unable to convert %s to int64", parts[i+1]) | ||
|
||
values[i] = v | ||
} | ||
|
||
// For remaining lines create conversions and convert values | ||
conversions := []Conversion{} | ||
|
||
// three positive integers | ||
re := regexp.MustCompile(`\d+\s+\d+\s+\d+`) | ||
|
||
for _, ln := range lines[2:] { | ||
switch { | ||
case re.MatchString(ln): | ||
parts = strings.Fields(ln) | ||
|
||
components := make([]int64, len(parts)) | ||
|
||
for i, s := range parts { | ||
var err error | ||
components[i], err = strconv.ParseInt(s, 10, 64) | ||
utils.Check(err, "Unable to convert %s to int64", s) | ||
} | ||
c := Conversion{Start: components[1], End: components[1] + components[2], Delta: components[0] - components[1]} | ||
conversions = append(conversions, c) | ||
|
||
case ln == "": | ||
sort.Slice(conversions, func(i, j int) bool { return conversions[i].Start < conversions[j].Start }) | ||
|
||
for i, v := range values { | ||
delta := getDelta(conversions, v) | ||
values[i] = v + delta | ||
} | ||
|
||
conversions = []Conversion{} | ||
} | ||
} | ||
|
||
if len(conversions) > 0 { | ||
sort.Slice(conversions, func(i, j int) bool { return conversions[i].Start < conversions[j].Start }) | ||
|
||
for i, v := range values { | ||
delta := getDelta(conversions, v) | ||
values[i] = v + delta | ||
} | ||
} | ||
|
||
min := values[0] | ||
|
||
for _, v := range values[1:] { | ||
if v < min { | ||
min = v | ||
} | ||
} | ||
|
||
return min | ||
} | ||
|
||
type Conversion struct { | ||
Start int64 | ||
End int64 | ||
Delta int64 | ||
} | ||
|
||
// Get the delta value by performing a binary search over sorted array of conversions | ||
func getDelta(arr []Conversion, val int64) int64 { | ||
low, high := 0, len(arr)-1 | ||
|
||
for low <= high { | ||
mid := low + (high-low)/2 | ||
|
||
if arr[mid].Start <= val && arr[mid].End > val { | ||
return arr[mid].Delta | ||
} | ||
|
||
if arr[mid].Start > val { | ||
high = mid - 1 | ||
} else if arr[mid].End <= val { | ||
low = mid + 1 | ||
} | ||
} | ||
|
||
return 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package day05p1 | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"aoc/utils" | ||
) | ||
|
||
var testInput = `seeds: 79 14 55 13 | ||
seed-to-soil map: | ||
50 98 2 | ||
52 50 48 | ||
soil-to-fertilizer map: | ||
0 15 37 | ||
37 52 2 | ||
39 0 15 | ||
fertilizer-to-water map: | ||
49 53 8 | ||
0 11 42 | ||
42 0 7 | ||
57 7 4 | ||
water-to-light map: | ||
88 18 7 | ||
18 25 70 | ||
light-to-temperature map: | ||
45 77 23 | ||
81 45 19 | ||
68 64 13 | ||
temperature-to-humidity map: | ||
0 69 1 | ||
1 0 69 | ||
humidity-to-location map: | ||
60 56 37 | ||
56 93 4` | ||
|
||
func TestSolve(t *testing.T) { | ||
tests := []struct { | ||
input string | ||
answer int64 | ||
}{ | ||
{testInput, 35}, | ||
} | ||
|
||
if testing.Verbose() { | ||
utils.Verbose = true | ||
} | ||
|
||
for _, test := range tests { | ||
r := strings.NewReader(test.input) | ||
|
||
result := Solve(r).(int64) | ||
|
||
if result != test.answer { | ||
t.Errorf("Expected %d, got %d", test.answer, result) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
package day05p2 | ||
|
||
import ( | ||
"io" | ||
"regexp" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
|
||
"aoc/utils" | ||
) | ||
|
||
func Solve(r io.Reader) any { | ||
lines := utils.ReadLines(r) | ||
|
||
// Get seeds from first line | ||
parts := strings.Fields(lines[0]) | ||
|
||
values := make([]int64, len(parts)-1) | ||
|
||
for i := 0; i < len(values); i++ { | ||
v, err := strconv.ParseInt(parts[i+1], 10, 64) | ||
utils.Check(err, "Unable to convert %s to int64", parts[i+1]) | ||
|
||
values[i] = v | ||
} | ||
|
||
// For remaining lines create conversions and convert values | ||
conversions := []Conversion{} | ||
|
||
// three positive integers | ||
re := regexp.MustCompile(`\d+\s+\d+\s+\d+`) | ||
|
||
for _, ln := range lines[2:] { | ||
switch { | ||
case re.MatchString(ln): | ||
// Build up the conversion array | ||
parts = strings.Fields(ln) | ||
|
||
components := make([]int64, len(parts)) | ||
|
||
for i, s := range parts { | ||
var err error | ||
components[i], err = strconv.ParseInt(s, 10, 64) | ||
utils.Check(err, "Unable to convert %s to int64", s) | ||
} | ||
c := Conversion{Start: components[1], End: components[1] + components[2], Delta: components[0] - components[1]} | ||
conversions = append(conversions, c) | ||
|
||
case ln == "": | ||
// Conversion array complete, calculate conversions | ||
sort.Slice(conversions, func(i, j int) bool { return conversions[i].Start < conversions[j].Start }) | ||
newvalues := []int64{} | ||
|
||
for i := 0; i < len(values); i += 2 { | ||
// For each input value and range we convert to a new value and range. | ||
// If the range is longer than the valid interval of the conversion we split up the range into two intervals | ||
// and then convert the second value and range as well... if that one is longer than the valid interval we repeat | ||
start, length := values[i], values[i+1] | ||
|
||
for { | ||
delta, interval := getDeltaInterval(conversions, start) | ||
newvalues = append(newvalues, start+delta) | ||
if length <= interval || interval == 0 { // 0 interval means the rest of the numbers follow that delta | ||
// The length of the input value range is less than the conversion interval | ||
newvalues = append(newvalues, length) | ||
break | ||
} else { | ||
// The length of the input value range is greater than the remaining | ||
// conversion interval, we need to split the solution up into multiple | ||
// ranges | ||
newvalues = append(newvalues, interval) | ||
start = start + interval | ||
length = length - interval | ||
} | ||
} | ||
} | ||
values = newvalues | ||
|
||
conversions = []Conversion{} | ||
} | ||
} | ||
|
||
if len(conversions) > 0 { | ||
// Do last conversion | ||
sort.Slice(conversions, func(i, j int) bool { return conversions[i].Start < conversions[j].Start }) | ||
newvalues := []int64{} | ||
|
||
for i := 0; i < len(values); i += 2 { | ||
// For each input value and range we convert to a new value and range. | ||
// If the range is longer than the valid interval of the conversion we split up the range into two intervals | ||
// and then convert the second value and range as well... if that one is longer than the valid interval we repeat | ||
start, length := values[i], values[i+1] | ||
|
||
for { | ||
delta, interval := getDeltaInterval(conversions, start) | ||
newvalues = append(newvalues, start+delta) | ||
if length <= interval || interval == 0 { // 0 interval means the rest of the numbers follow that delta | ||
// The length of the input value range is less than the conversion interval | ||
newvalues = append(newvalues, length) | ||
break | ||
} else { | ||
// The length of the input value range is greater than the remaining | ||
// conversion interval, we need to split the solution up into multiple | ||
// ranges | ||
newvalues = append(newvalues, interval) | ||
start = start + interval | ||
length = length - interval | ||
} | ||
} | ||
} | ||
|
||
values = newvalues | ||
} | ||
|
||
min := values[0] | ||
|
||
for i := 2; i < len(values); i += 2 { | ||
if values[i] < min { | ||
min = values[i] | ||
} | ||
} | ||
|
||
return min | ||
} | ||
|
||
type Conversion struct { | ||
Start int64 | ||
End int64 | ||
Delta int64 | ||
} | ||
|
||
// Get the delta value by performing a binary search over sorted array of conversions | ||
// also get the range of subsequent numbers for which it is valid | ||
func getDeltaInterval(arr []Conversion, val int64) (int64, int64) { | ||
low, high := 0, len(arr)-1 | ||
var mid int | ||
|
||
for low <= high { | ||
mid = low + (high-low)/2 | ||
|
||
if arr[mid].Start <= val && arr[mid].End > val { | ||
return arr[mid].Delta, arr[mid].End - val | ||
} | ||
|
||
if arr[mid].Start > val { | ||
high = mid - 1 | ||
} else if arr[mid].End <= val { | ||
low = mid + 1 | ||
} | ||
} | ||
|
||
if arr[mid].Start > val { | ||
return 0, arr[mid].Start - val | ||
} else if mid < len(arr)-1 { | ||
return 0, arr[mid+1].Start - val | ||
} else { | ||
return 0, 0 | ||
} | ||
} |
Oops, something went wrong.