package main import ( "fmt" "strings" "git.mstar.dev/mstar/aoc24/util" ) type Area struct { a [][]rune } func parseLinesToMap(lines []string) Area { out := [][]rune{} for _, line := range lines { out = append(out, []rune(strings.TrimSpace(line))) } return Area{out} } func parseInput(in []byte) (Area, []rune) { tmp := strings.Split(string(in), "\n\n") return parseLinesToMap(strings.Split(tmp[0], "\n")), []rune(tmp[1]) } func (area *Area) applyAction(action rune) { botX := -1 botY := -1 outer: for iy, line := range area.a { for ix, char := range line { if char == '@' { botX = ix botY = iy break outer } } } area.move(util.Vec2{X: int64(botX), Y: int64(botY)}, action) } func (area *Area) move(from util.Vec2, action rune) bool { switch action { case '<': return area.moveLeft(from) case '>': return area.moveRight(from) case 'v': return area.moveDown(from) case '^': return area.moveUp(from) default: return false } } func (area *Area) moveLeft(from util.Vec2) bool { target := area.a[from.Y][from.X-1] switch target { case '#': return false case '.': area.a[from.Y][from.X-1] = area.a[from.Y][from.X] area.a[from.Y][from.X] = '.' return true default: return area.moveLeft(from.Add(util.Vec2{X: -1, Y: 0})) && area.moveLeft(from) } } func (area *Area) moveRight(from util.Vec2) bool { target := area.a[from.Y][from.X+1] switch target { case '#': // fmt.Println("Wall") return false case '.': // fmt.Println("moving right") area.a[from.Y][from.X+1] = area.a[from.Y][from.X] area.a[from.Y][from.X] = '.' return true default: return area.moveRight(from.Add(util.Vec2{X: 1, Y: 0})) && area.moveRight(from) } } func (area *Area) moveUp(from util.Vec2) bool { target := area.a[from.Y-1][from.X] switch target { case '#': return false case '.': area.a[from.Y-1][from.X] = area.a[from.Y][from.X] area.a[from.Y][from.X] = '.' return true case '[': return area.moveUp(from.Add(util.Vec2{X: 0, Y: -1})) && area.moveUp(from.Add(util.Vec2{X: 1, Y: -1})) && area.moveUp(from) && area.moveUp(from.Add(util.Vec2{X: 1, Y: 0})) case ']': return area.moveUp(from.Add(util.Vec2{X: 0, Y: -1})) && area.moveUp(from.Add(util.Vec2{X: -1, Y: -1})) && area.moveUp(from) && area.moveUp(from.Add(util.Vec2{X: -1, Y: 0})) default: return area.moveUp(from.Add(util.Vec2{X: 0, Y: -1})) && area.moveUp(from) } } func (area *Area) moveDown(from util.Vec2) bool { target := area.a[from.Y+1][from.X] switch target { case '#': return false case '.': area.a[from.Y+1][from.X] = area.a[from.Y][from.X] area.a[from.Y][from.X] = '.' return true case '[': return area.moveUp(from.Add(util.Vec2{X: 0, Y: 1})) && area.moveUp(from.Add(util.Vec2{X: 1, Y: 1})) && area.moveUp(from) && area.moveUp(from.Add(util.Vec2{X: 1, Y: 0})) case ']': return area.moveUp(from.Add(util.Vec2{X: 0, Y: 1})) && area.moveUp(from.Add(util.Vec2{X: -1, Y: 1})) && area.moveUp(from) && area.moveUp(from.Add(util.Vec2{X: -1, Y: 0})) default: return area.moveDown(from.Add(util.Vec2{X: 0, Y: 1})) && area.moveDown(from) } } func (area *Area) visualise() string { builder := strings.Builder{} for _, line := range area.a { builder.WriteString(string(line)) builder.WriteRune('\n') } return builder.String() } func (area *Area) calcPosScore() int { score := 0 for iy, line := range area.a { for ix, char := range line { if char == 'O' || char == '[' { score += calcPosVal(ix, iy) } } } return score } func Widen(in string) string { return strings.ReplaceAll( strings.ReplaceAll( strings.ReplaceAll(strings.ReplaceAll(in, ".", ".."), "@", "@."), "#", "##", ), "O", "[]", ) } func calcPosVal(x, y int) int { return x + y*100 } func main() { rawInput := util.LoadFileFromArgs() area, inputs := parseInput(rawInput) // fmt.Println(area.visualise()) for _, action := range inputs { // fmt.Println(string(action)) area.applyAction(action) // fmt.Println(area.visualise()) } fmt.Printf("Task 1: %d\n", area.calcPosScore()) wArea, _ := parseInput([]byte(Widen(string(rawInput)))) fmt.Println(wArea.visualise()) for _, action := range inputs { wArea.applyAction(action) fmt.Println(wArea.visualise()) } fmt.Printf("Task 2: %d\n", wArea.calcPosScore()) }