This commit is contained in:
onyx-and-iris 2023-12-14 03:44:07 +00:00
parent 0f254a243e
commit 8b61fa2011
8 changed files with 284 additions and 0 deletions

10
day-12/go.mod Normal file
View File

@ -0,0 +1,10 @@
module github.com/onyx-and-iris/aoc2023/day-12
go 1.21.5
require github.com/sirupsen/logrus v1.9.3
require (
github.com/stretchr/testify v1.8.4 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)

17
day-12/go.sum Normal file
View File

@ -0,0 +1,17 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

10
day-12/makefile Normal file
View File

@ -0,0 +1,10 @@
TEST="test.txt"
INPUT="input.txt"
test:
cat $(TEST) | go run .
run:
cat $(INPUT) | go run .
all: test

113
day-12/one.go Normal file
View File

@ -0,0 +1,113 @@
package main
import (
"fmt"
log "github.com/sirupsen/logrus"
)
const (
OPERATIONAL = iota
DAMAGED
UNKNOWN
)
var records []record
type record struct {
format []int
springs []spring
}
func newRecord() record {
return record{}
}
// raw returns the state of all springs for this record as int array
func (r record) raw() []int {
vals := make([]int, len(r.springs))
for j, spring := range r.springs {
vals[j] = spring.state
}
return vals
}
type spring struct {
state int
}
func newSpring(state int) spring {
return spring{state: state}
}
// tracker keeps track of results for function calls
type tracker struct {
checked map[string]int
}
func newTracker() tracker {
return tracker{checked: make(map[string]int)}
}
func (t tracker) add(key string, res int) {
t.checked[key] = res
}
func validate(springs, format []int) bool {
return !contains(springs[:format[0]], OPERATIONAL) && (format[0] == len(springs) || springs[format[0]] != DAMAGED)
}
// count returns the number of arrangements possible for a set of springs
func count(springs, format []int) int {
if len(springs) == 0 {
if len(format) == 0 {
return 1
}
return 0
}
if len(format) == 0 {
if contains(springs, DAMAGED) {
return 0
}
return 1
}
identifier := fmt.Sprintf("%v%v", format, springs)
if res, ok := fnTracker.checked[fmt.Sprintf("%v%v", format, springs)]; ok {
log.Debug("returning cached value for ", identifier)
return res
}
result := 0
if springs[0] == OPERATIONAL || springs[0] == UNKNOWN {
result += count(springs[1:], format)
}
if springs[0] == DAMAGED || springs[0] == UNKNOWN {
if format[0] < len(springs) && validate(springs, format) {
result += count(springs[format[0]+1:], format[1:])
} else if format[0] == len(springs) && validate(springs, format) {
result += count(springs[format[0]:], format[1:])
}
}
fnTracker.add(identifier, result)
return result
}
var fnTracker tracker
// one returns the sum of all arrangement counts
func one(lines []string) int {
parselines(lines)
fnTracker = newTracker()
sum := 0
for _, record := range records {
raw := record.raw()
sum += count(raw, record.format)
}
return sum
}

5
day-12/run.sh Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
INPUT="input.txt"
cat $INPUT | go run .

22
day-12/solution.go Normal file
View File

@ -0,0 +1,22 @@
package main
import (
"fmt"
log "github.com/sirupsen/logrus"
)
func init() {
log.SetLevel(log.InfoLevel)
}
func main() {
lines := readlines()
ans := one(lines)
fmt.Printf("solution one: %d\n", ans)
ans = two(lines)
fmt.Printf("solution two: %d\n", ans)
}

36
day-12/two.go Normal file
View File

@ -0,0 +1,36 @@
package main
// multiplyBy unfolds all records by a given factor
func multiplyRawBy(factor int, original []int) []int {
multiplied := []int{}
for i := 0; i < factor; i++ {
multiplied = append(multiplied, original...)
if i < factor-1 {
multiplied = append(multiplied, UNKNOWN)
}
}
return multiplied
}
// multiplyBy unfolds all records by a given factor
func multiplyFormatBy(factor int, original []int) []int {
multiplied := []int{}
for i := 0; i < factor; i++ {
multiplied = append(multiplied, original...)
}
return multiplied
}
// two returns the sum of all unfolded arrangement counts
func two(lines []string) int {
fnTracker = newTracker()
sum := 0
for _, record := range records {
raw := record.raw()
sum += count(multiplyRawBy(5, raw), multiplyFormatBy(5, record.format))
}
return sum
}

71
day-12/util.go Normal file
View File

@ -0,0 +1,71 @@
package main
import (
"bufio"
"log"
"os"
"strconv"
"strings"
"unicode"
)
// readlines reads lines from stdin.
// returns input as an array of strings
func readlines() []string {
lines := []string{}
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
return lines
}
func parselines(lines []string) {
f := func(c rune) bool {
return !unicode.IsDigit(c)
}
records = make([]record, len(lines))
for i, line := range lines {
record := newRecord()
for _, r := range line {
switch r {
case '.':
record.springs = append(record.springs, newSpring(OPERATIONAL))
case '#':
record.springs = append(record.springs, newSpring(DAMAGED))
case '?':
record.springs = append(record.springs, newSpring(UNKNOWN))
}
}
record.format = convertToInts(strings.FieldsFunc(line, f))
records[i] = record
}
}
// convertToInts converts a string representing ints to an array of ints
func convertToInts(data []string) []int {
nums := []int{}
for _, elem := range data {
n, _ := strconv.Atoi(elem)
nums = append(nums, n)
}
return nums
}
// contains returns true if a slice of elements contains a given element
func contains[T comparable](elems []T, v T) bool {
for _, s := range elems {
if v == s {
return true
}
}
return false
}