287 lines
5.3 KiB
Go
287 lines
5.3 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] {
|
|
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()
|
|
}
|
|
}
|