275 lines
4.2 KiB
Go
275 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
MaxInt = int(^uint(0) >> 1)
|
|
MinInt = -MaxInt - 1
|
|
)
|
|
|
|
var lastState string
|
|
var garden *Garden
|
|
|
|
func main() {
|
|
inp := StdinToStringSlice()
|
|
garden = NewGarden(strings.Trim(inp[0], "intalsae: "), 0)
|
|
for _, v := range inp[2:] {
|
|
garden.transitions = append(garden.transitions, NewTransition(v))
|
|
}
|
|
generations := 50000000000
|
|
var i int
|
|
for i = 0; i < generations; i++ {
|
|
lastState = garden.string()
|
|
garden = garden.tick()
|
|
if garden.string() == lastState {
|
|
i++
|
|
break
|
|
}
|
|
}
|
|
garden.shiftPots(generations - i)
|
|
fmt.Println(garden.sum())
|
|
}
|
|
|
|
/**
|
|
* A Garden
|
|
*/
|
|
type Garden struct {
|
|
pots []*Pot
|
|
transitions []*Transition
|
|
}
|
|
|
|
func NewGarden(inp string, start int) *Garden {
|
|
g := &Garden{}
|
|
for k, v := range inp {
|
|
p := &Pot{
|
|
id: k + start,
|
|
value: rb(v),
|
|
}
|
|
g.pots = append(g.pots, p)
|
|
}
|
|
return g
|
|
}
|
|
|
|
func (g *Garden) shiftPots(val int) {
|
|
for i := range g.pots {
|
|
g.pots[i].id = g.pots[i].id + val
|
|
}
|
|
}
|
|
|
|
func (g *Garden) sum() int64 {
|
|
var ret int64
|
|
st, ed := g.getStartIndex(), g.getEndIndex()
|
|
for i := st; i <= ed; i++ {
|
|
if g.getPot(i).value {
|
|
ret += int64(i)
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (g *Garden) tick() *Garden {
|
|
earliest, latest := g.getStartIndex(), g.getEndIndex()
|
|
st := earliest - 2
|
|
ed := latest + 2
|
|
ret := &Garden{
|
|
transitions: g.transitions,
|
|
}
|
|
for i := st; i <= ed; i++ {
|
|
next := g.getNextStateForPot(i)
|
|
if next {
|
|
ret.pots = append(ret.pots, &Pot{
|
|
id: i,
|
|
value: next,
|
|
})
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (g *Garden) getNextStateForPot(id int) bool {
|
|
var ret byte
|
|
for i := id - 2; i <= id+2; i++ {
|
|
ret = ret << 1
|
|
if g.getPot(i).value {
|
|
ret = ret | 1
|
|
}
|
|
}
|
|
for _, v := range g.transitions {
|
|
if v.GetValue() == ret {
|
|
return v.next
|
|
}
|
|
}
|
|
return false //g.getPot(id).value
|
|
}
|
|
|
|
func (g *Garden) substring(st, ed int) string {
|
|
var ret string
|
|
for i := st; i <= ed; i++ {
|
|
ret += g.getPot(i).string()
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (g *Garden) string() string {
|
|
var ret string
|
|
for i := g.getStartIndex(); i <= g.getEndIndex(); i++ {
|
|
ret += g.getPot(i).string()
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (g *Garden) transitionStrings() string {
|
|
var ret string
|
|
for _, v := range g.transitions {
|
|
ret = ret + v.string() + "\n"
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (g *Garden) getStartIndex() int {
|
|
min := MaxInt
|
|
for _, v := range g.pots {
|
|
if v.id < min {
|
|
min = v.id
|
|
}
|
|
}
|
|
return min
|
|
}
|
|
|
|
func (g *Garden) getEndIndex() int {
|
|
max := MinInt
|
|
for _, v := range g.pots {
|
|
if v.id > max {
|
|
max = v.id
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
|
|
func (g *Garden) getPot(idx int) *Pot {
|
|
for _, v := range g.pots {
|
|
if v.id == idx {
|
|
return v
|
|
}
|
|
}
|
|
return &Pot{
|
|
id: idx,
|
|
value: false,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A Pot
|
|
*/
|
|
type Pot struct {
|
|
id int
|
|
value bool
|
|
}
|
|
|
|
func (p *Pot) string() string {
|
|
return bs(p.value)
|
|
}
|
|
|
|
/**
|
|
* Transitions
|
|
*/
|
|
type Transition struct {
|
|
state []bool
|
|
next bool
|
|
}
|
|
|
|
func NewTransition(inp string) *Transition {
|
|
var state []bool
|
|
var next bool
|
|
pts := strings.Split(inp, " => ")
|
|
for i := range pts[0] {
|
|
state = append(state, bb(pts[0][i]))
|
|
}
|
|
//for i := len(pts[0]) - 1; i >= 0; i-- {
|
|
// state = append(state, bb(pts[0][i]))
|
|
//}
|
|
next = bb(pts[1][0])
|
|
t := &Transition{
|
|
state: state,
|
|
next: next,
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (t *Transition) string() string {
|
|
var ret string
|
|
for _, v := range t.state {
|
|
ret += bs(v)
|
|
}
|
|
return ret + " => " + bs(t.next)
|
|
}
|
|
|
|
func (t *Transition) GetValue() byte {
|
|
var ret byte
|
|
for _, v := range t.state {
|
|
ret = ret << 1
|
|
if v {
|
|
ret = ret | 1
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
/**
|
|
* Helper Functions
|
|
*/
|
|
|
|
// Take a byte and return a string representation of the bits
|
|
func byteToString(b byte) string {
|
|
var ret string
|
|
for i := 0; i < 8; i++ {
|
|
if b&1 == 1 {
|
|
ret += "#"
|
|
} else {
|
|
ret += "."
|
|
}
|
|
b = b >> 1
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// Take a string representation of a bits and return the byte
|
|
func stringToByte(n string) byte {
|
|
var b byte
|
|
for i := range n {
|
|
if n[i] == '#' {
|
|
b = b | 1
|
|
}
|
|
b = b << 1
|
|
}
|
|
return b
|
|
}
|
|
|
|
func bs(b bool) string {
|
|
if b {
|
|
return "#"
|
|
}
|
|
return "."
|
|
}
|
|
|
|
func rb(r rune) bool {
|
|
return bb(byte(r))
|
|
}
|
|
func bb(b byte) bool {
|
|
return b == '#'
|
|
}
|
|
|
|
func StdinToStringSlice() []string {
|
|
var input []string
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
for scanner.Scan() {
|
|
input = append(input, scanner.Text())
|
|
}
|
|
return input
|
|
}
|