diff options
author | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-12-15 22:09:12 +0100 |
---|---|---|
committer | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-12-20 15:33:16 +0100 |
commit | 7ea9fa5a6b10f93f0aca09ac8eb90543a781e67b (patch) | |
tree | 2efc383f5f43c3805451d7df695a7d4891639bef /day15/main.go | |
parent | 6f40d23c5ed72ef7238c838f2373c1f202cb62bb (diff) |
Day 15
Diffstat (limited to 'day15/main.go')
-rw-r--r-- | day15/main.go | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/day15/main.go b/day15/main.go new file mode 100644 index 0000000..377b961 --- /dev/null +++ b/day15/main.go @@ -0,0 +1,271 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func main() { + { + exampleFloor := readFloor("example_map.txt") + exampleMoves, _ := os.ReadFile("example_moves.txt") + fmt.Println(part1(exampleFloor, exampleMoves), 10092) + } + + { + dataFloor := readFloor("data_map.txt") + dataMoves, _ := os.ReadFile("data_moves.txt") + fmt.Println(part1(dataFloor, dataMoves), 1492518) + } + + // { + // var simpleFloor1 [][]byte + // simpleFloor1 = append(simpleFloor1, []byte("##########")) + // simpleFloor1 = append(simpleFloor1, []byte("#........#")) + // simpleFloor1 = append(simpleFloor1, []byte("#........#")) + // simpleFloor1 = append(simpleFloor1, []byte("#.....O..#")) + // simpleFloor1 = append(simpleFloor1, []byte("#.....O..#")) + // simpleFloor1 = append(simpleFloor1, []byte("#.....O..#")) + // simpleFloor1 = append(simpleFloor1, []byte("#.....@..#")) + // simpleFloor1 = append(simpleFloor1, []byte("#........#")) + // simpleFloor1 = append(simpleFloor1, []byte("#........#")) + // simpleFloor1 = append(simpleFloor1, []byte("##########")) + + // simpleFloor := makeWide(simpleFloor1) + // simpleMoves := []byte("^^") + // part2(simpleFloor, simpleMoves) + // } + + { + exampleFloor := makeWide(readFloor("example_map.txt")) + exampleMoves, _ := os.ReadFile("example_moves.txt") + fmt.Println(part2(exampleFloor, exampleMoves), 9021) + } + + { + dataFloor := makeWide(readFloor("data_map.txt")) + dataMoves, _ := os.ReadFile("data_moves.txt") + fmt.Println(part2(dataFloor, dataMoves)) + } +} + +func part1(floor [][]byte, moves []byte) (result int) { + botx, boty := findInitialPosition(floor) + + doMoves(moves, botx, boty, floor, doMovePart1) + + for y, line := range floor { + for x, c := range line { + if c == 'O' { + result += y*100 + x + } + } + } + + return +} + +func doMovePart1(floor [][]byte, startx, starty int, dx, dy int) (endx, endy int) { + x := startx + y := starty + moved := 0 + for { + moved += 1 + x += dx + y += dy + + if floor[y][x] == '#' { + return startx, starty + } + + if floor[y][x] == '.' { + if moved > 1 { + floor[y][x] = 'O' + } + floor[starty+dy][startx+dx] = '@' + floor[starty][startx] = '.' + return startx + dx, starty + dy + } + } +} + +func doMoves(moves []byte, botx int, boty int, floor [][]byte, doMoveFn func([][]byte, int, int, int, int) (int, int)) { + for _, move := range moves { + switch move { + case '^': + botx, boty = doMoveFn(floor, botx, boty, 0, -1) + case 'v': + botx, boty = doMoveFn(floor, botx, boty, 0, 1) + case '<': + botx, boty = doMoveFn(floor, botx, boty, -1, 0) + case '>': + botx, boty = doMoveFn(floor, botx, boty, 1, 0) + } + } +} + +func findInitialPosition(floor [][]byte) (int, int) { + var botx, boty int + for y, line := range floor { + idx := strings.IndexByte(string(line), '@') + if idx >= 0 { + botx = idx + boty = y + break + } + } + return botx, boty +} + +type QueuedMove struct { + x, y int + c byte +} + +func part2(floor [][]byte, moves []byte) (result int) { + var botx, boty int + for y, line := range floor { + idx := strings.IndexByte(string(line), '@') + if idx >= 0 { + botx = idx + boty = y + break + } + } + + for _, move := range moves { + var queued []QueuedMove + var dx, dy int + switch move { + case '^': + dx, dy = 0, -1 + case 'v': + dx, dy = 0, 1 + case '<': + dx, dy = -1, 0 + case '>': + dx, dy = 1, 0 + default: + continue + } + + if doMovePart2(floor, botx+dx, boty+dy, dx, dy, &queued) { + hasMadeMove := make(map[QueuedMove]bool) + for i := range queued { + m := queued[len(queued)-1-i] + _, alreadyMade := hasMadeMove[m] + if !alreadyMade { + floor[m.y][m.x] = m.c + floor[m.y-dy][m.x-dx] = '.' + hasMadeMove[m] = true + } + } + + floor[boty][botx] = '.' + botx += dx + boty += dy + floor[boty][botx] = '@' + } + + if !checkIntegrity(floor) { + panic("no") + } + } + + for y, line := range floor { + for x, c := range line { + if c == '[' { + result += y*100 + x + } + } + } + + return +} + +func printMap(floor [][]byte) { + for _, line := range floor { + fmt.Println(string(line)) + } +} + +func checkIntegrity(floor [][]byte) bool { + for _, line := range floor { + for x, c := range line { + if c == '[' && line[x+1] != ']' { + return false + } + } + } + return true +} + +func enqueueMove(x, y int, c byte, queued *[]QueuedMove) { + *queued = append(*queued, QueuedMove{x, y, c}) +} + +func doMovePart2(floor [][]byte, x, y int, dx, dy int, queued *[]QueuedMove) bool { + switch floor[y][x] { + case '.': + return true + case '#': + return false + case '[': + if dx == 0 { + enqueueMove(x, y+dy, '[', queued) + enqueueMove(x+1, y+dy, ']', queued) + return doMovePart2(floor, x, y+dy, dx, dy, queued) && doMovePart2(floor, x+1, y+dy, dx, dy, queued) + } else if dx > 0 { + enqueueMove(x+1, y, '[', queued) + enqueueMove(x+2, y, ']', queued) + return doMovePart2(floor, x+2, y, dx, dy, queued) + } + case ']': + if dx == 0 { + enqueueMove(x-1, y+dy, '[', queued) + enqueueMove(x, y+dy, ']', queued) + return doMovePart2(floor, x-1, y+dy, dx, dy, queued) && doMovePart2(floor, x, y+dy, dx, dy, queued) + } else if dx < 0 { + enqueueMove(x-1, y, ']', queued) + enqueueMove(x-2, y, '[', queued) + return doMovePart2(floor, x-2, y, dx, dy, queued) + } + } + return false +} + +func makeWide(floor [][]byte) (wide [][]byte) { + wide = make([][]byte, len(floor)) + for y, line := range floor { + w := make([]byte, len(line)*2) + for i, c := range line { + switch c { + case '#': + w[i*2+0] = '#' + w[i*2+1] = '#' + case '.': + w[i*2+0] = '.' + w[i*2+1] = '.' + case 'O': + w[i*2+0] = '[' + w[i*2+1] = ']' + case '@': + w[i*2+0] = '@' + w[i*2+1] = '.' + } + } + wide[y] = w + } + return +} + +func readFloor(fileName string) (floor [][]byte) { + fp, _ := os.Open(fileName) + scanner := bufio.NewScanner(fp) + for scanner.Scan() { + floor = append(floor, []byte(strings.TrimSpace(scanner.Text()))) + } + return +} |