package main import ( "bufio" "fmt" "io" "os" "strings" "github.com/perlw/advent_of_code/toolkit/grid" ) // Tiles ... type Tiles map[int]grid.Grid // Possibilities ... type Possibilities struct { T map[int][]string B map[int][]string L map[int][]string R map[int][]string } // Count ... func (p Possibilities) Count() int { return len(p.T) + len(p.B) + len(p.L) + len(p.R) } func addPossibility( p Possibilities, l, r, t, b bool, transform string, id int, ) Possibilities { if t && strings.Contains(transform, "h") { if _, ok := p.B[id]; !ok { p.B[id] = make([]string, 0, 10) } p.B[id] = append(p.B[id], transform) } else if t && !strings.Contains(transform, "h") { if _, ok := p.T[id]; !ok { p.T[id] = make([]string, 0, 10) } p.T[id] = append(p.T[id], transform) } if b && strings.Contains(transform, "h") { if _, ok := p.T[id]; !ok { p.T[id] = make([]string, 0, 10) } p.T[id] = append(p.T[id], transform) } else if b && !strings.Contains(transform, "h") { if _, ok := p.B[id]; !ok { p.B[id] = make([]string, 0, 10) } p.B[id] = append(p.B[id], transform) } if l && strings.Contains(transform, "v") { if _, ok := p.R[id]; !ok { p.R[id] = make([]string, 0, 10) } p.R[id] = append(p.R[id], transform) } else if l && !strings.Contains(transform, "v") { if _, ok := p.L[id]; !ok { p.L[id] = make([]string, 0, 10) } p.L[id] = append(p.L[id], transform) } if r && strings.Contains(transform, "v") { if _, ok := p.L[id]; !ok { p.L[id] = make([]string, 0, 10) } p.L[id] = append(p.L[id], transform) } else if r && !strings.Contains(transform, "v") { if _, ok := p.R[id]; !ok { p.R[id] = make([]string, 0, 10) } p.R[id] = append(p.R[id], transform) } return p } func (ts Tiles) buildPosssibilities(debug bool) map[int]Possibilities { result := make(map[int]Possibilities) for i := range ts { result[i] = Possibilities{ T: make(map[int][]string), B: make(map[int][]string), L: make(map[int][]string), R: make(map[int][]string), } var count int for j := range ts { if j == i { continue } tile := ts[j] g := *(&tile).Clone() l, r, t, b := compareEdges(ts[i], g) if l || r || t || b { count++ result[i] = addPossibility(result[i], l, r, t, b, "none", j) if debug { fmt.Printf("potential %d -> %d\n", i, j) } } g = *(&tile).Clone() hflipGrid(&g) l, r, t, b = compareEdges(ts[i], g) if l || r || t || b { count++ result[i] = addPossibility(result[i], l, r, t, b, "hflip", j) if debug { fmt.Printf("potential hflip %d -> %d\n", i, j) } } g = *(&tile).Clone() vflipGrid(&g) l, r, t, b = compareEdges(ts[i], g) if l || r || t || b { count++ result[i] = addPossibility(result[i], l, r, t, b, "vflip", j) if debug { fmt.Printf("potential vflip %d -> %d\n", i, j) } } g = *(&tile).Clone() hflipGrid(&g) vflipGrid(&g) l, r, t, b = compareEdges(ts[i], g) if l || r || t || b { count++ result[i] = addPossibility(result[i], l, r, t, b, "hvflip", j) if debug { fmt.Printf("potential hvflip %d -> %d\n", i, j) } } g = *(&tile).Clone() rotateGrid(&g) l, r, t, b = compareEdges(ts[i], g) if l || r || t || b { count++ result[i] = addPossibility( result[i], l, r, t, b, "rot", j, ) if debug { fmt.Printf("potential rot %d -> %d\n", i, j) } } vflipGrid(&g) l, r, t, b = compareEdges(ts[i], g) if l || r || t || b { count++ result[i] = addPossibility( result[i], l, r, t, b, "vfliprot", j, ) if debug { fmt.Printf("potential vfliprot %d -> %d\n", i, j) } } hflipGrid(&g) l, r, t, b = compareEdges(ts[i], g) if l || r || t || b { count++ result[i] = addPossibility( result[i], l, r, t, b, "hfliprot", j, ) if debug { fmt.Printf("potential hfliprot %d -> %d\n", i, j) } } vflipGrid(&g) l, r, t, b = compareEdges(ts[i], g) if l || r || t || b { count++ result[i] = addPossibility( result[i], l, r, t, b, "hvfliprot", j, ) if debug { fmt.Printf("potential hvfliprot %d -> %d\n", i, j) } } } if debug { fmt.Printf("%d -> %d\n", i, count) } } return result } func rotateGrid(g *grid.Grid) { t := g.Clone() for y := 0; y < g.Height; y++ { for x := 0; x < g.Width; x++ { a := (y * g.Width) + x b := (x * g.Width) + (g.Width - y - 1) t.Cells[b] = g.Cells[a] } } *g = *t } func vflipGrid(g *grid.Grid) { for y := 0; y < g.Height; y++ { for x := 0; x < g.Width/2; x++ { a := (y * g.Width) + x b := (y * g.Width) + (g.Width - x - 1) g.Cells[a], g.Cells[b] = g.Cells[b], g.Cells[a] } } } func hflipGrid(g *grid.Grid) { for y := 0; y < g.Height/2; y++ { for x := 0; x < g.Width; x++ { a := (y * g.Width) + x b := ((g.Height - y - 1) * g.Width) + x g.Cells[a], g.Cells[b] = g.Cells[b], g.Cells[a] } } } func compareEdges(a, b grid.Grid) (bool, bool, bool, bool) { lEdge, rEdge, tEdge, bEdge := true, true, true, true opEdge := (b.Height - 1) * b.Width for x := 0; x < a.Width; x++ { if a.Cells[x] != b.Cells[opEdge+x] { tEdge = false break } } for x := 0; x < a.Width; x++ { if a.Cells[((a.Height-1)*a.Height)+x] != b.Cells[x] { bEdge = false break } } opEdge = b.Width - 1 for y := 0; y < a.Height; y++ { if a.Cells[y*a.Width] != b.Cells[(y*b.Width)+opEdge] { lEdge = false break } } for y := 0; y < a.Height; y++ { if a.Cells[(y*a.Width)+a.Width-1] != b.Cells[y*b.Width] { rEdge = false break } } return lEdge, rEdge, tEdge, bEdge } func inSlice(s []int, a int) bool { for i := range s { if s[i] == a { return true } } return false } // Task1 ... func Task1(input Tiles, debug bool) int { possibilities := input.buildPosssibilities(debug) if debug { fmt.Printf("%+v\n", possibilities) } result := 1 for i := range possibilities { if possibilities[i].Count() == 2 { result *= i } } return result } // Task2 ... func Task2(input Tiles, debug bool) int { possibilities := input.buildPosssibilities(debug) if debug { fmt.Printf("%+v\n", possibilities) } corners := struct { tl, tr, bl, br int }{} fmt.Println() for i, p := range possibilities { if p.Count() == 2 { if len(p.T) > 0 && len(p.R) > 0 { fmt.Printf("tl: %d %+v\n", i, p) corners.tl = i } if len(p.T) > 0 && len(p.L) > 0 { fmt.Printf("tr: %d %+v\n", i, p) corners.tr = i } if len(p.B) > 0 && len(p.R) > 0 { fmt.Printf("bl: %d %+v\n", i, p) corners.bl = i } if len(p.B) > 0 && len(p.L) > 0 { fmt.Printf("br: %d %+v\n", i, p) corners.br = i } } } fmt.Printf("%+v\n", corners) // find corner to corner paths /*for _, i := range corners { fmt.Printf("searching from %d\n", i) c := i found: for { next: for j := range possibilities[c] { if c == j { continue } fmt.Printf("checking %d\n", j) for k := range possibilities[j] { if c == k { continue } if len(possibilities[k]) == 2 { fmt.Printf("corner %d\n", k) // TODO: store break found } else if len(possibilities[k]) == 3 { fmt.Printf("found next %d\n", k) c = k break next } } } } }*/ return -1 } func readInput(reader io.Reader) Tiles { input := make(Tiles) scanner := bufio.NewScanner(reader) var currentID int cells := make([]rune, 0, 10) var stride int for scanner.Scan() { line := scanner.Text() if line == "" { input[currentID] = grid.FromRunes(cells, stride, stride) cells = make([]rune, 0, 10) continue } if strings.HasPrefix(line, "Tile") { fmt.Sscanf(line, "Tile %d", ¤tID) } else { if len(cells) == 0 { stride = len(line) } cells = append(cells, []rune(line)...) } } input[currentID] = grid.FromRunes(cells, stride, stride) return input } func main() { var input Tiles file, _ := os.Open("input.txt") input = readInput(file) file.Close() result := Task1(input, false) fmt.Printf("Task 1: %d\n", result) result = Task2(input, false) fmt.Printf("Task 2: %d\n", result) }