adventofcode/2020/day20/main.go

315 lines
5.7 KiB
Go

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] {
if monster[y][x] == '#' {
SeaMonster[h.Coordinate{X: x, Y: y}] = true
}
}
}
}
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.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++ {
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 {
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()
}
}
func PrintCoordMap(m map[h.Coordinate]bool) {
minX := h.MAX_INT
maxX := h.MIN_INT
minY := h.MAX_INT
maxY := h.MIN_INT
for k := range m {
if k.X < minX {
minX = k.X
}
if k.X > maxX {
maxX = k.X
}
if k.Y < minY {
minY = k.Y
}
if k.Y > maxY {
maxY = k.Y
}
}
for y := minY; y <= maxY; y++ {
for x := minX; x <= maxX; x++ {
if v, ok := m[h.Coordinate{X: x, Y: y}]; ok {
if v {
fmt.Print("#")
} else {
fmt.Print(".")
}
} else {
fmt.Print(".")
}
}
fmt.Println()
}
}