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() }