295 lines
6.0 KiB
Go
295 lines
6.0 KiB
Go
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 " "
|
|
}
|