advent_of_code_2024/day_15/solution.go

360 lines
8.1 KiB
Go

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
}