Day 5 done
I'm a fucking idiot and made it harder for myself than it had to be Go's sorting function already takes care of the actual sort, I just had to tell it how two elements compare according to the rules
This commit is contained in:
parent
713be4de8e
commit
7d63141f07
6 changed files with 1782 additions and 0 deletions
194
day5/.null-ls_704051_main.go
Normal file
194
day5/.null-ls_704051_main.go
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.mstar.dev/mstar/aoc24/util"
|
||||||
|
"git.mstar.dev/mstar/goutils/other"
|
||||||
|
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gameplan:
|
||||||
|
1. Einlesen als roh linien
|
||||||
|
2. Linien bis \n\n sind Regeln
|
||||||
|
3. Jede Linie danach muss den Regeln folgen
|
||||||
|
4. Regeln in zwei maps speichern
|
||||||
|
- 1. Map zuvor -> []danach: Jede nr hat eine Liste an nr, welche nach key kommen müssen
|
||||||
|
- 2. Map danach -> []zuvor: Jede nr hat eine liste an nr, welche vor key kommen müssen
|
||||||
|
5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type RulePair struct {
|
||||||
|
Left, Right int
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRule(rule string) (int, int) {
|
||||||
|
tmp := strings.Split(rule, "|")
|
||||||
|
return other.Must(strconv.Atoi(tmp[0])), other.Must(strconv.Atoi(tmp[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUpdate(line string) []int {
|
||||||
|
tmp := strings.Split(line, ",")
|
||||||
|
return sliceutils.Map(tmp, func(t string) int { return other.Must(strconv.Atoi(t)) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUpdateWithRules(rulesAfter, rulesBefore map[int][]int, update []int) bool {
|
||||||
|
for i, num := range update {
|
||||||
|
for _, elem := range update[:i] {
|
||||||
|
if sliceutils.Contains(rulesAfter[num], elem) {
|
||||||
|
// fmt.Printf("%v failed rule %d: %v\n", update, num, rulesAfter[num])
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, elem := range update[i:] {
|
||||||
|
if sliceutils.Contains(rulesBefore[num], elem) {
|
||||||
|
// fmt.Printf("%v failed rule %d: %v\n", update, num, rulesBefore[num])
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCenterVal(elems []int) int {
|
||||||
|
return elems[len(elems)/2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortByRules(nrsIn []int, rules []RulePair) []int {
|
||||||
|
nrsToUse := slices.Clone(nrsIn)
|
||||||
|
_ = nrsToUse
|
||||||
|
nrsOut := []int{}
|
||||||
|
sortedAfterRules := slices.Clone(rules)
|
||||||
|
slices.SortFunc(
|
||||||
|
rules,
|
||||||
|
func(a, b RulePair) int { return a.Left - b.Left },
|
||||||
|
)
|
||||||
|
for len(sortedAfterRules) != 0 {
|
||||||
|
popAt := []int{}
|
||||||
|
for i, rule := range sortedAfterRules {
|
||||||
|
if !sliceutils.Contains(nrsToUse, rule.Left) ||
|
||||||
|
!sliceutils.Contains(nrsToUse, rule.Right) {
|
||||||
|
popAt = append(popAt, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nrsOut
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLowestIndex(inSlice, elems []int) int {
|
||||||
|
index := len(inSlice)
|
||||||
|
for _, elem := range elems {
|
||||||
|
i := slices.Index(inSlice, elem)
|
||||||
|
if i < index {
|
||||||
|
index = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index == len(inSlice) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
blocks := strings.Split(string(util.LoadFileFromArgs()), "\n\n")
|
||||||
|
ruleLines := sliceutils.Filter(
|
||||||
|
strings.Split(blocks[0], "\n"),
|
||||||
|
func(t string) bool { return len(t) != 0 },
|
||||||
|
)
|
||||||
|
applyLines := sliceutils.Filter(
|
||||||
|
strings.Split(blocks[1], "\n"),
|
||||||
|
func(t string) bool { return len(t) != 0 },
|
||||||
|
)
|
||||||
|
|
||||||
|
rulesAfter := map[int][]int{}
|
||||||
|
rulesBefore := map[int][]int{}
|
||||||
|
rulePairs := []RulePair{}
|
||||||
|
|
||||||
|
for _, rule := range ruleLines {
|
||||||
|
before, after := parseRule(rule)
|
||||||
|
rulesAfter[before] = append(rulesAfter[before], after)
|
||||||
|
rulesBefore[after] = append(rulesBefore[after], before)
|
||||||
|
rulePairs = append(rulePairs, RulePair{before, after})
|
||||||
|
}
|
||||||
|
for k, nums := range rulesBefore {
|
||||||
|
slices.Sort(nums)
|
||||||
|
rulesBefore[k] = sliceutils.RemoveDuplicate(nums)
|
||||||
|
}
|
||||||
|
for k, nums := range rulesAfter {
|
||||||
|
slices.Sort(nums)
|
||||||
|
rulesAfter[k] = sliceutils.RemoveDuplicate(nums)
|
||||||
|
}
|
||||||
|
// fmt.Printf("Rules before a number: %v\nRules after a number: %v\n", rulesBefore, rulesAfter)
|
||||||
|
updates := sliceutils.Map(applyLines, func(t string) []int { return parseUpdate(t) })
|
||||||
|
correctUpdates := [][]int{}
|
||||||
|
incorrectUpdates := [][]int{}
|
||||||
|
for _, update := range updates {
|
||||||
|
if checkUpdateWithRules(rulesAfter, rulesBefore, update) {
|
||||||
|
correctUpdates = append(correctUpdates, update)
|
||||||
|
} else {
|
||||||
|
incorrectUpdates = append(incorrectUpdates, update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = incorrectUpdates
|
||||||
|
|
||||||
|
acc := 0
|
||||||
|
for _, elems := range correctUpdates {
|
||||||
|
acc += getCenterVal(elems)
|
||||||
|
}
|
||||||
|
fmt.Printf("Task 1: %d\n", acc)
|
||||||
|
// fmt.Printf("#rulesBefore: %d, #rulesAfter: %d\n", len(rulesBefore), len(rulesAfter))
|
||||||
|
// allRuleNumbers := []int{}
|
||||||
|
// for k, _ := range rulesBefore {
|
||||||
|
// allRuleNumbers = append(allRuleNumbers, k)
|
||||||
|
// }
|
||||||
|
// for k, _ := range rulesAfter {
|
||||||
|
// allRuleNumbers = append(allRuleNumbers, k)
|
||||||
|
// }
|
||||||
|
// allRuleNumbers = sliceutils.RemoveDuplicate(allRuleNumbers)
|
||||||
|
// fmt.Printf("#allRuleNumbers: %d, allRuleNumbers: %v \n", len(allRuleNumbers), allRuleNumbers)
|
||||||
|
for i := range incorrectUpdates {
|
||||||
|
fmt.Printf("Checking invalid update %v\n", incorrectUpdates[i])
|
||||||
|
for !checkUpdateWithRules(rulesAfter, rulesBefore, incorrectUpdates[i]) {
|
||||||
|
// Go over every element in the update
|
||||||
|
updateLoop:
|
||||||
|
for ie, elem := range incorrectUpdates[i] {
|
||||||
|
// Get all rules where the current number is on the right side of the rule (has to come after)
|
||||||
|
appliedRulesLeft := sliceutils.Filter(rulePairs, func(t RulePair) bool {
|
||||||
|
return elem == t.Right
|
||||||
|
})
|
||||||
|
// Same but current number is on the left (has to come before)
|
||||||
|
appliedRulesRight := sliceutils.Filter(rulePairs, func(t RulePair) bool {
|
||||||
|
return elem == t.Left
|
||||||
|
})
|
||||||
|
for _, rule := range appliedRulesLeft {
|
||||||
|
// If rule violation, swap the two elements
|
||||||
|
if il := slices.Index(incorrectUpdates[i][:ie], rule.Left); il != -1 {
|
||||||
|
fmt.Printf("Swapping according to rule %#v\n", rule)
|
||||||
|
incorrectUpdates[i][ie] = rule.Left
|
||||||
|
incorrectUpdates[i][il] = elem
|
||||||
|
break updateLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, rule := range appliedRulesRight {
|
||||||
|
if ir := slices.Index(incorrectUpdates[i][ie:], rule.Right); ir != -1 {
|
||||||
|
fmt.Printf("Swapping according to rule %#v\n", rule)
|
||||||
|
incorrectUpdates[i][ie] = rule.Right
|
||||||
|
incorrectUpdates[i][ir] = elem
|
||||||
|
break updateLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acc = 0
|
||||||
|
for _, elems := range incorrectUpdates {
|
||||||
|
acc += getCenterVal(elems)
|
||||||
|
}
|
||||||
|
fmt.Printf("Task 2: %d\n", acc)
|
||||||
|
}
|
1391
day5/input
Normal file
1391
day5/input
Normal file
File diff suppressed because it is too large
Load diff
143
day5/main.go
Normal file
143
day5/main.go
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.mstar.dev/mstar/aoc24/util"
|
||||||
|
"git.mstar.dev/mstar/goutils/other"
|
||||||
|
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gameplan:
|
||||||
|
1. Einlesen als roh linien
|
||||||
|
2. Linien bis \n\n sind Regeln
|
||||||
|
3. Jede Linie danach muss den Regeln folgen
|
||||||
|
4. Regeln in zwei maps speichern
|
||||||
|
- 1. Map zuvor -> []danach: Jede nr hat eine Liste an nr, welche nach key kommen müssen
|
||||||
|
- 2. Map danach -> []zuvor: Jede nr hat eine liste an nr, welche vor key kommen müssen
|
||||||
|
5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type RulePair struct {
|
||||||
|
Left, Right int
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRule(rule string) (int, int) {
|
||||||
|
tmp := strings.Split(rule, "|")
|
||||||
|
return other.Must(strconv.Atoi(tmp[0])), other.Must(strconv.Atoi(tmp[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUpdate(line string) []int {
|
||||||
|
tmp := strings.Split(line, ",")
|
||||||
|
return sliceutils.Map(tmp, func(t string) int { return other.Must(strconv.Atoi(t)) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUpdateWithRules(rulesAfter, rulesBefore map[int][]int, update []int) bool {
|
||||||
|
for i, num := range update {
|
||||||
|
for _, elem := range update[:i] {
|
||||||
|
if sliceutils.Contains(rulesAfter[num], elem) {
|
||||||
|
// fmt.Printf("%v failed rule %d: %v\n", update, num, rulesAfter[num])
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, elem := range update[i:] {
|
||||||
|
if sliceutils.Contains(rulesBefore[num], elem) {
|
||||||
|
// fmt.Printf("%v failed rule %d: %v\n", update, num, rulesBefore[num])
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCenterVal(elems []int) int {
|
||||||
|
return elems[len(elems)/2]
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
blocks := strings.Split(string(util.LoadFileFromArgs()), "\n\n")
|
||||||
|
ruleLines := sliceutils.Filter(
|
||||||
|
strings.Split(blocks[0], "\n"),
|
||||||
|
func(t string) bool { return len(t) != 0 },
|
||||||
|
)
|
||||||
|
applyLines := sliceutils.Filter(
|
||||||
|
strings.Split(blocks[1], "\n"),
|
||||||
|
func(t string) bool { return len(t) != 0 },
|
||||||
|
)
|
||||||
|
|
||||||
|
rulesAfter := map[int][]int{}
|
||||||
|
rulesBefore := map[int][]int{}
|
||||||
|
rulePairs := []RulePair{}
|
||||||
|
|
||||||
|
for _, rule := range ruleLines {
|
||||||
|
before, after := parseRule(rule)
|
||||||
|
rulesAfter[before] = append(rulesAfter[before], after)
|
||||||
|
rulesBefore[after] = append(rulesBefore[after], before)
|
||||||
|
rulePairs = append(rulePairs, RulePair{before, after})
|
||||||
|
}
|
||||||
|
for k, nums := range rulesBefore {
|
||||||
|
slices.Sort(nums)
|
||||||
|
rulesBefore[k] = sliceutils.RemoveDuplicate(nums)
|
||||||
|
}
|
||||||
|
for k, nums := range rulesAfter {
|
||||||
|
slices.Sort(nums)
|
||||||
|
rulesAfter[k] = sliceutils.RemoveDuplicate(nums)
|
||||||
|
}
|
||||||
|
// fmt.Printf("Rules before a number: %v\nRules after a number: %v\n", rulesBefore, rulesAfter)
|
||||||
|
updates := sliceutils.Map(applyLines, func(t string) []int { return parseUpdate(t) })
|
||||||
|
correctUpdates := [][]int{}
|
||||||
|
incorrectUpdates := [][]int{}
|
||||||
|
for _, update := range updates {
|
||||||
|
if checkUpdateWithRules(rulesAfter, rulesBefore, update) {
|
||||||
|
correctUpdates = append(correctUpdates, update)
|
||||||
|
} else {
|
||||||
|
incorrectUpdates = append(incorrectUpdates, update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = incorrectUpdates
|
||||||
|
|
||||||
|
acc := 0
|
||||||
|
for _, elems := range correctUpdates {
|
||||||
|
acc += getCenterVal(elems)
|
||||||
|
}
|
||||||
|
fmt.Printf("Task 1: %d\n", acc)
|
||||||
|
// fmt.Printf("#rulesBefore: %d, #rulesAfter: %d\n", len(rulesBefore), len(rulesAfter))
|
||||||
|
// allRuleNumbers := []int{}
|
||||||
|
// for k, _ := range rulesBefore {
|
||||||
|
// allRuleNumbers = append(allRuleNumbers, k)
|
||||||
|
// }
|
||||||
|
// for k, _ := range rulesAfter {
|
||||||
|
// allRuleNumbers = append(allRuleNumbers, k)
|
||||||
|
// }
|
||||||
|
// allRuleNumbers = sliceutils.RemoveDuplicate(allRuleNumbers)
|
||||||
|
// fmt.Printf("#allRuleNumbers: %d, allRuleNumbers: %v \n", len(allRuleNumbers), allRuleNumbers)
|
||||||
|
for _, update := range incorrectUpdates {
|
||||||
|
appliedRules := sliceutils.Filter(rulePairs, func(t RulePair) bool {
|
||||||
|
return sliceutils.Contains(update, t.Left) && sliceutils.Contains(update, t.Right)
|
||||||
|
})
|
||||||
|
slices.SortFunc(update, func(a, b int) int {
|
||||||
|
// Find all rules that apply to the current number pair, should only ever produce one
|
||||||
|
currentRules := sliceutils.Filter(appliedRules, func(t RulePair) bool {
|
||||||
|
return t.Left == a && t.Right == b || t.Left == b && t.Right == a
|
||||||
|
})
|
||||||
|
if len(currentRules) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
rule := currentRules[0]
|
||||||
|
if a == rule.Left {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
acc = 0
|
||||||
|
for _, elems := range incorrectUpdates {
|
||||||
|
acc += getCenterVal(elems)
|
||||||
|
}
|
||||||
|
fmt.Printf("Task 2: %d\n", acc)
|
||||||
|
}
|
28
day5/sample
Normal file
28
day5/sample
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
47|53
|
||||||
|
97|13
|
||||||
|
97|61
|
||||||
|
97|47
|
||||||
|
75|29
|
||||||
|
61|13
|
||||||
|
75|53
|
||||||
|
29|13
|
||||||
|
97|29
|
||||||
|
53|29
|
||||||
|
61|53
|
||||||
|
97|53
|
||||||
|
61|29
|
||||||
|
47|13
|
||||||
|
75|47
|
||||||
|
97|75
|
||||||
|
47|61
|
||||||
|
75|61
|
||||||
|
47|29
|
||||||
|
75|13
|
||||||
|
53|13
|
||||||
|
|
||||||
|
75,47,61,53,29
|
||||||
|
97,61,53,29,13
|
||||||
|
75,29,13
|
||||||
|
75,97,47,61,53
|
||||||
|
61,13,29
|
||||||
|
97,13,75,29,47
|
14
util/math.go
Normal file
14
util/math.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
func MaxI(x ...int) int {
|
||||||
|
if len(x) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
tmp := x[0]
|
||||||
|
for _, v := range x {
|
||||||
|
if tmp < v {
|
||||||
|
tmp = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
12
util/slices.go
Normal file
12
util/slices.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
func RemoveFromSlice[T any](s []T, i int) []T {
|
||||||
|
s[i] = s[len(s)-1]
|
||||||
|
return s[:len(s)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceSwap[T any](s []T, x, y int) {
|
||||||
|
tmp := s[x]
|
||||||
|
s[x] = s[y]
|
||||||
|
s[y] = tmp
|
||||||
|
}
|
Loading…
Reference in a new issue