diff --git a/internal/2023/day_07/in.go b/internal/2023/day_07/in.go index 2e8ba94..6c96ea6 100644 --- a/internal/2023/day_07/in.go +++ b/internal/2023/day_07/in.go @@ -1,11 +1,284 @@ package day_07 -type Day struct{} +import ( + "fmt" + "sort" + "strings" + + "git.rpjosh.de/RPJosh/go-logger" + "rpjosh.de/adventOfCode/pkg/utils" +) + +type Day struct { + hands map[byte]int +} func (d *Day) Part1(in string) string { - return "" + inSplit := strings.Split(in, "\n") + //inSplit := []string{"23332 246"} + + hands := map[byte]int{ + 'A': 0, + 'K': 1, + 'Q': 2, + 'J': 3, + 'T': 4, + '9': 5, + '8': 6, + '7': 7, + '6': 8, + '5': 9, + '4': 10, + '3': 11, + '2': 12, + } + + sort.SliceStable(inSplit, func(a, b int) bool { + aVal := getTypeOrders(strings.Split(inSplit[a], " ")[0]) + bVal := getTypeOrders(strings.Split(inSplit[b], " ")[0]) + + if aVal > bVal { + return true + } else if aVal == bVal { + // Get first number + aS := strings.Split(inSplit[a], " ")[0] + bS := strings.Split(inSplit[b], " ")[0] + + for i := 0; i < 5; i++ { + if hands[aS[i]] > hands[bS[i]] { + return true + } else if hands[aS[i]] == hands[bS[i]] { + continue + } else { + return false + } + } + logger.Error("Two values are the same. What to do?") + return false + } else { + return false + } + }) + + rtc := 0 + for i, val := range inSplit { + if val == "" { + continue + } + logger.Debug("%s == %d", val, getTypeOrders(strings.Split(val, " ")[0])) + rtc += (i + 1) * utils.ToInt(strings.Split(val, " ")[1]) + } + + return fmt.Sprintf("%d", rtc) +} + +type Values struct { + val rune + count int + positions string +} + +func getTypeOrders(numbers string) int { + + if len(numbers) < 4 { + logger.Error("Received invalid input: %q", numbers) + return 0 + } + + // Five in a row + if strings.Count(numbers, string(numbers[0])) == 5 { + return 1 + } + + // Four in a row + if strings.Count(numbers, string(numbers[0])) == 4 || strings.Count(numbers, string(numbers[1])) == 4 { + return 2 + } + // Prepare values + numbersSorted := utils.SortRunes(numbers) + runeMap := map[rune]int{} + + // Three of a kind or full house + vals := make([]Values, 3) + for i, val := range numbers { + if vals[0].val == val { + vals[0].count += 1 + vals[0].positions += fmt.Sprintf("%d", i) + } else if vals[1].val == val { + vals[1].count += 1 + vals[1].positions += fmt.Sprintf("%d", i) + } else if vals[2].val == val { + vals[2].count += 1 + vals[2].positions += fmt.Sprintf("%d", i) + } else if vals[0].count == 0 { + vals[0].val = val + vals[0].count = 1 + vals[0].positions += fmt.Sprintf("%d", i) + } else if vals[1].count == 0 { + vals[1].val = val + vals[1].count = 1 + vals[1].positions += fmt.Sprintf("%d", i) + } else if vals[2].count == 0 { + vals[2].val = val + vals[2].count = 1 + vals[2].positions += fmt.Sprintf("%d", i) + } + } + // We need three same kinds + threeValid := false + for i := 0; i < 3; i++ { + if vals[i].count == 3 { + threeValid = true + /* + //logger.Debug("==== Having three") + // In order + lastVal := int(vals[i].positions[0]) + for _, r := range vals[i].positions[1:] { + //logger.Debug("order: %d %d", lastVal, r-1) + if lastVal == int(r)-1 { + threeValid = true + lastVal = int(r) + } else { + threeValid = false + } + } + */ + } + } + + if threeValid && ((vals[0].count == 3 && vals[1].count == 1) || (vals[1].count == 3 && vals[0].count == 1) || vals[2].count == 3 && vals[0].count == 1) { + return 4 + } else if threeValid && (vals[1].count == 2 || vals[0].count == 2 || vals[3].count == 2) { + return 3 + } + + // Two pair + runeMap = map[rune]int{} + for _, val := range numbersSorted { + runeMap[val] += 1 + } + pairs := 0 + for _, val := range runeMap { + if val == 2 { + pairs += 1 + } + } + + // Two pairs + if pairs == 2 { + return 5 + } + + // One pair + if pairs == 1 { + return 6 + } + + // Do we need to distinct? + areDistinct := true + for _, val := range runeMap { + if val > 1 { + logger.Fatal("Values are not distinct. That's a logic error: %q", numbers) + areDistinct = false + } + } + + if areDistinct { + return 7 + } + + return 10 +} + +func (d *Day) GetBestJokerValue(numbers string) string { + // Nothing to do here + if !strings.Contains(numbers, "J") { + return numbers + } + + // Loop through every 'J'. A 'J' can occur twice -> Brute force with recursion :) + bestNumbers := "" + lastValue := 20 + for k := range d.hands { + // Not allowed - endless loop! + if k == 'J' { + continue + } + + lastJ := strings.LastIndex(numbers, "J") + numbersNew := utils.ReplaceCharacterInString(numbers, string(k), lastJ) + + // Replace any other 'J' value + numbersNew = d.GetBestJokerValue(numbersNew) + + // Test the value + result := getTypeOrders(numbersNew) + + // Better result + if result < lastValue { + lastValue = result + bestNumbers = numbersNew + } + } + + return bestNumbers } func (d *Day) Part2(in string) string { - return "" + inSplit := strings.Split(in, "\n") + //inSplit := []string{"23332 246"} + + d.hands = map[byte]int{ + 'A': 0, + 'K': 1, + 'Q': 2, + 'T': 4, + '9': 5, + '8': 6, + '7': 7, + '6': 8, + '5': 9, + '4': 10, + '3': 11, + '2': 12, + 'J': 13, + } + + sort.SliceStable(inSplit, func(a, b int) bool { + aVal := getTypeOrders(d.GetBestJokerValue(strings.Split(inSplit[a], " ")[0])) + bVal := getTypeOrders(d.GetBestJokerValue(strings.Split(inSplit[b], " ")[0])) + + if aVal > bVal { + return true + } else if aVal == bVal { + // Get first number + aS := strings.Split(inSplit[a], " ")[0] + bS := strings.Split(inSplit[b], " ")[0] + + for i := 0; i < 5; i++ { + if d.hands[aS[i]] > d.hands[bS[i]] { + return true + } else if d.hands[aS[i]] == d.hands[bS[i]] { + continue + } else { + return false + } + } + logger.Error("Two values are the same. What to do?") + return false + } else { + return false + } + }) + + rtc := 0 + for i, val := range inSplit { + if val == "" { + continue + } + logger.Debug("%s == %d", val, getTypeOrders(strings.Split(val, " ")[0])) + rtc += (i + 1) * utils.ToInt(strings.Split(val, " ")[1]) + } + + return fmt.Sprintf("%d", rtc) } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 546fce8..b15e907 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -6,11 +6,10 @@ import ( "net/http" "os" "os/exec" + "sort" "strconv" "strings" "time" - - "git.rpjosh.de/RPJosh/go-logger" ) // Copies the given string to the clipboard. @@ -89,7 +88,7 @@ func PrintError(format string, a ...any) { func ToInt(val string) int { value, err := strconv.Atoi(val) if err != nil { - logger.Fatal("Failed to convert %q to a number: %s", val, err) + panic(fmt.Sprintf("Failed to convert %q to a number: %s", val, err)) } return value @@ -135,3 +134,19 @@ func ConvertArrayToInt(values []string) []int { return rtc } + +// SortRunes sorts a string based on the rune value +// of every character +func SortRunes(value string) string { + runeSlice := []rune(value) + sort.Slice(runeSlice, func(i, j int) bool { + return runeSlice[i] < runeSlice[j] + }) + return string(runeSlice) +} + +// ReplaceCharacterInString replaces the character at the given index with +// the provided value +func ReplaceCharacterInString(str, replace string, index int) string { + return str[:index] + replace + str[index+1:] +}