package main import ( "flag" "fmt" "regexp" "strings" "github.com/alexchao26/advent-of-code-go/cast" "github.com/alexchao26/advent-of-code-go/util" ) func main() { var part int flag.IntVar(&part, "part", 1, "part 1 or 2") flag.Parse() fmt.Println("Running part", part) var ans int if part == 1 { ans = part1(util.ReadFile("./input.txt")) } else { ans = part2(util.ReadFile("./input.txt")) } fmt.Println("Output:", ans) } // 392 func part1(input string) int { parsed := parseInput(input) var total int for _, line := range parsed { // sum up this line, addressing things inside parens first... total += doMaths(line, calcFlatSlicePart1) } return total } // ~1700 :( func part2(input string) int { lines := parseInput(input) var total int for _, line := range lines { total += doMaths(line, calcFlatSlicePart2) } return total } func parseInput(input string) (ans [][]string) { lines := strings.Split(input, "\n") for _, l := range lines { // I got lucky that they were all single digit numbers... ans = append(ans, strings.Split(strings.ReplaceAll(l, " ", ""), "")) } return ans } func doMaths(input []string, flatteningFunc func([]string) string) int { var stackOpenIndices []int var stackFlattened []string for i := 0; i < len(input); i++ { // iterate through input, always append onto the flattened stack // track open paren indices (in the flattened stack) // on closing parens, use the top of the stackOpenIndices to flatten // the most recent set of values/operations within parens, and replace // their opening paren with the flattneed value stackFlattened = append(stackFlattened, input[i]) switch input[i] { case "(": stackOpenIndices = append(stackOpenIndices, len(stackFlattened)-1) case ")": // on close parens, pass a section tohandleFlat // then remove a bunch of shit from input, openIndex := stackOpenIndices[len(stackOpenIndices)-1] stackOpenIndices = stackOpenIndices[:len(stackOpenIndices)-1] // do not include leading or trailing paren sliToFlatten := stackFlattened[openIndex+1 : len(stackFlattened)-1] stackFlattened[openIndex] = flatteningFunc(sliToFlatten) // remove the values that were flattened off the top of the stack stackFlattened = stackFlattened[:openIndex+1] } } // slice should now be flat return cast.ToInt(flatteningFunc(stackFlattened)) } func calcFlatSlicePart1(input []string) string { for _, v := range input { if v == "(" || v == ")" { panic(fmt.Sprintf("unexpected paren in flat input, %v", input)) } } result := cast.ToInt(input[0]) for i := range input { if i+2 < len(input) { switch input[i+1] { case "+": result += cast.ToInt(input[i+2]) case "*": result *= cast.ToInt(input[i+2]) } } } return cast.ToString(result) } func calcFlatSlicePart2(input []string) string { for _, v := range input { if v == "(" || v == ")" { panic(fmt.Sprintf("unexpected paren in flat input, %v", input)) } } // handle all additions for i := 1; i < len(input)-1; i++ { if input[i] == "+" { toLeft := input[i-1] toRight := input[i+1] if isNum(toLeft) && isNum(toRight) { input[i-1] = addStrings(toLeft, toRight) input = splice(input, i, 2) i-- } } } // then handle all multiplications for i := 1; i < len(input)-1; i++ { if input[i] == "*" { toLeft := input[i-1] toRight := input[i+1] if isNum(toLeft) && isNum(toRight) { input[i-1] = multiplyStrings(toLeft, toRight) input = splice(input, i, 2) i-- } } } return input[0] } var numReg = regexp.MustCompile("[0-9]") func isNum(str string) bool { return numReg.MatchString(str) } func addStrings(strs ...string) string { var sum int for _, str := range strs { sum += cast.ToInt(str) } return cast.ToString(sum) } func multiplyStrings(strs ...string) string { sum := 1 for _, str := range strs { sum *= cast.ToInt(str) } return cast.ToString(sum) } // removes a particular number of elements from the middle of the slice func splice(sli []string, startIndex, items int) []string { copy(sli[startIndex:], sli[startIndex+items:]) sli = sli[:len(sli)-items] return sli }