package main import ( "bufio" "errors" "fmt" "log" "os" "strconv" "strings" ) func main() { doPart := 2 inp := "vbqugkhl" // My puzzle input if len(os.Args) > 1 { if os.Args[1] == "-1" { doPart = 1 } else { inp = strings.ToLower(os.Args[1]) } } if doPart == 1 { part1(inp) } else { part2(inp) } } func part1(inp string) { var diskRows []string for i := 0; i < 128; i++ { diskRows = append(diskRows, KnotHash(fmt.Sprintf("%s-%d", inp, i))) } var usedSquares int for i := range diskRows { bin := GetBinaryString(diskRows[i]) usedSquares += strings.Count(bin, "1") } fmt.Println(usedSquares, "used squares") } var diskGrid map[string]bool var groups map[string]int func part2(inp string) { diskGrid = make(map[string]bool) groups = make(map[string]int) fmt.Println("Building DiskGrid...") var grpCnt int for i := 0; i < 128; i++ { row := GetBinaryString(KnotHash(fmt.Sprintf("%s-%d", inp, i))) for j := range row { diskGrid[cs(i, j)] = (row[j] == '1') if row[j] == '1' { groups[cs(i, j)] = grpCnt grpCnt++ } } } var iters int red := true for red { red = ReduceGroups() iters++ } fmt.Println("Reduced", iters, "times") fmt.Println(GetGroupCount(), "regions") } func ReduceGroups() bool { var ret bool for x := 0; x < 128; x++ { for y := 0; y < 128; y++ { if oV, oOk := diskGrid[cs(x, y)]; oOk && oV { if dV, dOk := diskGrid[cs(x, y-1)]; dOk && dV && groups[cs(x, y-1)] != groups[cs(x, y)] { CombineBlockGroups(cs(x, y), cs(x, y-1)) ret = true } if dV, dOk := diskGrid[cs(x-1, y)]; dOk && dV && groups[cs(x-1, y)] != groups[cs(x, y)] { CombineBlockGroups(cs(x, y), cs(x-1, y)) ret = true } if dV, dOk := diskGrid[cs(x+1, y)]; dOk && dV && groups[cs(x+1, y)] != groups[cs(x, y)] { CombineBlockGroups(cs(x+1, y), cs(x, y)) ret = true } if dV, dOk := diskGrid[cs(x, y+1)]; dOk && dV && groups[cs(x, y+1)] != groups[cs(x, y)] { CombineBlockGroups(cs(x, y+1), cs(x, y)) ret = true } } } } return ret } func CombineBlockGroups(b1, b2 string) { if groups[b1] < groups[b2] { groups[b1] = groups[b2] } else { groups[b2] = groups[b1] } } func GetGroupCount() int { var gps []int for i := range groups { var have bool for j := range gps { if groups[i] == gps[j] { have = true break } } if !have { gps = append(gps, groups[i]) } } return len(gps) } func FindGroup(x, y int) (int, error) { key := cs(x, y) if v, ok := groups[key]; ok { return v, nil } return -1, errors.New("Not in a group") } func PrintUsageChunk(stX, stY, endX, endY int, showGroups bool) { for x := stX; x < endX; x++ { for y := stY; y < endY; y++ { spot := "." if v, ok := groups[cs(x, y)]; ok { if showGroups { v = v % 16 spot = fmt.Sprintf("%x", v) } else { spot = "#" } } fmt.Printf(spot) } fmt.Println("") } } // Get a map coordinate string for x, y func cs(x, y int) string { return fmt.Sprint(x, "-", y) } // Get the x, y coordinate from a string func sc(c string) (int, int) { pts := strings.Split(c, "-") return Atoi(pts[0]), Atoi(pts[1]) } func KnotHash(inp string) string { var idx, skip int var list []int for i := 0; i < 256; i++ { list = append(list, i) } inpBts := []byte(inp) inpBts = append(inpBts, []byte{17, 31, 73, 47, 23}...) for j := 0; j < 64; j++ { for i := range inpBts { idx, skip, list = khRound(int(inpBts[i]), idx, skip, list) } } // Now calculate the dense hash var dense []byte for i := 0; i < len(list); i += 16 { dense = append(dense, xorList(list[i:i+16])) } return fmt.Sprintf("%x", dense) } func khRound(i, idx, skip int, list []int) (int, int, []int) { // if idx+i overflows, pull from the front var revList []int for j := idx; j < idx+i; j++ { revList = append([]int{list[j%256]}, revList...) } for j := 0; j < len(revList); j++ { list[(idx+j)%256] = revList[j] } idx += i + skip skip++ return idx, skip, list } func xorList(inp []int) byte { var ret byte for i := range inp { ret ^= byte(inp[i]) } return ret } func GetBinaryString(inp string) string { var bin string for i := range inp { var v int if inp[i] >= '0' && inp[i] <= '9' { v = int(inp[i] - '0') } else if inp[i] >= 'a' && inp[i] <= 'f' { v = int(inp[i] - 'a' + 10) } nibble := fmt.Sprintf("%04s", strconv.FormatInt(int64(v), 2)) bin += nibble } return bin } func StdinToString() string { var input string scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { input = scanner.Text() } return input } func Atoi(i string) int { var ret int var err error if ret, err = strconv.Atoi(i); err != nil { log.Fatal("Invalid Atoi") } return ret }