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