2017 Day 23 Complete

This commit is contained in:
2017-12-23 09:01:32 -06:00
parent d16cc22158
commit 6c4e6ec824
8 changed files with 387 additions and 268 deletions

View File

@@ -3,296 +3,108 @@ package main
import (
"bufio"
"fmt"
"log"
"math"
"os"
"strings"
"time"
)
var allRules []Rule
var rules map[string]string
func main() {
rules = make(map[string]string)
inp := StdinToStrings()
for i := range inp {
allRules = append(allRules, Rule(inp[i]))
pts := strings.Split(inp[i], " ")
rules[pts[0]] = pts[2]
rules[Flip(pts[0])] = pts[2]
rules[Rotate(pts[0], 90)] = pts[2]
rules[Rotate(pts[0], 180)] = pts[2]
rules[Rotate(pts[0], 270)] = pts[2]
}
//pattern := Pattern(".#./..#/###")
pattern := Pattern("../##")
fmt.Println(pattern)
if !pattern.IsValid() {
fmt.Println("Invalid Pattern Given. Must be square.")
}
ClearScreen()
pattern.PrettyPrint()
time.Sleep(time.Second)
for iters := 0; iters < 5; iters++ {
ClearScreen()
pattern = Tick(pattern)
pattern.PrettyPrint()
time.Sleep(time.Second)
}
fmt.Println("On:", pattern.GetOnCount())
}
// Stitch takes a slice of patterns and turns them into one large (square) pattern
func Stitch(inp []Pattern) Pattern {
ppr := int(math.Sqrt(float64(len(inp))))
newSize := ppr * inp[0].Size()
var currRow int
rows := make(map[int]string)
for i := 0; i < len(inp); i++ {
currRow = (i / ppr) * 2
for j := 0; j < len(inp[0]); j++ {
if len(rows[currRow+j]) == newSize {
rows[currRow+j] += "/"
}
rows[currRow+j] += inp[i].GetRow(j)
pattern := ".#./..#/###"
for i := 0; i < 2; i++ {
Tick(pattern)
}
/*
pattern = rules[pattern]
pts := SplitBlocks(pattern)
for i := range pts {
pts[i] = rules[pts[i]]
}
}
var lines []string
for i := 0; i < newSize; i++ {
lines = append(lines, rows[i])
}
return Pattern(strings.Join(lines, "/"))
fmt.Println(pts)
fmt.Println(CombineBlocks(pts))
*/
}
// Run the pattern, or all of it's subpatterns
// through the rules and return the new pattern
func Tick(inp Pattern) Pattern {
if inp.SubpatternCount() > 1 {
var subs []Pattern
// Tick each subpattern
for i := 0; i < inp.SubpatternCount(); i++ {
if s := inp.GetSubpattern(i); s.IsValid() {
subs = append(subs, Tick(s))
} else {
log.Fatal("Error ticking pattern")
}
func Tick(pt string) string {
fmt.Println(SplitBlocks(pt))
return ""
}
func SplitBlocks(pt string) []string {
var ret []string
pts := strings.Split(pt, "/")
if len(pts[0]) <= 3 {
return []string{pt}
}
// Assume 3x3
blkSize := 3
if len(pts[0])%2 == 0 {
// 2x2 blocks
blkSize = 2
}
for j := 0; j < len(pts); j += blkSize {
for i := 0; i < len(pts[j]); i += blkSize {
ret = append(ret, pts[j][i:i+blkSize]+"/"+pts[j+1][i:i+blkSize])
}
return Stitch(subs)
}
var foundMatch bool
for i := range allRules {
if allRules[i].Matches(inp) {
inp = allRules[i].Output()
foundMatch = true
}
}
if !foundMatch {
fmt.Println("~ ERROR TICKING ~")
inp.PrettyPrint()
fmt.Println("~ ERROR TICKING ~")
os.Exit(1)
}
return inp
}
/**
* A pattern is a string with some special methods
*/
type Pattern string
// Print a pattern prettily. (in a square)
func (p Pattern) PrettyPrint() {
pts := strings.Split(string(p), "/")
for i := range pts {
fmt.Println(pts[i])
}
}
func (p Pattern) GetOnCount() int {
return strings.Count(string(p), "#")
}
// Returns if a pattern is valid.
// A pattern is valid if it has more than 0 rows
// and every row is the same length as the number
// of columns
func (p Pattern) IsValid() bool {
pts := strings.Split(string(p), "/")
if len(pts) == 0 {
return false
}
for i := range pts {
if len(pts[i]) != len(pts) {
return false
}
}
return true
}
// Returns the "size" of the pattern
// That is, how many rows/columns it has
func (p Pattern) Size() int {
return len(strings.Split(string(p), "/"))
}
// Flip returns a new pattern that has been flipped horizontally
func (p Pattern) Flip() Pattern {
// We can only flip the smallest patterns (2x2 or 3x3)
if p.SubpatternCount() != 1 {
return p
}
pts := strings.Split(string(p), "/")
for i := range pts {
pts[i] = RevString(pts[i])
}
return Pattern(strings.Join(pts, "/"))
}
// Rotate returns a new pattern that has been rotated deg degrees
// Only right-angles
func (p Pattern) Rotate(deg int) Pattern {
// We can only rotate the smallest patterns (2x2 or 3x3)
if p.SubpatternCount() != 1 {
return p
}
if deg < 0 {
deg += 360
} else if deg == 0 {
return p
}
pts := strings.Split(string(p), "/")
ret := make([]string, len(pts))
use := p
switch deg {
case 90:
if p.Size()%3 == 0 {
ret[0] = RevString(use.GetCol(2))
ret[1] = RevString(use.GetCol(1))
ret[2] = RevString(use.GetCol(0))
} else {
ret[0] = RevString(use.GetCol(1))
ret[1] = RevString(use.GetCol(0))
}
case 180:
if p.Size()%3 == 0 {
ret[0] = RevString(use.GetRow(2))
ret[1] = RevString(use.GetRow(1))
ret[2] = RevString(use.GetRow(0))
} else {
ret[0] = RevString(use.GetRow(1))
ret[1] = RevString(use.GetRow(0))
}
case 270:
if p.Size()%3 == 0 {
ret[0] = use.GetCol(2)
ret[1] = use.GetCol(1)
ret[2] = use.GetCol(0)
} else {
ret[0] = use.GetCol(1)
ret[1] = use.GetCol(0)
}
}
use = Pattern(strings.Join(ret, "/"))
return Pattern(strings.Join(ret, "/"))
}
// GetRow returns a row as a string
func (p Pattern) GetRow(row int) string {
pts := strings.Split(string(p), "/")
if row >= len(pts) {
return ""
}
return pts[row]
}
// GetCol returns a column as a string
func (p Pattern) GetCol(col int) string {
var ret string
pts := strings.Split(string(p), "/")
if col >= len(pts[0]) {
return ""
}
for i := 0; i < len(pts); i++ {
ret = ret + string(pts[i][col])
}
return ret
}
// Counts the number of subpatterns in the pattern
func (p Pattern) SubpatternCount() int {
if p.Size()%2 == 0 {
return (p.Size() / 2) * (p.Size() / 2)
} else {
return (p.Size() / 3) * (p.Size() / 3)
func CombineBlocks(pts []string) string {
var ret string
sz := int(math.Sqrt(float64(len(pts))))
fmt.Println("Combining")
fmt.Println(sz)
//var j int
for i := 0; i < len(pts); i++ {
}
return ret
}
// Gets a specific subpattern out of the pattern
func (p Pattern) GetSubpattern(i int) Pattern {
if i > p.SubpatternCount() {
return Pattern("")
func Flip(pt string) string {
pts := strings.Split(pt, "/")
var newPts []string
for i := range pts {
newPts = append(newPts, RevString(pts[i]))
}
subSize := 3 // Assume 3x3 subpatterns
if p.Size()%2 == 0 {
// Subpatterns are actually 2x2
subSize = 2
return strings.Join(newPts, "/")
}
func Rotate(pt string, deg int) string {
if deg == 0 {
return pt
}
ppr := p.Size() / subSize
col := i % (p.Size() / subSize)
row := i / ppr
ptString := ""
for j := 0; j < subSize; j++ {
ptString += p.GetRow((row * 2) + j)[(col*subSize):(col*subSize)+subSize] + "/"
if deg%90 != 0 {
// We can't do this
fmt.Println("Invalid Rotation:", deg)
os.Exit(1)
}
return Pattern(ptString[:len(ptString)-1])
}
/**
* Rule is an interface that can take a pattern and return if it matches,
* report on it's size, or return the resulting pattern from applying itself
* to a pattern
*/
type RuleFace interface {
Matches(inp Pattern) bool
InputSize() int
Apply(inp Pattern) Pattern
}
/**
* Rule implements the RuleFace interface
* for 2x2 => 3x3 transitions
* or 3x3 => 4x4 transitions
*/
type Rule string
func (r Rule) Input() Pattern {
pts := strings.Split(string(r), " ")
return Pattern(pts[0])
}
func (r Rule) Output() Pattern {
pts := strings.Split(string(r), " ")
return Pattern(pts[2])
}
func (r Rule) Matches(inp Pattern) bool {
if inp.Size() != r.InputSize() {
return false
pts := strings.Split(pt, "/")
var tst string
if len(pts[0])%2 == 0 {
// 2x2 pattern
tst = string(pts[1][0]) + string(pts[0][0]) + "/"
tst += string(pts[1][1]) + string(pts[0][1])
return Rotate(tst, deg-90)
}
// Try it rotated 90,180,270
for i := 0; i < 360; i += 90 {
if r.Equals(inp.Rotate(i)) || r.Equals(inp.Flip().Rotate(i)) {
return true
}
}
return false
}
func (r Rule) InputSize() int {
return len(strings.Split(string(r.Input()), "/"))
}
// Equals is a direct string comparison
func (r Rule) Equals(inp Pattern) bool {
return string(r.Input()) == string(inp)
// 3x3 pattern
tst = string(pts[2][0]) + string(pts[1][0]) + string(pts[0][0]) + "/"
tst += string(pts[2][1]) + string(pts[1][1]) + string(pts[0][1]) + "/"
tst += string(pts[2][2]) + string(pts[1][2]) + string(pts[0][2])
return Rotate(tst, deg-90)
}
func RevString(s string) string {
@@ -303,10 +115,6 @@ func RevString(s string) string {
return string(runes)
}
func ClearScreen() {
fmt.Println("\033[H\033[2J")
}
func StdinToStrings() []string {
var input []string
scanner := bufio.NewScanner(os.Stdin)