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) }