🌅
This commit is contained in:
301
solutions/day17/day17.go
Normal file
301
solutions/day17/day17.go
Normal file
@@ -0,0 +1,301 @@
|
||||
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.")
|
||||
}
|
||||
Reference in New Issue
Block a user