package day_15 import ( "fmt" "slices" "strings" ) const ( Wall = '#' Player = '@' Box = 'O' Empty = '.' WideBoxL = '[' WideBoxR = ']' ) const ( InstUp = '^' InstDown = 'v' InstLeft = '<' InstRight = '>' ) type Warehouse [][]rune func (w Warehouse) Print() { for _, line := range w { fmt.Println(string(line)) } } func (w Warehouse) At(x, y int) rune { if x < 0 || y < 0 || x >= len(w[0]) || y >= len(w) { return Wall } return w[y][x] } // task:https://adventofcode.com/2024/day/13 func SolveBasic(input string) int { warehouse, instructions, rX, rY := load(input) for _, instr := range instructions { switch instr { case InstUp: rX, rY = moveUp(warehouse, rX, rY) case InstDown: rX, rY = moveDown(warehouse, rX, rY) case InstLeft: rX, rY = moveLeft(warehouse, rX, rY) case InstRight: rX, rY = moveRight(warehouse, rX, rY) default: panic("unknown instruction " + string(instr)) } } sum := 0 for y, line := range warehouse { for x, val := range line { if val == Box { sum += y*100 + x } } } return sum } // task:https://adventofcode.com/2024/day/12#part2 func SolveComplex(input string) int { warehouse, instructions, rX, rY := loadWide(input) for _, instr := range instructions { switch instr { case InstUp: rX, rY = moveWideUp(warehouse, rX, rY) case InstDown: rX, rY = moveWideDown(warehouse, rX, rY) case InstLeft: rX, rY = moveWideLeft(warehouse, rX, rY) case InstRight: rX, rY = moveWideRight(warehouse, rX, rY) default: panic("unknown instruction " + string(instr)) } } sum := 0 for y, line := range warehouse { for x, val := range line { if val == WideBoxL { sum += y*100 + x } } } return sum } func load(input string) (Warehouse, []rune, int, int) { substrings := strings.Split(input, "\n\n") warehouse := Warehouse{} rX := 0 rY := 0 for y, line := range strings.Split(substrings[0], "\n") { if strings.ContainsRune(line, Player) { rY = y rX = strings.IndexRune(line, Player) } warehouse = append(warehouse, []rune(line)) } instructions := []rune(strings.ReplaceAll(substrings[1], "\n", "")) return warehouse, instructions, rX, rY } func loadWide(input string) (Warehouse, []rune, int, int) { substrings := strings.Split(input, "\n\n") warehouse := Warehouse{} rX := 0 rY := 0 for y, line := range strings.Split(substrings[0], "\n") { line = expandLine(line) if strings.ContainsRune(line, Player) { rY = y rX = strings.IndexRune(line, Player) } warehouse = append(warehouse, []rune(line)) } instructions := []rune(strings.ReplaceAll(substrings[1], "\n", "")) return warehouse, instructions, rX, rY } func expandLine(line string) string { line = strings.ReplaceAll(line, "#", "##") line = strings.ReplaceAll(line, ".", "..") line = strings.ReplaceAll(line, "@", "@.") line = strings.ReplaceAll(line, "O", "[]") return line } func moveUp(warehouse Warehouse, x int, y int) (int, int) { if warehouse.At(x, y-1) == Wall { return x, y } else if warehouse.At(x, y-1) == Empty { warehouse[y][x] = Empty warehouse[y-1][x] = Player return x, y - 1 } else { newY := y - 1 for ; warehouse.At(x, newY) == Box; newY-- { } if warehouse.At(x, newY) == Empty { warehouse[newY][x] = Box warehouse[y-1][x] = Player warehouse[y][x] = Empty return x, y - 1 } } return x, y } func moveDown(warehouse Warehouse, x int, y int) (int, int) { if warehouse.At(x, y+1) == Wall { return x, y } else if warehouse.At(x, y+1) == Empty { warehouse[y][x] = Empty warehouse[y+1][x] = Player return x, y + 1 } else { newY := y + 1 for ; warehouse.At(x, newY) == Box; newY++ { } if warehouse.At(x, newY) == Empty { warehouse[newY][x] = Box warehouse[y+1][x] = Player warehouse[y][x] = Empty return x, y + 1 } } return x, y } func moveLeft(warehouse Warehouse, x int, y int) (int, int) { if warehouse.At(x-1, y) == Wall { return x, y } else if warehouse.At(x-1, y) == Empty { warehouse[y][x] = Empty warehouse[y][x-1] = Player return x - 1, y } else { newX := x - 1 for ; warehouse.At(newX, y) == Box; newX-- { } if warehouse.At(newX, y) == Empty { warehouse[y][newX] = Box warehouse[y][x-1] = Player warehouse[y][x] = Empty return x - 1, y } } return x, y } func moveRight(warehouse Warehouse, x int, y int) (int, int) { if warehouse.At(x+1, y) == Wall { return x, y } else if warehouse.At(x+1, y) == Empty { warehouse[y][x] = Empty warehouse[y][x+1] = Player return x + 1, y } else { newX := x + 1 for ; warehouse.At(newX, y) == Box; newX++ { } if warehouse.At(newX, y) == Empty { warehouse[y][newX] = Box warehouse[y][x+1] = Player warehouse[y][x] = Empty return x + 1, y } } return x, y } func moveWideUp(warehouse Warehouse, x int, y int) (int, int) { if warehouse.At(x, y-1) == Wall { return x, y } else if warehouse.At(x, y-1) == Empty { warehouse[y][x] = Empty warehouse[y-1][x] = Player return x, y - 1 } else { type Point struct { x, y int } checkable := []Point{{x, y}} for i := 0; i < len(checkable); i++ { p := checkable[i] l := warehouse.At(p.x, p.y-1) if l == Wall { return x, y } if l == WideBoxL { if !slices.Contains(checkable, Point{p.x, p.y - 1}) { checkable = append(checkable, Point{p.x, p.y - 1}) } if !slices.Contains(checkable, Point{p.x + 1, p.y - 1}) { checkable = append(checkable, Point{p.x + 1, p.y - 1}) } } if l == WideBoxR { if !slices.Contains(checkable, Point{p.x, p.y - 1}) { checkable = append(checkable, Point{p.x, p.y - 1}) } if !slices.Contains(checkable, Point{p.x - 1, p.y - 1}) { checkable = append(checkable, Point{p.x - 1, p.y - 1}) } } } for i := len(checkable) - 1; i >= 0; i-- { p := checkable[i] warehouse[p.y-1][p.x] = warehouse[p.y][p.x] warehouse[p.y][p.x] = Empty } return x, y - 1 } } func moveWideDown(warehouse Warehouse, x int, y int) (int, int) { if warehouse.At(x, y+1) == Wall { return x, y } else if warehouse.At(x, y+1) == Empty { warehouse[y][x] = Empty warehouse[y+1][x] = Player return x, y + 1 } else { type Point struct { x, y int } checkable := []Point{{x, y}} for i := 0; i < len(checkable); i++ { p := checkable[i] l := warehouse.At(p.x, p.y+1) if l == Wall { return x, y } if l == WideBoxL { if !slices.Contains(checkable, Point{p.x, p.y + 1}) { checkable = append(checkable, Point{p.x, p.y + 1}) } if !slices.Contains(checkable, Point{p.x + 1, p.y + 1}) { checkable = append(checkable, Point{p.x + 1, p.y + 1}) } } if l == WideBoxR { if !slices.Contains(checkable, Point{p.x, p.y + 1}) { checkable = append(checkable, Point{p.x, p.y + 1}) } if !slices.Contains(checkable, Point{p.x - 1, p.y + 1}) { checkable = append(checkable, Point{p.x - 1, p.y + 1}) } } } for i := len(checkable) - 1; i >= 0; i-- { p := checkable[i] warehouse[p.y+1][p.x] = warehouse[p.y][p.x] warehouse[p.y][p.x] = Empty } return x, y + 1 } } func moveWideLeft(warehouse Warehouse, x int, y int) (int, int) { if warehouse.At(x-1, y) == Wall { return x, y } else if warehouse.At(x-1, y) == Empty { warehouse[y][x] = Empty warehouse[y][x-1] = Player return x - 1, y } else { newX := x - 1 for ; warehouse.At(newX, y) == WideBoxL || warehouse.At(newX, y) == WideBoxR; newX-- { } if warehouse.At(newX, y) == Empty { for tx := newX; tx < x-1; tx++ { warehouse[y][tx] = warehouse[y][tx+1] } warehouse[y][x-1] = Player warehouse[y][x] = Empty return x - 1, y } } return x, y } func moveWideRight(warehouse Warehouse, x int, y int) (int, int) { if warehouse.At(x+1, y) == Wall { return x, y } else if warehouse.At(x+1, y) == Empty { warehouse[y][x] = Empty warehouse[y][x+1] = Player return x + 1, y } else { newX := x + 1 for ; warehouse.At(newX, y) == WideBoxL || warehouse.At(newX, y) == WideBoxR; newX++ { } if warehouse.At(newX, y) == Empty { for tx := newX; tx > x+1; tx-- { warehouse[y][tx] = warehouse[y][tx-1] } warehouse[y][x+1] = Player warehouse[y][x] = Empty return x + 1, y } } return x, y }