adventofcode/2018/day18/day18.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++
}
}