Switching Computers
This commit is contained in:
parent
aafafc5338
commit
c40e8ce7bb
288
2020/day19/main.go
Normal file
288
2020/day19/main.go
Normal file
@ -0,0 +1,288 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("# Day 19")
|
||||
fmt.Println()
|
||||
inp := h.StdinToStringSlice()
|
||||
part := h.OptArgNumber(1, "2")
|
||||
solve(inp, h.Atoi(part))
|
||||
}
|
||||
|
||||
var loopingTokens []int
|
||||
var allTokens map[int]*Token
|
||||
var totalRules int
|
||||
|
||||
/*
|
||||
8: 42 | 42 8
|
||||
11: 42 31 | 42 11 31
|
||||
|
||||
Figure out the values of 31 & 42, then see how they
|
||||
affect things as they repeat
|
||||
|
||||
31: 95 7 | 104 130
|
||||
42: 29 104 | 115 95
|
||||
*/
|
||||
|
||||
func solve(inp []string, part int) {
|
||||
allTokens = make(map[int]*Token)
|
||||
var section int
|
||||
var rules []string
|
||||
var messages []string
|
||||
for _, v := range inp {
|
||||
if v == "" {
|
||||
// End of the rules list
|
||||
section++
|
||||
}
|
||||
switch section {
|
||||
case 0:
|
||||
rules = append(rules, v)
|
||||
case 1:
|
||||
messages = append(messages, v)
|
||||
}
|
||||
}
|
||||
totalRules = len(rules)
|
||||
last := -1
|
||||
for RulesSolved() < totalRules && last != RulesSolved() {
|
||||
last = RulesSolved()
|
||||
for _, v := range rules {
|
||||
fmt.Println("Restarting Rules Parsing")
|
||||
pts := strings.Split(v, ": ")
|
||||
num := h.Atoi(pts[0])
|
||||
if part == 2 {
|
||||
if num == 8 {
|
||||
pts[1] = "42 | 42 8"
|
||||
} else if num == 11 {
|
||||
pts[1] = "42 31 | 42 11 31"
|
||||
}
|
||||
}
|
||||
if _, ok := allTokens[num]; !ok {
|
||||
GenerateToken(num, pts[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
if RulesSolved() < totalRules {
|
||||
fmt.Println("Unable to solve all rules")
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("Calculating Answer")
|
||||
var matches int
|
||||
for _, v := range messages {
|
||||
if allTokens[0].Matches(v) {
|
||||
matches++
|
||||
}
|
||||
}
|
||||
fmt.Printf("## Part 1\nAnswer: %d\n", matches)
|
||||
}
|
||||
|
||||
// RulesSolved TODO
|
||||
func RulesSolved() int {
|
||||
var ret int
|
||||
for k := range allTokens {
|
||||
if k >= 0 {
|
||||
ret++
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Token Kinds
|
||||
const (
|
||||
LEAF = iota
|
||||
STICK
|
||||
BRANCH
|
||||
)
|
||||
|
||||
// Token TODO
|
||||
type Token struct {
|
||||
kind int
|
||||
num int
|
||||
value string
|
||||
raw string
|
||||
|
||||
PartNums []int
|
||||
Parts []*Token
|
||||
|
||||
CachedValues []string
|
||||
Loops bool
|
||||
}
|
||||
|
||||
// GenerateToken TODO
|
||||
func GenerateToken(num int, raw string) *Token {
|
||||
if v, ok := allTokens[num]; ok {
|
||||
return v
|
||||
}
|
||||
t := Token{num: num, raw: raw}
|
||||
if raw[0] == '"' {
|
||||
t.value = strings.ReplaceAll(raw, "\"", "")
|
||||
t.kind = LEAF
|
||||
} else {
|
||||
if !strings.Contains(raw, "|") {
|
||||
t.kind = STICK
|
||||
var partTokens []*Token
|
||||
pts := strings.Split(raw, " ")
|
||||
for k := range pts {
|
||||
if pts[k] == "|" {
|
||||
continue
|
||||
}
|
||||
t.PartNums = append(t.PartNums, h.Atoi(pts[k]))
|
||||
}
|
||||
solved := true
|
||||
for k := range t.PartNums {
|
||||
if v, ok := allTokens[t.PartNums[k]]; ok || h.IntSliceContains(loopingTokens, t.PartNums[k]) {
|
||||
partTokens = append(partTokens, v)
|
||||
} else {
|
||||
solved = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if solved {
|
||||
t.Parts = partTokens
|
||||
}
|
||||
} else {
|
||||
t.kind = BRANCH
|
||||
pts := strings.Split(raw, " ")
|
||||
for k := range pts {
|
||||
if pts[k] == "|" {
|
||||
continue
|
||||
}
|
||||
n := h.Atoi(pts[k])
|
||||
if n == num {
|
||||
if !h.IntSliceContains(loopingTokens, t.num) {
|
||||
fmt.Println(num, "IS A LOOPING TOKEN")
|
||||
loopingTokens = append(loopingTokens, t.num)
|
||||
t.Loops = true
|
||||
}
|
||||
}
|
||||
}
|
||||
branches := strings.Split(raw, " | ")
|
||||
var partTokens []*Token
|
||||
for k, branch := range branches {
|
||||
branchID := -(num*1000 + k)
|
||||
t.PartNums = append(t.PartNums, branchID)
|
||||
p := GenerateToken(branchID, branch)
|
||||
if p.Solved() {
|
||||
allTokens[branchID] = p
|
||||
partTokens = append(partTokens, p)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
solved := true
|
||||
for _, bid := range t.PartNums {
|
||||
if _, ok := allTokens[bid]; !ok {
|
||||
solved = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if solved {
|
||||
t.Parts = partTokens
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.Solved() {
|
||||
allTokens[num] = &t
|
||||
} else {
|
||||
fmt.Println("> Couldn't Solve", num)
|
||||
}
|
||||
return &t
|
||||
}
|
||||
|
||||
// Solved TODO
|
||||
func (t *Token) Solved() bool {
|
||||
return len(t.Values()) > 0
|
||||
}
|
||||
|
||||
// Values TODO
|
||||
func (t *Token) Values() []string {
|
||||
switch t.kind {
|
||||
case LEAF:
|
||||
return t.LeafValue()
|
||||
case STICK:
|
||||
return t.StickValue()
|
||||
case BRANCH:
|
||||
return t.BranchValue()
|
||||
default:
|
||||
return []string{}
|
||||
}
|
||||
}
|
||||
|
||||
// LeafValue TODO
|
||||
func (t *Token) LeafValue() []string {
|
||||
return []string{t.value}
|
||||
}
|
||||
|
||||
// StickValue TODO
|
||||
func (t *Token) StickValue() []string {
|
||||
if len(t.Parts) < len(t.PartNums) {
|
||||
return []string{}
|
||||
}
|
||||
if len(t.CachedValues) > 0 {
|
||||
return t.CachedValues
|
||||
}
|
||||
var ret []string
|
||||
for k := range t.Parts {
|
||||
if len(ret) == 0 {
|
||||
ret = t.Parts[k].Values()
|
||||
} else {
|
||||
var newRet []string
|
||||
for rk := range ret {
|
||||
newRet = append(newRet, h.AppendStrings(ret[rk], t.Parts[k].Values())...)
|
||||
}
|
||||
ret = newRet
|
||||
}
|
||||
}
|
||||
t.CachedValues = ret
|
||||
return ret
|
||||
}
|
||||
|
||||
// BranchValue TODO
|
||||
func (t *Token) BranchValue() []string {
|
||||
if len(t.Parts) < len(t.PartNums) {
|
||||
return []string{}
|
||||
}
|
||||
if len(t.CachedValues) > 0 {
|
||||
return t.CachedValues
|
||||
}
|
||||
var ret []string
|
||||
for k := range t.Parts {
|
||||
ret = append(ret, t.Parts[k].Values()...)
|
||||
}
|
||||
t.CachedValues = ret
|
||||
return ret
|
||||
}
|
||||
|
||||
// Matches actually tests a message against this token
|
||||
func (t *Token) Matches(msg string) bool {
|
||||
for _, v := range t.Values() {
|
||||
if msg == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t Token) String() string {
|
||||
return fmt.Sprintf("{num:%d, kind:%s, partNums:%v, value:%v}", t.num, KindToString(t.kind), t.PartNums, t.Values())
|
||||
}
|
||||
|
||||
// KindToString TODO
|
||||
func KindToString(kind int) string {
|
||||
switch kind {
|
||||
case LEAF:
|
||||
return "LEAF"
|
||||
case STICK:
|
||||
return "STICK"
|
||||
case BRANCH:
|
||||
return "BRANCH"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
286
2020/day20/main.go
Normal file
286
2020/day20/main.go
Normal file
@ -0,0 +1,286 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
||||
)
|
||||
|
||||
var N h.Coordinate
|
||||
var E h.Coordinate
|
||||
var S h.Coordinate
|
||||
var W h.Coordinate
|
||||
var SeaMonster map[h.Coordinate]bool
|
||||
|
||||
func init() {
|
||||
N = h.Coordinate{X: 0, Y: -1}
|
||||
E = h.Coordinate{X: 1, Y: 0}
|
||||
S = h.Coordinate{X: 0, Y: 1}
|
||||
W = h.Coordinate{X: -1, Y: 0}
|
||||
SeaMonster = make(map[h.Coordinate]bool)
|
||||
monster := []string{
|
||||
" # ",
|
||||
"# ## ## ###",
|
||||
" # # # # # # ",
|
||||
}
|
||||
for y := range monster {
|
||||
for x := range monster[y] {
|
||||
SeaMonster[h.Coordinate{X: x, Y: y}] = monster[y][x] == '#'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("# Day 20")
|
||||
fmt.Println()
|
||||
inp := h.StdinToStringSlice()
|
||||
part := h.OptArgNumber(1, "2")
|
||||
solve(inp, h.Atoi(part))
|
||||
}
|
||||
|
||||
func solve(inp []string, part int) {
|
||||
tiles := buildTiles(inp)
|
||||
alignGrid(tiles)
|
||||
if part == 1 {
|
||||
var result uint64 = 1
|
||||
for k := range tiles {
|
||||
if len(tiles[k].Neighbors) == 2 {
|
||||
result *= uint64(k)
|
||||
}
|
||||
}
|
||||
fmt.Println("## Part 1\nAnswer:", result)
|
||||
} else {
|
||||
image := buildMapFromTiles(tiles)
|
||||
image = monsterSlayer(image)
|
||||
var waves int
|
||||
for y := range image {
|
||||
for x := range image[y] {
|
||||
if image[y][x] {
|
||||
waves++
|
||||
fmt.Print("#")
|
||||
} else {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
fmt.Println("## Part2\nAnswer:", waves)
|
||||
}
|
||||
}
|
||||
|
||||
func buildTiles(inp []string) map[int]*Tile {
|
||||
ret := make(map[int]*Tile)
|
||||
var tileNum int
|
||||
var tileLines []string
|
||||
for k := range inp {
|
||||
if inp[k] == "" {
|
||||
continue
|
||||
} else if strings.HasPrefix(inp[k], "Tile ") {
|
||||
if len(tileLines) != 0 {
|
||||
// Create the tile
|
||||
t := NewTile(tileNum, tileLines)
|
||||
ret[t.Num] = t
|
||||
}
|
||||
tileNum = h.Atoi(strings.TrimPrefix(strings.TrimSuffix(inp[k], ":"), "Tile "))
|
||||
tileLines = []string{}
|
||||
} else {
|
||||
tileLines = append(tileLines, inp[k])
|
||||
}
|
||||
}
|
||||
// Add the last tile
|
||||
if len(tileLines) != 0 {
|
||||
// Create the tile
|
||||
t := NewTile(tileNum, tileLines)
|
||||
ret[t.Num] = t
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func alignGrid(tiles map[int]*Tile) {
|
||||
done := make(map[int]bool)
|
||||
for tile := range tiles {
|
||||
if len(tiles[tile].Neighbors) != 0 {
|
||||
done[tile] = true
|
||||
}
|
||||
}
|
||||
if len(done) == 0 {
|
||||
for tile := range tiles {
|
||||
done[tile] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
match := true
|
||||
for match {
|
||||
match = false
|
||||
for tile := range done {
|
||||
for wrk := range tiles {
|
||||
if wrk == tile {
|
||||
continue
|
||||
}
|
||||
_, found := done[wrk]
|
||||
// Test if wrk can be a neighbor to tile
|
||||
if tiles[tile].IsNeighbor(tiles[wrk], !found) {
|
||||
match = true
|
||||
done[wrk] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildMapFromTiles(tiles map[int]*Tile) [][]bool {
|
||||
done := make(map[*Tile]bool)
|
||||
var wrk *Tile
|
||||
queue := make(map[*Tile]h.Coordinate)
|
||||
|
||||
for t := range tiles {
|
||||
wrk = tiles[t]
|
||||
done[wrk] = true
|
||||
for n, p := range wrk.Neighbors {
|
||||
queue[p] = n
|
||||
}
|
||||
// TODO Analyze
|
||||
break
|
||||
}
|
||||
if wrk == nil {
|
||||
return nil
|
||||
}
|
||||
grid := make(map[h.Coordinate]*Tile)
|
||||
grid[h.Coordinate{}] = wrk
|
||||
var minX, minY, maxX, maxY int
|
||||
tileSize := len(wrk.Data) - 2
|
||||
for len(queue) > 0 {
|
||||
for p, dir := range queue {
|
||||
delete(queue, p)
|
||||
done[p] = true
|
||||
for n, np := range p.Neighbors {
|
||||
if _, ok := done[np]; !ok {
|
||||
queue[np] = dir.Relative(n)
|
||||
}
|
||||
}
|
||||
grid[dir] = p
|
||||
if dir.X < minX {
|
||||
minX = dir.X
|
||||
}
|
||||
if dir.X > maxX {
|
||||
maxX = dir.X
|
||||
}
|
||||
if dir.Y < minY {
|
||||
minY = dir.Y
|
||||
}
|
||||
if dir.Y > maxY {
|
||||
maxY = dir.Y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret := make([][]bool, (maxY-minY+1)*tileSize)
|
||||
size := maxX - minX + 1
|
||||
// Build the map, trimming the borders
|
||||
for y := minY; y <= maxY; y++ {
|
||||
for x := minX; x <= maxX; x++ {
|
||||
tile := grid[h.Coordinate{X: x, Y: y}]
|
||||
for tileY, row := range tile.Data[1 : tileSize+1] {
|
||||
procY := (y-minY)*tileSize + tileY
|
||||
if ret[procY] == nil {
|
||||
ret[procY] = make([]bool, size*tileSize)
|
||||
}
|
||||
for tileX, t := range row[1 : len(row)-1] {
|
||||
procX := (x-minX)*tileSize + tileX
|
||||
ret[procY][procX] = t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func monsterSlayer(inp [][]bool) [][]bool {
|
||||
var found bool
|
||||
for r := 0; r < 8; r++ {
|
||||
if r == 6 {
|
||||
// This is the correct orientation
|
||||
fmt.Println("Rotation:", r)
|
||||
PrintMap(inp)
|
||||
}
|
||||
for y := range inp {
|
||||
for x := range inp[y] {
|
||||
match := true
|
||||
for m := range SeaMonster {
|
||||
if y+m.Y >= len(inp) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
if x+m.X >= len(inp[y+m.Y]) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
if !inp[y+m.Y][x+m.X] {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
found = true
|
||||
// Remove the monster
|
||||
for m := range SeaMonster {
|
||||
inp[y+m.Y][x+m.X] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if found {
|
||||
fmt.Println("Found, breaking")
|
||||
break
|
||||
}
|
||||
if r&1 == 0 {
|
||||
inp = FlipMap(inp)
|
||||
} else {
|
||||
inp = RotateMap(FlipMap(inp))
|
||||
}
|
||||
}
|
||||
return inp
|
||||
}
|
||||
|
||||
func FlipMap(inp [][]bool) [][]bool {
|
||||
size := len(inp)
|
||||
newData := make([][]bool, size)
|
||||
for i := 0; i < size; i++ {
|
||||
newData[i] = make([]bool, size)
|
||||
}
|
||||
for y := range inp {
|
||||
for x := range inp[y] {
|
||||
newData[y][size-x-1] = inp[y][x]
|
||||
}
|
||||
}
|
||||
return newData
|
||||
}
|
||||
|
||||
func RotateMap(inp [][]bool) [][]bool {
|
||||
size := len(inp)
|
||||
newData := make([][]bool, size)
|
||||
for y := 0; y < size; y++ {
|
||||
newData[y] = make([]bool, size)
|
||||
}
|
||||
for y := range inp {
|
||||
for x := range inp[y] {
|
||||
newData[size-x-1][y] = inp[y][x]
|
||||
}
|
||||
}
|
||||
return newData
|
||||
}
|
||||
|
||||
func PrintMap(inp [][]bool) {
|
||||
for y := range inp {
|
||||
for x := range inp[y] {
|
||||
if inp[y][x] {
|
||||
fmt.Print("#")
|
||||
} else {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user