249 lines
6.4 KiB
Go
249 lines
6.4 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"AOC2022/helper"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
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("day17/input")
|
||
|
field := Field{createField(), Block{}, 0}
|
||
|
field.addBlock()
|
||
|
fieldCopy := Field{createField(), Block{}, 0}
|
||
|
fieldCopy.addBlock()
|
||
|
heightForBlocks := part1(fieldCopy, lines)
|
||
|
currentPrefix := 0
|
||
|
currentCycle := len(lines[0])
|
||
|
finished := false
|
||
|
for !finished {
|
||
|
if currentPrefix < currentCycle {
|
||
|
currentPrefix++
|
||
|
fmt.Println(currentPrefix)
|
||
|
} else {
|
||
|
fmt.Println(currentCycle)
|
||
|
currentCycle += len(lines[0])
|
||
|
}
|
||
|
fieldCopy = Field{createField(), Block{}, 0}
|
||
|
fieldCopy.addBlock()
|
||
|
finished = checkCycle(currentPrefix, currentCycle, fieldCopy, lines)
|
||
|
}
|
||
|
fmt.Println(currentCycle)
|
||
|
|
||
|
part2(field, lines)
|
||
|
|
||
|
}
|
||
|
|
||
|
func checkCycle(prefix, cycle int, field Field, lines []string) bool {
|
||
|
currentJetMove := 0
|
||
|
values := make(map[int]int)
|
||
|
for field.spawnedBlocks != 4*cycle+prefix {
|
||
|
field.move(rune(lines[0][currentJetMove]))
|
||
|
if (field.spawnedBlocks-prefix)%cycle == 0 {
|
||
|
values[field.spawnedBlocks] = len(field.field) - field.getLineWithHighestRock()
|
||
|
}
|
||
|
currentJetMove++
|
||
|
if currentJetMove == len(lines[0]) {
|
||
|
currentJetMove = 0
|
||
|
}
|
||
|
}
|
||
|
cycleHeight := values[cycle+prefix] - values[prefix]
|
||
|
for i := prefix; i < 3*cycle+prefix; i += cycle {
|
||
|
if !(values[i+cycle]-values[i] == cycleHeight) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func part2(field Field, lines []string) {
|
||
|
prefix := 25
|
||
|
cycle := 35
|
||
|
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) {
|
||
|
currentJetMove := 0
|
||
|
for field.spawnedBlocks != 2023 {
|
||
|
field.move(rune(lines[0][currentJetMove]))
|
||
|
heightForBlocks[field.spawnedBlocks] = len(field.field) - field.getLineWithHighestRock()
|
||
|
currentJetMove++
|
||
|
if currentJetMove == len(lines[0]) {
|
||
|
currentJetMove = 0
|
||
|
}
|
||
|
}
|
||
|
fmt.Printf("%v : %v \n", field.spawnedBlocks, (len(field.field) - field.getLineWithHighestRock()))
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
}
|