AOC-2024/15/main.go
2024-12-15 23:07:48 +01:00

382 lines
9.3 KiB
Go

package main
import (
"bufio"
"fmt"
"os"
"time"
)
const FILE_PATH = "./input.txt"
//const FILE_PATH = "./sample.txt"
func LoadInput(filename string) ([]string, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return nil, err
}
return lines, nil
}
type Vector2 struct {
x int
y int
}
const (
EMPTY = iota
ROBOT = iota
BOX = iota
WALL = iota
LEFT_BOX = iota
RIGHT_BOX = iota
)
const (
UP = '^'
DOWN = 'v'
LEFT = '<'
RIGHT = '>'
)
var MOVEMENT = map[byte]Vector2{
UP: Vector2{0, -1},
DOWN: Vector2{0, 1},
LEFT: Vector2{-1, 0},
RIGHT: Vector2{1, 0},
}
type Lab struct {
field [][]int
command string
}
func cloneLab(lab Lab) Lab {
var field = make([][]int, len(lab.field))
for y := 0; y < len(lab.field); y++ {
field[y] = make([]int, len(lab.field[y]))
for x := 0; x < len(lab.field[y]); x++ {
field[y][x] = lab.field[y][x]
}
}
return Lab{field, lab.command}
}
func cloneWideLab(lab Lab) Lab {
var field = make([][]int, len(lab.field))
for y := 0; y < len(lab.field); y++ {
field[y] = make([]int, len(lab.field[y])*2)
for x := 0; x < len(lab.field[y]); x++ {
if lab.field[y][x] == BOX {
field[y][x*2] = LEFT_BOX
field[y][x*2+1] = RIGHT_BOX
} else if lab.field[y][x] == ROBOT {
field[y][x*2] = ROBOT
field[y][x*2+1] = EMPTY
} else {
field[y][x*2] = lab.field[y][x]
field[y][x*2+1] = lab.field[y][x]
}
}
}
return Lab{field, lab.command}
}
type structuredInput = Lab
func TransformInput(lines []string) structuredInput {
var field = make([][]int, 0)
var row []int
var i = 0
var endField = false
var command string = ""
for !endField {
if lines[i] == "" {
endField = true
break
}
row = make([]int, 0)
for j := 0; j < len(lines[i]); j++ {
if lines[i][j] == '@' {
row = append(row, ROBOT)
} else if lines[i][j] == 'O' {
row = append(row, BOX)
} else if lines[i][j] == '#' {
row = append(row, WALL)
} else {
row = append(row, EMPTY)
}
}
field = append(field, row)
i += 1
}
for ; i < len(lines); i++ {
command += lines[i]
}
return Lab{field, command}
}
func tryPush(lab *Lab, position Vector2, direction Vector2) {
if lab.field[position.y+direction.y][position.x+direction.x] == WALL {
return
}
if lab.field[position.y+direction.y][position.x+direction.x] == EMPTY {
lab.field[position.y+direction.y][position.x+direction.x] = lab.field[position.y][position.x]
lab.field[position.y][position.x] = EMPTY
return
}
tryPush(lab, Vector2{position.x + direction.x, position.y + direction.y}, direction)
if lab.field[position.y+direction.y][position.x+direction.x] == EMPTY {
lab.field[position.y+direction.y][position.x+direction.x] = lab.field[position.y][position.x]
lab.field[position.y][position.x] = EMPTY
}
}
func getRobot(lab Lab) Vector2 {
for y := 0; y < len(lab.field); y++ {
for x := 0; x < len(lab.field[y]); x++ {
if lab.field[y][x] == ROBOT {
return Vector2{x, y}
}
}
}
return Vector2{-1, -1}
}
func printLab(lab Lab, command byte) {
for y := 0; y < len(lab.field); y++ {
for x := 0; x < len(lab.field[y]); x++ {
if lab.field[y][x] == ROBOT {
fmt.Printf("%c", command)
}
if lab.field[y][x] == EMPTY {
fmt.Print(".")
}
if lab.field[y][x] == WALL {
fmt.Print("#")
}
if lab.field[y][x] == LEFT_BOX {
fmt.Print("[")
}
if lab.field[y][x] == RIGHT_BOX {
fmt.Print("]")
}
if lab.field[y][x] == BOX {
fmt.Print("O")
}
}
fmt.Println()
}
}
func calculateLabScore(lab Lab) int {
var result = 0
for y := 0; y < len(lab.field); y++ {
for x := 0; x < len(lab.field[y]); x++ {
if lab.field[y][x] == BOX {
result += x + y*100
}
}
}
return result
}
func calculateWideLabScore(lab Lab) int {
var result = 0
for y := 0; y < len(lab.field); y++ {
for x := 0; x < len(lab.field[y]); x++ {
if lab.field[y][x] == LEFT_BOX {
result += x + y*100
}
}
}
return result
}
func Part1(input structuredInput) int {
var lab = cloneLab(input)
for i := 0; i < len(lab.command); i++ {
tryPush(&lab, getRobot(lab), MOVEMENT[lab.command[i]])
}
return calculateLabScore(lab)
}
func clearScreen() {
fmt.Print("\033[H\033[2J") // Clear entire screen
}
func moveCursorUp(lines int) {
fmt.Printf("\033[%dA", lines) // Move cursor up n lines
}
func canPushRobot(lab *Lab, position Vector2, direction Vector2) bool {
if lab.field[position.y+direction.y][position.x+direction.x] == WALL {
return false
}
if lab.field[position.y+direction.y][position.x+direction.x] == EMPTY {
return true
}
return canPushBox(lab, Vector2{position.x + direction.x, position.y + direction.y}, direction)
}
func canPushBox(lab *Lab, position Vector2, direction Vector2) bool {
var left_position = position
if lab.field[position.y][position.x] == RIGHT_BOX {
left_position = Vector2{position.x - 1, position.y}
}
if direction == MOVEMENT[UP] || direction == MOVEMENT[DOWN] {
if lab.field[left_position.y+direction.y][left_position.x] == WALL ||
lab.field[left_position.y+direction.y][left_position.x+1] == WALL {
return false
}
if lab.field[left_position.y+direction.y][left_position.x] == EMPTY &&
lab.field[left_position.y+direction.y][left_position.x+1] == EMPTY {
return true
}
if (lab.field[left_position.y+direction.y][left_position.x] == RIGHT_BOX || lab.field[left_position.y+direction.y][left_position.x] == LEFT_BOX) && !canPushBox(lab, Vector2{left_position.x, left_position.y + direction.y}, direction) {
return false
}
if lab.field[left_position.y+direction.y][left_position.x+1] == LEFT_BOX && !canPushBox(lab, Vector2{left_position.x + 1, left_position.y + direction.y}, direction) {
return false
}
return true
}
if direction == MOVEMENT[LEFT] {
if lab.field[left_position.y][left_position.x-1] == WALL {
return false
}
if lab.field[left_position.y][left_position.x-1] == EMPTY {
return true
}
return canPushBox(lab, Vector2{left_position.x - 1, left_position.y}, direction)
}
if lab.field[left_position.y][left_position.x+2] == WALL {
return false
}
if lab.field[left_position.y][left_position.x+2] == EMPTY {
return true
}
return canPushBox(lab, Vector2{left_position.x + 2, left_position.y}, direction)
}
func pushRobot(lab *Lab, position Vector2, direction Vector2) {
if lab.field[position.y+direction.y][position.x+direction.x] != EMPTY {
pushBox(lab, Vector2{position.x + direction.x, position.y + direction.y}, direction)
}
lab.field[position.y+direction.y][position.x+direction.x] = ROBOT
lab.field[position.y][position.x] = EMPTY
}
func pushBox(lab *Lab, position Vector2, direction Vector2) {
var left_position = position
if lab.field[position.y][position.x] == RIGHT_BOX {
left_position = Vector2{position.x - 1, position.y}
}
if direction == MOVEMENT[UP] || direction == MOVEMENT[DOWN] {
if lab.field[left_position.y+direction.y][left_position.x] == LEFT_BOX || lab.field[left_position.y+direction.y][left_position.x] == RIGHT_BOX {
pushBox(lab, Vector2{left_position.x, left_position.y + direction.y}, direction)
}
if lab.field[left_position.y+direction.y][left_position.x+1] == LEFT_BOX {
pushBox(lab, Vector2{left_position.x + 1, left_position.y + direction.y}, direction)
}
lab.field[left_position.y+direction.y][left_position.x] = LEFT_BOX
lab.field[left_position.y+direction.y][left_position.x+1] = RIGHT_BOX
lab.field[left_position.y][left_position.x] = EMPTY
lab.field[left_position.y][left_position.x+1] = EMPTY
}
if direction == MOVEMENT[LEFT] {
if lab.field[left_position.y][left_position.x-1] == RIGHT_BOX {
pushBox(lab, Vector2{position.x - 1, position.y}, direction)
}
lab.field[left_position.y][left_position.x-1] = LEFT_BOX
lab.field[left_position.y][left_position.x] = RIGHT_BOX
lab.field[left_position.y][left_position.x+1] = EMPTY
}
if direction == MOVEMENT[RIGHT] {
if lab.field[left_position.y][left_position.x+2] == LEFT_BOX {
pushBox(lab, Vector2{position.x + 2, position.y}, direction)
}
lab.field[left_position.y][left_position.x+1] = LEFT_BOX
lab.field[left_position.y][left_position.x+2] = RIGHT_BOX
lab.field[left_position.y][left_position.x] = EMPTY
}
}
func Part2(input structuredInput) int {
var lab = cloneWideLab(input)
//clearScreen()
//printLab(lab, lab.command[0])
//time.Sleep(1000 * time.Millisecond)
for i := 0; i < len(lab.command); i++ {
//moveCursorUp(len(lab.field))
if canPushRobot(&lab, getRobot(lab), MOVEMENT[lab.command[i]]) {
pushRobot(&lab, getRobot(lab), MOVEMENT[lab.command[i]])
}
//printLab(lab, lab.command[i+1])
//time.Sleep(100 * time.Millisecond)
}
return calculateWideLabScore(lab)
}
func main() {
var start time.Time
var elapsed time.Duration = 0
input, err := LoadInput(FILE_PATH)
if err != nil {
fmt.Println("Error loading input:", err)
return
}
structuredInput := TransformInput(input)
fmt.Println("Structured Input:", structuredInput)
start = time.Now()
fmt.Println("Solution Part 1: ", Part1(structuredInput))
elapsed = time.Since(start)
fmt.Printf("Part 1 took %s\n", elapsed)
start = time.Now()
fmt.Println("Solution Part 2: ", Part2(structuredInput))
elapsed = time.Since(start)
fmt.Printf("Part 2 took %s\n", elapsed)
}