241 lines
6.2 KiB
Go
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()
|
|
}
|