package main import ( "bufio" "flag" "fmt" "io" "os" ) // Dir ... type Dir int // Dir constants. const ( DirNE Dir = iota DirE DirSE DirSW DirW DirNW ) func (d Dir) String() string { switch d { case DirNE: return "ne" case DirE: return "e" case DirSE: return "se" case DirSW: return "sw" case DirW: return "w" case DirNW: return "nw" } return "n/a" } // MovedPos ... func (d Dir) MovedPos(initial Pos) Pos { move := initial switch d { case DirNE: if move.Y%2 != 0 { move.X++ } move.Y-- case DirE: move.X++ case DirSE: if move.Y%2 != 0 { move.X++ } move.Y++ case DirSW: if move.Y%2 == 0 { move.X-- } move.Y++ case DirW: move.X-- case DirNW: if move.Y%2 == 0 { move.X-- } move.Y-- } return move } // StrToDir ... func StrToDir(str string) Dir { switch str { case "ne": return DirNE case "e": return DirE case "se": return DirSE case "sw": return DirSW case "w": return DirW case "nw": return DirNW } return -1 } // Cell ... type Cell struct { Position Pos Flipped bool } // Pos ... type Pos struct { X, Y int } // HexGrid ... type HexGrid struct { Root *Cell Cells map[Pos]*Cell } // MakeMoves ... func (hg *HexGrid) MakeMoves(moves string, debug bool) { if debug { fmt.Printf("\n==>%s\n", moves) } var currPos Pos for i := 0; i < len(moves); i++ { var dir Dir if moves[i] == 's' || moves[i] == 'n' { dir = StrToDir(moves[i : i+2]) i++ } else { dir = StrToDir(string(moves[i])) } move := dir.MovedPos(currPos) if debug { fmt.Printf("%+v move %s to %+v\n", currPos, dir, move) } cell, ok := hg.Cells[move] if !ok { cell = &Cell{ Position: move, } } if i == len(moves)-1 { cell.Flipped = !cell.Flipped if debug { fmt.Printf("flipped %+v, now %v\n", cell.Position, cell.Flipped) } } hg.Cells[move] = cell currPos = move } } // IterateLife ... func (hg *HexGrid) IterateLife(debug bool) { cells := make(map[Pos]*Cell) var topLeft, bottomRight Pos for _, c := range hg.Cells { if c.Position.X < topLeft.X { topLeft.X = c.Position.X } if c.Position.Y < topLeft.Y { topLeft.Y = c.Position.Y } if c.Position.X > bottomRight.X { bottomRight.X = c.Position.X } if c.Position.Y > bottomRight.Y { bottomRight.Y = c.Position.Y } } if debug { fmt.Printf("%+v -> %+v\n", topLeft, bottomRight) } // Adding in missing slots in grid to allow growth. for y := topLeft.Y - 1; y <= bottomRight.Y+1; y++ { for x := topLeft.X - 1; x <= bottomRight.X+1; x++ { pos := Pos{x, y} if _, ok := hg.Cells[pos]; !ok { hg.Cells[pos] = &Cell{ Position: pos, } } } } for _, c := range hg.Cells { xw, xe, x, y := c.Position.X, c.Position.X, c.Position.X, c.Position.Y if y%2 != 0 { xe++ } if y%2 == 0 { xw-- } neighbors := []Pos{ {xe, y - 1}, {x + 1, y}, {xe, y + 1}, {xw, y + 1}, {x - 1, y}, {xw, y - 1}, } var flipped int if debug { fmt.Printf("%+v is %v, neighbors...\n", c.Position, c.Flipped) } for i := range neighbors { cell, ok := hg.Cells[neighbors[i]] if debug { if ok { fmt.Printf("\t%+v is %v\n", cell.Position, cell.Flipped) } else { fmt.Printf("\t%+v [none] \n", neighbors[i]) } } if ok && cell.Flipped { flipped++ } } if debug { fmt.Printf("num flipped: %d\n", flipped) } cell := *c if (cell.Flipped && (flipped == 0 || flipped > 2)) || (!cell.Flipped && flipped == 2) { if debug { fmt.Printf("=> flipping (%v and %d) <==\n", cell.Flipped, flipped) } cell.Flipped = !cell.Flipped } cells[cell.Position] = &cell } hg.Cells = cells } // Task1 ... func Task1(input []string, debug bool) int { hg := HexGrid{ Root: &Cell{}, Cells: make(map[Pos]*Cell), } for _, in := range input { hg.MakeMoves(in, debug) } var result int for _, c := range hg.Cells { if c.Flipped { result++ } } return result } // Task2 ... func Task2(input []string, iterate int, debug bool) int { hg := HexGrid{ Root: &Cell{}, Cells: make(map[Pos]*Cell), } for _, in := range input { hg.MakeMoves(in, debug) } for i := 0; i < iterate; i++ { if debug { fmt.Printf("Day %d:", i+1) } hg.IterateLife(debug) if debug { var result int for _, c := range hg.Cells { if c.Flipped { result++ } } fmt.Printf(" %2d\n", result) } } var result int for _, c := range hg.Cells { if c.Flipped { result++ } } return result } func readInput(reader io.Reader) []string { input := make([]string, 0, 10) scanner := bufio.NewScanner(reader) for scanner.Scan() { input = append(input, scanner.Text()) } return input } func main() { var debug bool flag.BoolVar(&debug, "debug", false, "debug") flag.Parse() var input []string file, _ := os.Open("input.txt") input = readInput(file) file.Close() result := Task1(input, debug) fmt.Printf("Task 1: %d\n", result) result = Task2(input, 100, debug) fmt.Printf("Task 2: %d\n", result) }