AOC2022/day17/day17.go
2022-12-18 03:28:01 +01:00

241 lines
6.2 KiB
Go

package main
import (
"AOC2022/helper"
"fmt"
"os"
)
type Field struct {
field [][]rune
currentBlock Block
spawnedBlocks int
}
type Block struct {
form [][2]int
position [2]int
}
func main() {
args := os.Args[1:]
lines := helper.ReadTextFile(args[0])
field := Field{createField(), Block{}, 0}
field.addBlock()
fieldCopy := Field{createField(), Block{}, 0}
fieldCopy.addBlock()
heightForBlocks := part1(fieldCopy, lines)
currentPrefix := 0
currentCycle := 1
finished := false
for !finished {
if currentPrefix < currentCycle {
currentPrefix++
} else {
currentPrefix = 0
currentCycle++
}
fieldCopy = Field{createField(), Block{}, 0}
fieldCopy.addBlock()
finished = checkCycle(currentPrefix, currentCycle, heightForBlocks)
}
fmt.Println(currentCycle)
part2(currentPrefix, currentCycle, field, lines)
}
func checkCycle(prefix, cycle int, heightForBlocks map[int]int) bool {
cycleHeight := heightForBlocks[cycle+prefix] - heightForBlocks[prefix]
for i := prefix; i < len(heightForBlocks); i += cycle {
if !(heightForBlocks[i+cycle]-heightForBlocks[i] == cycleHeight) {
return false
}
}
return true
}
func part2(prefix, cycle int, field Field, lines []string) {
suffix := (1000000000000 - prefix) % cycle
prefixHeight := 0
cycleHeight := 0
suffixHeight := 0
currentJetMove := 0
for field.spawnedBlocks != 3*cycle+prefix {
field.move(rune(lines[0][currentJetMove]))
if field.spawnedBlocks == prefix+1 {
prefixHeight = (len(field.field) - field.getLineWithHighestRock())
}
if field.spawnedBlocks == prefix+cycle+1 {
cycleHeight = (len(field.field) - field.getLineWithHighestRock()) - prefixHeight
}
if field.spawnedBlocks == cycle+prefix+suffix+1 {
suffixHeight = (len(field.field) - field.getLineWithHighestRock()) - cycleHeight - prefixHeight
}
currentJetMove++
if currentJetMove == len(lines[0]) {
currentJetMove = 0
}
}
fmt.Println(((1000000000000 - prefix) / cycle * cycleHeight) + prefixHeight + suffixHeight)
}
func part1(field Field, lines []string) (heightForBlocks map[int]int) {
heightForBlocks = make(map[int]int)
currentJetMove := 0
solutionPart1 := 0
for field.spawnedBlocks != 50000 {
field.move(rune(lines[0][currentJetMove]))
if field.spawnedBlocks == 2023 {
solutionPart1 = len(field.field) - field.getLineWithHighestRock()
}
heightForBlocks[field.spawnedBlocks] = len(field.field) - field.getLineWithHighestRock()
currentJetMove++
if currentJetMove == len(lines[0]) {
currentJetMove = 0
}
}
fmt.Printf("%v : %v \n", field.spawnedBlocks, solutionPart1)
return
}
func getNewBlock(spawnedBlocks int) Block {
lineBlock := [][2]int{{0, 0}, {0, 1}, {0, 2}, {0, 3}}
crossBlock := [][2]int{{0, 1}, {1, 1}, {1, 0}, {1, 2}, {2, 1}}
lBlock := [][2]int{{2, 0}, {2, 1}, {2, 2}, {1, 2}, {0, 2}}
iBlock := [][2]int{{0, 0}, {1, 0}, {2, 0}, {3, 0}}
squareBlock := [][2]int{{0, 0}, {0, 1}, {1, 1}, {1, 0}}
blocks := [][][2]int{lineBlock, crossBlock, lBlock, iBlock, squareBlock}
blockForm := blocks[(spawnedBlocks)%5]
return Block{blockForm, [2]int{0, 2}}
}
func (field *Field) move(exhaust rune) {
direction := [2]int{0, 0}
switch exhaust {
case '<':
direction[1] = -1
case '>':
direction[1] = +1
}
field.moveBlock(direction)
oldPos := field.currentBlock.position
field.moveBlock([2]int{1, 0})
if oldPos == field.currentBlock.position {
field.writeBlockToField()
field.addBlock()
}
}
func (field *Field) writeBlockToField() {
currentBlockPoints := field.getCurrentBLockPoints()
for _, point := range currentBlockPoints {
field.field[point[0]][point[1]] = '#'
}
}
func (field *Field) getCurrentBLockPoints() [][2]int {
curretnPoints := [][2]int{}
for _, point := range field.currentBlock.form {
blocPoint := [2]int{point[0] + field.currentBlock.position[0], point[1] + field.currentBlock.position[1]}
curretnPoints = append(curretnPoints, blocPoint)
}
return curretnPoints
}
func (field *Field) moveBlock(direction [2]int) {
possibleNewPos := addPosition(field.currentBlock.position, direction)
for _, point := range field.currentBlock.form {
pointToCheck := [2]int{point[0] + possibleNewPos[0], point[1] + possibleNewPos[1]}
if pointToCheck[0] >= len(field.field) {
return
}
if pointToCheck[1] >= len(field.field[pointToCheck[0]]) || pointToCheck[1] < 0 {
return
}
if field.field[pointToCheck[0]][pointToCheck[1]] == '#' {
return
}
}
field.currentBlock.position = possibleNewPos
}
func addPosition(a [2]int, b [2]int) [2]int {
return [2]int{a[0] + b[0], a[1] + b[1]}
}
func (field *Field) addBlock() {
//if (field.spawnedBlocks-25)%35 == 0 {
// fmt.Printf("%v : %v \n", field.spawnedBlocks, (len(field.field) - field.getLineWithHighestRock()))
//}
lineWithHighestRock := field.getLineWithHighestRock()
field.field = field.field[lineWithHighestRock:]
field.currentBlock = getNewBlock(field.spawnedBlocks)
blockHeight := field.currentBlock.getBlockHeight()
field.addLines(3 + blockHeight)
field.spawnedBlocks++
}
func (block Block) getBlockHeight() int {
height := 0
for _, pos := range block.form {
if pos[0] > height {
height = pos[0]
}
}
return height + 1
}
func (field *Field) getLineWithHighestRock() int {
for i, line := range field.field {
for _, elem := range line {
if elem == '#' {
return i
}
}
}
return len(field.field)
}
func (field *Field) addLines(n int) {
newLines := [][]rune{}
for i := 0; i < n; i++ {
fieldLine := []rune{}
for j := 0; j < 7; j++ {
fieldLine = append(fieldLine, '.')
}
newLines = append(newLines, fieldLine)
}
newLines = append(newLines, field.field...)
field.field = newLines
}
func createField() [][]rune {
field := [][]rune{}
for i := 0; i < 4; i++ {
fieldLine := []rune{}
for j := 0; j < 7; j++ {
fieldLine = append(fieldLine, '.')
}
field = append(field, fieldLine)
}
return field
}
func (field Field) printField() {
currentBlockPoints := field.getCurrentBLockPoints()
fieldCopy := make([][]rune, len(field.field))
for i := range field.field {
fieldCopy[i] = make([]rune, len(field.field[i]))
copy(fieldCopy[i], field.field[i])
}
for _, point := range currentBlockPoints {
fieldCopy[point[0]][point[1]] = '@'
}
for i := 0; i < len(fieldCopy); i++ {
fmt.Println(string(fieldCopy[i]))
}
fmt.Println()
}