Files
advent2022/solutions/day17/day17.go
2022-12-23 19:45:56 -08:00

302 lines
6.8 KiB
Go

package day17
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
type Piece []string
var horizontalBar Piece = Piece{"@@@@"}
var plus Piece = Piece{".@.", "@@@", ".@."}
var backwardsL Piece = Piece{"..@", "..@", "@@@"}
var verticalBar Piece = Piece{"@", "@", "@", "@"}
var block Piece = Piece{"@@", "@@"}
var pieces []Piece = []Piece{horizontalBar, plus, backwardsL, verticalBar, block}
type Board [][]rune
func newBoard() Board {
return Board{}
}
func pieceToRows(piece Piece) Board {
retval := Board{}
for i := range piece {
newRow := append([]rune(".."), []rune(piece[i])...)
for len(newRow) < 7 {
newRow = append(newRow, '.')
}
retval = append(retval, newRow)
}
return retval
}
func rowEmpty(row int, board Board) bool {
if row >= len(board) {
return false
}
for i := 0; i < 7; i++ {
if board[row][i] != '.' {
return false
}
}
return true
}
func addPiece(piece Piece, board *Board) {
rows := pieceToRows(piece)
for !(rowEmpty(0, *board) && rowEmpty(1, *board) && rowEmpty(2, *board)) {
*board = append(Board{[]rune(".......")}, *board...)
}
for len(*board) >= 3 && rowEmpty(3, *board) {
*board = (*board)[1:]
}
*board = append(rows, *board...)
}
func printBoard(board *Board) {
for _, row := range *board {
fmt.Printf("|")
for _, r := range row {
fmt.Printf("%c", r)
}
fmt.Printf("|\n")
}
fmt.Printf("+-------+\n")
}
func copyBoard(clearPiece bool, currentBoard *Board, newBoard *Board) {
for len(*newBoard) < len(*currentBoard) {
*newBoard = append(Board{[]rune(".......")}, *newBoard...)
}
for len(*newBoard) > len(*currentBoard) {
*newBoard = (*newBoard)[1:]
}
for y, row := range *currentBoard {
for x, value := range row {
if clearPiece && value == '@' {
(*newBoard)[y][x] = '.'
} else {
(*newBoard)[y][x] = (*currentBoard)[y][x]
}
}
}
}
func movePiece(direction rune, currentBoard *Board, newBoard *Board) bool {
/// match the values
copyBoard(true, currentBoard, newBoard)
for y, row := range *currentBoard {
for x, value := range row {
if value == '@' {
newX := x
newY := y
switch direction {
case '<':
newX = x - 1
case '>':
newX = x + 1
case 'v':
newY = y + 1
default:
fmt.Printf("PANIC: Invalid direction %c\n", direction)
return false
}
if newX < 0 || newX >= 7 || newY >= len(*currentBoard) {
return false
}
if (*currentBoard)[newY][newX] == '#' {
return false
}
(*newBoard)[newY][newX] = '@'
}
}
}
return true
}
func freezeBoard(board *Board) int {
for _, row := range *board {
for x, value := range row {
if value == '@' {
row[x] = '#'
}
}
}
for y := 0; y < len(*board)-3; y++ {
allCovered := true
for x := 0; x < 7; x++ {
allCovered = allCovered && ((*board)[y][x] == '#' || (*board)[y+1][x] == '#' || (*board)[y+2][x] == '#' || (*board)[y+3][x] == '#')
}
if allCovered {
originalLength := len(*board)
//fmt.Println("--- original board ---")
//printBoard(board)
*board = (*board)[0 : y+4]
//fmt.Println("--- cleaned board ---")
//printBoard(board)
//fmt.Println("---", originalLength-len(*board), "rows removed ---")
removedRows := originalLength - len(*board)
//if removedRows > 0 {
// fmt.Println("Removed", removedRows, "rows.")
//}
return removedRows
}
}
return 0
}
func sameBoard(board1 Board, board2 Board) bool {
if len(board1) != len(board2) {
return false
}
for y, row := range board1 {
for x := range row {
if board1[y][x] != board2[y][x] {
return false
}
}
}
return true
}
func duplicateBoard(board Board) Board {
newBoard := Board{}
for _, row := range board {
newRow := []rune(".......")
copy(newRow, row)
newBoard = append(newBoard, newRow)
}
return newBoard
}
type PreviousState struct {
moveIndex int
pieceIndex int
}
type PreviousLocation struct {
dropNo int
removedRows int
board Board
}
func Run(filename string, targetStr string) {
targetPieces, err := strconv.Atoi(targetStr)
if err != nil {
fmt.Println("PANIC: Don't understand target number")
return
}
file, err := os.Open(filename)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
instructions := []rune{}
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimFunc(line, func(r rune) bool {
return r != '>' && r != '<'
})
instructions = append(instructions, []rune(line)...)
}
file.Close()
board1 := newBoard()
board2 := newBoard()
moveIndex := 0
pieceIndex := 0
previousStates := map[PreviousState][]PreviousLocation{}
removedRows := 0
skippedAhead := false
for dropNo := 1; dropNo <= targetPieces; dropNo++ {
//fmt.Println("Dropping piece", dropNo, "with piece", pieceIndex, "and move", moveIndex)
if !skippedAhead {
state := PreviousState{moveIndex, pieceIndex}
previousLocations, previousStateExisted := previousStates[state]
if previousStateExisted {
for _, location := range previousLocations {
if sameBoard(board1, location.board) {
dropDifference := dropNo - location.dropNo
removedRowsDifference := removedRows - location.removedRows
fmt.Println("Got it, current drop", dropNo, "previous version", location.dropNo, "removed row diff", removedRowsDifference)
repeats := (targetPieces - dropNo) / dropDifference
fmt.Println("Drop difference of", dropDifference, "meaning we can do this gap", repeats, "more times")
fmt.Println("removed rows", removedRows, "+", repeats*removedRowsDifference, "=", removedRows+(repeats*removedRowsDifference))
removedRows += repeats * removedRowsDifference
fmt.Println("Drop number", dropNo, "+", repeats*dropDifference, "=", dropNo+(repeats*dropDifference))
dropNo += repeats*dropDifference - 1
skippedAhead = true
break
}
}
if skippedAhead {
continue
} else {
previousStates[state] = append(previousStates[state], PreviousLocation{dropNo, removedRows, duplicateBoard(board1)})
}
} else {
previousStates[state] = []PreviousLocation{{dropNo, removedRows, duplicateBoard(board1)}}
}
}
addPiece(pieces[pieceIndex], &board1)
for {
//fmt.Println("Move", moveIndex, "is", string(instructions[moveIndex]))
if !movePiece(instructions[moveIndex], &board1, &board2) {
copyBoard(false, &board1, &board2)
}
moveIndex = (moveIndex + 1) % len(instructions)
if !movePiece('v', &board2, &board1) {
copyBoard(false, &board2, &board1)
break
}
}
removedRows += freezeBoard(&board1)
pieceIndex = (pieceIndex + 1) % len(pieces)
}
//printBoard(&board1)
fmt.Println("Removed", removedRows, "rows.")
rowsWithRocks := removedRows
for i := 0; i < len(board1); i++ {
if !rowEmpty(i, board1) {
rowsWithRocks += 1
}
}
fmt.Println("Rocks are", rowsWithRocks, "rows tall.")
}