201 lines
3.7 KiB
Go
201 lines
3.7 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
DIR_N = -1i
|
||
|
DIR_NE = 1 - 1i
|
||
|
DIR_E = 1
|
||
|
DIR_SE = 1 + 1i
|
||
|
DIR_S = 1i
|
||
|
DIR_SW = -1 + 1i
|
||
|
DIR_W = -1
|
||
|
DIR_NW = -1 - 1i
|
||
|
|
||
|
CLEAR_SCREEN = "\033[H\033[2J"
|
||
|
)
|
||
|
|
||
|
var scan []byte
|
||
|
var next []byte
|
||
|
var prevScans [][]byte
|
||
|
var width, height int
|
||
|
|
||
|
func main() {
|
||
|
stdinToByteSlice()
|
||
|
part1()
|
||
|
part2()
|
||
|
}
|
||
|
|
||
|
func part1() {
|
||
|
printScan()
|
||
|
for i := 0; i < 10; i++ {
|
||
|
fmt.Print(CLEAR_SCREEN)
|
||
|
scan = tickToNext()
|
||
|
printScan()
|
||
|
time.Sleep(time.Millisecond * 250)
|
||
|
}
|
||
|
var ttlOpen, ttlTrees, ttlLmbr int
|
||
|
for i := range scan {
|
||
|
switch getByte(getPosFromInt(i)) {
|
||
|
case '.':
|
||
|
ttlOpen++
|
||
|
case '|':
|
||
|
ttlTrees++
|
||
|
case '#':
|
||
|
ttlLmbr++
|
||
|
}
|
||
|
}
|
||
|
_ = ttlOpen
|
||
|
fmt.Println("= Part 1 =")
|
||
|
fmt.Println(ttlTrees * ttlLmbr)
|
||
|
}
|
||
|
|
||
|
func part2() {
|
||
|
var isDupe bool
|
||
|
var i int
|
||
|
target := 1000000000
|
||
|
for i = 0; i < target; i++ {
|
||
|
next := tickToNext()
|
||
|
if isDupe, prevScans = checkDuplicateState(next, prevScans); isDupe {
|
||
|
i++
|
||
|
scan = next
|
||
|
break
|
||
|
}
|
||
|
prevScans = append(prevScans, next)
|
||
|
scan = next
|
||
|
}
|
||
|
// We need to find the state after `target` increments
|
||
|
scan = prevScans[(target-i)%len(prevScans)]
|
||
|
var ttlOpen, ttlTrees, ttlLmbr int
|
||
|
for i := range scan {
|
||
|
switch getByte(getPosFromInt(i)) {
|
||
|
case '.':
|
||
|
ttlOpen++
|
||
|
case '|':
|
||
|
ttlTrees++
|
||
|
case '#':
|
||
|
ttlLmbr++
|
||
|
}
|
||
|
}
|
||
|
_ = ttlOpen
|
||
|
fmt.Println("= Part 2 =")
|
||
|
fmt.Println(ttlTrees * ttlLmbr)
|
||
|
}
|
||
|
|
||
|
// checkDuplicateState returns the slice of prev that has all remaining available states
|
||
|
func checkDuplicateState(s []byte, prev [][]byte) (bool, [][]byte) {
|
||
|
for i, v := range prev {
|
||
|
if areasAreEqual(s, v) {
|
||
|
return true, prev[i:]
|
||
|
}
|
||
|
}
|
||
|
return false, prev
|
||
|
}
|
||
|
|
||
|
func areasAreEqual(a1, a2 []byte) bool {
|
||
|
for i := range a1 {
|
||
|
if a1[i] != a2[i] {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func tickToNext() []byte {
|
||
|
var ret []byte
|
||
|
for i := 0; i < len(scan); i++ {
|
||
|
c := getPosFromInt(i)
|
||
|
_, sTree, sLmbr := getSurroundingCounts(c)
|
||
|
b := getByte(c)
|
||
|
switch b {
|
||
|
case '.':
|
||
|
if sTree >= 3 {
|
||
|
ret = append(ret, '|')
|
||
|
} else {
|
||
|
ret = append(ret, '.')
|
||
|
}
|
||
|
case '|':
|
||
|
if sLmbr >= 3 {
|
||
|
ret = append(ret, '#')
|
||
|
} else {
|
||
|
ret = append(ret, '|')
|
||
|
}
|
||
|
case '#':
|
||
|
if sLmbr > 0 && sTree > 0 {
|
||
|
ret = append(ret, '#')
|
||
|
} else {
|
||
|
ret = append(ret, '.')
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
// getSurroundingCounts takes a pos and returns:
|
||
|
// Number of open areas
|
||
|
// Number of trees
|
||
|
// Number of lumberyards
|
||
|
// Surrounding that spot
|
||
|
func getSurroundingCounts(c complex64) (int, int, int) {
|
||
|
var sOpen, sTree, sLmbr int
|
||
|
for _, v := range []complex64{DIR_NW, DIR_N, DIR_NE, DIR_W, DIR_E, DIR_SW, DIR_S, DIR_SE} {
|
||
|
switch getByte(c + v) {
|
||
|
case '.':
|
||
|
sOpen++
|
||
|
case '|':
|
||
|
sTree++
|
||
|
case '#':
|
||
|
sLmbr++
|
||
|
}
|
||
|
}
|
||
|
return sOpen, sTree, sLmbr
|
||
|
}
|
||
|
|
||
|
func printScan() {
|
||
|
for i := 0; i < len(scan)/width; i++ {
|
||
|
fmt.Println(string(scan[i*width : (i+1)*width]))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// getByte pulls a byte from the given position in the scan
|
||
|
func getByte(pos complex64) byte {
|
||
|
//idx := int(real(pos)) + int(imag(pos))*width
|
||
|
if int(real(pos)) < 0 || int(imag(pos)) < 0 || int(real(pos)) >= width || int(imag(pos)) >= height {
|
||
|
return 0
|
||
|
}
|
||
|
return scan[int(real(pos))+int(imag(pos))*width]
|
||
|
}
|
||
|
|
||
|
func getPosComplex(x, y int) complex64 {
|
||
|
return complex(float32(x), float32(y))
|
||
|
}
|
||
|
|
||
|
func getPosFromInt(i int) complex64 {
|
||
|
return complex(float32(i%width), float32(i/width))
|
||
|
}
|
||
|
|
||
|
func getIdxFromPos(pos complex64) int {
|
||
|
return int(real(pos)) + int(imag(pos))*width
|
||
|
}
|
||
|
|
||
|
func getCoordString(p complex64) string {
|
||
|
return fmt.Sprintf("(%d,%d [%d])", int(real(p)), int(imag(p)), getIdxFromPos(p))
|
||
|
}
|
||
|
|
||
|
func stdinToByteSlice() {
|
||
|
scanner := bufio.NewScanner(os.Stdin)
|
||
|
for scanner.Scan() {
|
||
|
s := scanner.Bytes()
|
||
|
if width == 0 {
|
||
|
width = len(s)
|
||
|
}
|
||
|
scan = append(scan, s...)
|
||
|
height++
|
||
|
}
|
||
|
}
|