2022-12-18 03:11:23 +01:00
package main
import (
"AOC2022/helper"
"fmt"
2022-12-18 03:28:01 +01:00
"os"
2022-12-18 03:11:23 +01:00
)
type Field struct {
field [ ] [ ] rune
currentBlock Block
spawnedBlocks int
}
type Block struct {
form [ ] [ 2 ] int
position [ 2 ] int
}
func main ( ) {
2022-12-18 03:28:01 +01:00
args := os . Args [ 1 : ]
lines := helper . ReadTextFile ( args [ 0 ] )
2022-12-18 03:11:23 +01:00
field := Field { createField ( ) , Block { } , 0 }
field . addBlock ( )
fieldCopy := Field { createField ( ) , Block { } , 0 }
fieldCopy . addBlock ( )
heightForBlocks := part1 ( fieldCopy , lines )
currentPrefix := 0
2022-12-18 03:28:01 +01:00
currentCycle := 1
2022-12-18 03:11:23 +01:00
finished := false
for ! finished {
if currentPrefix < currentCycle {
currentPrefix ++
} else {
2022-12-18 03:28:01 +01:00
currentPrefix = 0
currentCycle ++
2022-12-18 03:11:23 +01:00
}
fieldCopy = Field { createField ( ) , Block { } , 0 }
fieldCopy . addBlock ( )
2022-12-18 03:28:01 +01:00
finished = checkCycle ( currentPrefix , currentCycle , heightForBlocks )
2022-12-18 03:11:23 +01:00
}
fmt . Println ( currentCycle )
2022-12-18 03:28:01 +01:00
part2 ( currentPrefix , currentCycle , field , lines )
2022-12-18 03:11:23 +01:00
}
2022-12-18 03:28:01 +01:00
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 ) {
2022-12-18 03:11:23 +01:00
return false
}
}
return true
}
2022-12-18 03:28:01 +01:00
func part2 ( prefix , cycle int , field Field , lines [ ] string ) {
2022-12-18 03:11:23 +01:00
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 ) {
2022-12-18 03:28:01 +01:00
heightForBlocks = make ( map [ int ] int )
2022-12-18 03:11:23 +01:00
currentJetMove := 0
2022-12-18 03:28:01 +01:00
solutionPart1 := 0
for field . spawnedBlocks != 50000 {
2022-12-18 03:11:23 +01:00
field . move ( rune ( lines [ 0 ] [ currentJetMove ] ) )
2022-12-18 03:28:01 +01:00
if field . spawnedBlocks == 2023 {
solutionPart1 = len ( field . field ) - field . getLineWithHighestRock ( )
}
2022-12-18 03:11:23 +01:00
heightForBlocks [ field . spawnedBlocks ] = len ( field . field ) - field . getLineWithHighestRock ( )
currentJetMove ++
if currentJetMove == len ( lines [ 0 ] ) {
currentJetMove = 0
}
}
2022-12-18 03:28:01 +01:00
fmt . Printf ( "%v : %v \n" , field . spawnedBlocks , solutionPart1 )
return
2022-12-18 03:11:23 +01:00
}
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 ( )
}