2022 Day 17 part 1 Complete
This commit is contained in:
294
2022/day17/main.go
Normal file
294
2022/day17/main.go
Normal file
@@ -0,0 +1,294 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
||||
)
|
||||
|
||||
const (
|
||||
dirU = iota
|
||||
dirR
|
||||
dirD
|
||||
dirL
|
||||
|
||||
rocks = 5
|
||||
|
||||
debug = false
|
||||
)
|
||||
|
||||
var (
|
||||
emptyRow = []byte{'|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '|'}
|
||||
bottomRow = []byte{'+', '-', '-', '-', '-', '-', '-', '-', '+'}
|
||||
)
|
||||
|
||||
func main() {
|
||||
inp := h.StdinToString()
|
||||
//fmt.Println("# Part 1")
|
||||
//simulate(inp, 2022)
|
||||
fmt.Println("# Part 2")
|
||||
simulate(inp, 1000000000000)
|
||||
}
|
||||
|
||||
func stateAndSleep(m *h.GrowUpCoordByteMap) {
|
||||
if !debug {
|
||||
return
|
||||
}
|
||||
fmt.Print(h.CLEAR_SCREEN)
|
||||
//fmt.Println()
|
||||
fmt.Println(m)
|
||||
time.Sleep(time.Second / 10)
|
||||
}
|
||||
|
||||
var cache map[string][]int
|
||||
|
||||
func simulate(jets string, numRocks int) {
|
||||
cache = make(map[string][]int)
|
||||
m := h.NewGrowUpCoordByteMap()
|
||||
m.PutBytes([][]byte{
|
||||
bottomRow, emptyRow, emptyRow, emptyRow, emptyRow,
|
||||
emptyRow, emptyRow, emptyRow,
|
||||
}, h.Coordinate{X: 0, Y: 0})
|
||||
m.StringEmptyIsSpace = true
|
||||
|
||||
jetIdx := 0
|
||||
rockType := 0
|
||||
var state string
|
||||
var height int
|
||||
var turboHeight int
|
||||
var cacheDisabled bool
|
||||
for rockNum := 1; rockNum <= numRocks; rockNum++ {
|
||||
fmt.Println(h.CLEAR_SCREEN)
|
||||
fmt.Println("Simulating:", rockNum, "/", numRocks)
|
||||
h.PrintProgress(rockNum, numRocks)
|
||||
fmt.Println("\nHeight:", height)
|
||||
if !cacheDisabled {
|
||||
state, height = GetState(rockType, jetIdx, m)
|
||||
if v, ok := cache[state]; !cacheDisabled && ok {
|
||||
// Ok, we've got a duplicate. Go full turbo.
|
||||
//addHeight := height
|
||||
mult := numRocks / rockNum
|
||||
newRockNum := rockNum * mult
|
||||
turboHeight = height * mult
|
||||
/*
|
||||
for rockNum+v[1] <= numRocks {
|
||||
rockNum = rockNum + v[1]
|
||||
height = height + addHeight
|
||||
fmt.Println(h.CLEAR_SCREEN)
|
||||
fmt.Println("Simulating:", rockNum, "/", numRocks)
|
||||
h.PrintProgress(rockNum, numRocks)
|
||||
fmt.Println("\nHeight:", height)
|
||||
}
|
||||
*/
|
||||
fmt.Println(h.CLEAR_SCREEN)
|
||||
fmt.Println("Simulating:", rockNum, "/", numRocks)
|
||||
h.PrintProgress(rockNum, numRocks)
|
||||
fmt.Println("\nHeight:", height)
|
||||
fmt.Println("State", state, "\nV:", v, "\nMult:", mult, "\nRockNum:", rockNum, "\nNewRockNum:", newRockNum, "\nTurboHeight:", turboHeight)
|
||||
cacheDisabled = true
|
||||
os.Exit(0)
|
||||
} else {
|
||||
cache[state] = []int{rockNum, height}
|
||||
}
|
||||
} else {
|
||||
fmt.Println("\nHeight:", turboHeight+GetHeight(m))
|
||||
}
|
||||
AddFallingRock(rockType, m)
|
||||
stateAndSleep(m)
|
||||
doJet := true
|
||||
for {
|
||||
if !doJet && AtRest(m) {
|
||||
break
|
||||
}
|
||||
shiftDir := dirD
|
||||
if doJet {
|
||||
switch jets[jetIdx] {
|
||||
case '>':
|
||||
shiftDir = dirR
|
||||
case '<':
|
||||
shiftDir = dirL
|
||||
}
|
||||
jetIdx = (jetIdx + 1) % len(jets)
|
||||
}
|
||||
ShiftRock(shiftDir, m)
|
||||
stateAndSleep(m)
|
||||
doJet = !doJet
|
||||
}
|
||||
// The falling rock has stopped
|
||||
StopRock(m)
|
||||
stateAndSleep(m)
|
||||
rockType = (rockType + 1) % rocks
|
||||
}
|
||||
fmt.Println("After", numRocks, "the tower is", GetHeight(m), "blocks tall")
|
||||
}
|
||||
|
||||
func GetState(r, j int, m *h.GrowUpCoordByteMap) (string, int) {
|
||||
ret := fmt.Sprintf("%d;%d;", r, j)
|
||||
for x := 1; x < 7; x++ {
|
||||
for y := m.TLY; y > 1; y-- {
|
||||
if m.Get(h.Coordinate{X: x, Y: y}) == '#' {
|
||||
ret = fmt.Sprintf("%s-%d", ret, m.TLY-y)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, GetHeight(m)
|
||||
}
|
||||
|
||||
func GetHeight(m *h.GrowUpCoordByteMap) int {
|
||||
return h.GetHighestY(m.FindAll('#')...)
|
||||
}
|
||||
|
||||
func FindTopAtRestY(m *h.GrowUpCoordByteMap) int {
|
||||
rockSpots := m.FindAll('#')
|
||||
if len(rockSpots) != 0 {
|
||||
return h.GetHighestY(rockSpots...)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func StopRock(m *h.GrowUpCoordByteMap) {
|
||||
rockSpots := m.FindAll('@')
|
||||
for i := range rockSpots {
|
||||
m.Put(rockSpots[i], '#')
|
||||
}
|
||||
}
|
||||
func ShiftRock(dir int, m *h.GrowUpCoordByteMap) {
|
||||
if !CanShift(dir, m) {
|
||||
return
|
||||
}
|
||||
|
||||
rockSpots := m.FindAll('@')
|
||||
for i := range rockSpots {
|
||||
m.Put(rockSpots[i], ' ')
|
||||
}
|
||||
for i := range rockSpots {
|
||||
switch dir {
|
||||
case dirU:
|
||||
m.Put(GetUp(rockSpots[i]), '@')
|
||||
case dirR:
|
||||
m.Put(GetRight(rockSpots[i]), '@')
|
||||
case dirD:
|
||||
m.Put(GetDown(rockSpots[i]), '@')
|
||||
case dirL:
|
||||
m.Put(GetLeft(rockSpots[i]), '@')
|
||||
}
|
||||
}
|
||||
}
|
||||
func CanShift(dir int, m *h.GrowUpCoordByteMap) bool {
|
||||
rockSpots := m.FindAll('@')
|
||||
switch dir {
|
||||
case dirU: // Shouldn't need to, though.
|
||||
return true
|
||||
case dirR:
|
||||
for i := range rockSpots {
|
||||
tst := m.Get(GetRight(rockSpots[i]))
|
||||
if tst != '@' && tst != ' ' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case dirD:
|
||||
for i := range rockSpots {
|
||||
tst := m.Get(GetDown(rockSpots[i]))
|
||||
if tst != '@' && tst != ' ' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case dirL:
|
||||
for i := range rockSpots {
|
||||
tst := m.Get(GetLeft(rockSpots[i]))
|
||||
if tst != '@' && tst != ' ' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func AddFallingRock(tp int, m *h.GrowUpCoordByteMap) {
|
||||
rock := GetRockBytes(tp)
|
||||
pos := h.Coordinate{X: 3, Y: FindTopAtRestY(m) + 4}
|
||||
for pos.Y+3 > m.TLY {
|
||||
m.PutBytes([][]byte{emptyRow}, h.Coordinate{X: 0, Y: m.TLY + 1})
|
||||
}
|
||||
m.PutBytes(rock, pos)
|
||||
}
|
||||
func AtRest(m *h.GrowUpCoordByteMap) bool {
|
||||
rockSpots := m.FindAll('@')
|
||||
if len(rockSpots) == 0 {
|
||||
return true
|
||||
}
|
||||
for i := range rockSpots {
|
||||
pos := GetDown(rockSpots[i])
|
||||
wrk := m.Get(pos)
|
||||
if pos.Y == m.BRY {
|
||||
return true
|
||||
}
|
||||
if wrk != ' ' && wrk != '@' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetRockBytes(tp int) [][]byte {
|
||||
switch tp {
|
||||
case 0:
|
||||
return [][]byte{{'@', '@', '@', '@'}}
|
||||
case 1:
|
||||
return [][]byte{
|
||||
{' ', '@', ' '},
|
||||
{'@', '@', '@'},
|
||||
{' ', '@', ' '},
|
||||
}
|
||||
case 2:
|
||||
return [][]byte{
|
||||
{'@', '@', '@'},
|
||||
{' ', ' ', '@'},
|
||||
{' ', ' ', '@'},
|
||||
}
|
||||
case 3:
|
||||
return [][]byte{
|
||||
{'@'},
|
||||
{'@'},
|
||||
{'@'},
|
||||
{'@'},
|
||||
}
|
||||
case 4:
|
||||
return [][]byte{
|
||||
{'@', '@'},
|
||||
{'@', '@'},
|
||||
}
|
||||
}
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
func GetLeft(c h.Coordinate) h.Coordinate {
|
||||
return h.Coordinate{X: c.X - 1, Y: c.Y}
|
||||
}
|
||||
func GetRight(c h.Coordinate) h.Coordinate {
|
||||
return h.Coordinate{X: c.X + 1, Y: c.Y}
|
||||
}
|
||||
func GetUp(c h.Coordinate) h.Coordinate {
|
||||
return h.Coordinate{X: c.X, Y: c.Y + 1}
|
||||
}
|
||||
func GetDown(c h.Coordinate) h.Coordinate {
|
||||
return h.Coordinate{X: c.X, Y: c.Y - 1}
|
||||
}
|
||||
func DirToString(shiftDir int) string {
|
||||
switch shiftDir {
|
||||
case dirU:
|
||||
return "^"
|
||||
case dirR:
|
||||
return ">"
|
||||
case dirD:
|
||||
return "v"
|
||||
case dirL:
|
||||
return "<"
|
||||
}
|
||||
return " "
|
||||
}
|
||||
Reference in New Issue
Block a user