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() } }