diff --git a/day07/cards.go b/day07/cards.go index 4cd409c..67ce34b 100644 --- a/day07/cards.go +++ b/day07/cards.go @@ -259,7 +259,7 @@ func quicksort(a, h []int, hType int) []int { func main() { - file, err := os.Open("./inputs/day7_try") + file, err := os.Open("./inputs/day07_input") check(err) defer file.Close() diff --git a/day08/charpath.go b/day08/charpath.go new file mode 100644 index 0000000..7f8ae28 --- /dev/null +++ b/day08/charpath.go @@ -0,0 +1,233 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "time" + "regexp" + "sync" +) + +const LEFT = 'L' + +type Nodes struct { + mu sync.Mutex + commands []int32 + singleN []int32 + leftN []int32 + rightN []int32 + index int + steps uint64 + allSteps []int +} + +func check(e error) { + if e != nil { + panic(e) + } +} + +func PrintAndWait(x ...any) { + fmt.Print(x...) + fmt.Scanln() +} + +// https://siongui.github.io/2017/06/03/go-find-lcm-by-gcd/ +// greatest common divisor (GCD) via Euclidean algorithm +func GCD(a, b int) int { + for b != 0 { + t := b + b = a % b + a = t + } + return a +} + +// find Least Common Multiple (LCM) via GCD +func LCM(a, b int, integers ...int) int { + result := a * b / GCD(a, b) + + for i := 0; i < len(integers); i++ { + result = LCM(result, integers[i]) + } + + return result +} + +func (n *Nodes) toByteSingle(s string) { + // I've just received something like AAA + var temp int32 + for i := len(s) - 1; i >= 0; i-- { + a := int32(s[i]) + temp += a << ((len(s) - 1 - i) * 8) + } + n.singleN = append(n.singleN, temp) +} + +func (n *Nodes) toByteDuet(s, r string) { + // I've just received something like AAA BBB + var tempL, tempR int32 + for i := len(s) - 1; i >= 0; i-- { + tempL += int32(s[i]) << ((len(s) - 1 - i) * 8) + tempR += int32(r[i]) << ((len(s) - 1 - i) * 8) + } + n.leftN = append(n.leftN, tempL) + n.rightN = append(n.rightN, tempR) +} + +func (n *Nodes) findNext(myN int32) int { + //var wg sync.WaitGroup + ind := 0 + for i := 0; i < len(n.singleN); i++ { + if myN^n.singleN[i] == 0 { + n.mu.Lock() + n.index = i + n.mu.Unlock() + ind = i + break + } + } + return ind +} + +func (n *Nodes) findAll(ind int, sp []int, wg *sync.WaitGroup) { + index := 0 + // We only go from the start + matching := n.rightN[sp[ind]] + if n.commands[0]^LEFT == 0 { + matching = n.leftN[sp[ind]] + } + index = n.findNext(matching) + n.allSteps[ind]++ + i := 0 + for { + // Every step is in a single direction. For every step, we may need to + // scan len(n.singleN) elements. + // Circular loop + index = n.findNext(matching) + // Increment i after finding the match + i++ + i = i % len(n.commands) + // By default, we will assume we are on the right + matching = n.rightN[index] + //PrintAndWait() + // If we are not, we are in the left + if n.commands[i]^LEFT == 0 { + matching = n.leftN[index] + } + n.allSteps[ind]++ + // If we find XXZ, end + temp := matching & 255 + if temp ^ 'Z' == 0 { + break + } + } + //fmt.Printf("I started from %d, matched at %d, taking %d steps.\n", sp[ind], index, n.allSteps[ind] ) + wg.Done() +} + +func timer(name string) func() { + start := time.Now() + return func() { + fmt.Printf("%s took %v\n", name, time.Since(start)) + } +} + +func main() { + defer timer ("main")() + file, err := os.Open("./inputs/day08_input") + check(err) + defer file.Close() + // Struct with my node + n := Nodes{} + // Prepare the regex + repath := regexp.MustCompile("([A-Z]{3})") + // Build the END + var END = 'Z' + END += ('Z' << 8) + END += ('Z' << 16) + + scanner := bufio.NewScanner(file) + scanner.Scan() + // First line, RL commands + strCommands := scanner.Text() + // Get every char inside the string just obtained + for i := 0; i < len(strCommands); i++ { + n.commands = append(n.commands, int32(strCommands[i])) + } + // One empty line + scanner.Scan() + // X = (Y, Z) + // We regex this one + for scanner.Scan() { + tempNodes := repath.FindAllString(scanner.Text(), -1) + n.toByteSingle(tempNodes[0]) + n.toByteDuet(tempNodes[1], tempNodes[2]) + } + // We start from 0, we find the match + // Let's start an infinite loop + // Circular index + i := 0 + // We start from the AAA element + START := 'A' + START += ('A' << 8) + START += ('A' << 16) + matching := START + // Store where AAA is + n.findNext(matching) + // By default, we will assume we are on the right + // If we are not, we are in the left + matching = n.rightN[n.index] + if n.commands[i]^LEFT == 0 { + matching = n.leftN[n.index] + } + n.steps++ + // Infinite loop + for { + // Every step is in a single direction. For every step, we may need to + // scan len(n.singleN) elements. + // Circular loop + n.findNext(matching) + // Increment i after finding the match + i++ + i = i % len(n.commands) + // By default, we will assume we are on the right + matching = n.rightN[n.index] + //PrintAndWait() + // If we are not, we are in the left + if n.commands[i]^LEFT == 0 { + matching = n.leftN[n.index] + } + n.steps++ + // If we find ZZZ, end + if matching^END == 0 { + break + } + } + fmt.Printf("\nSteps: %d\n", n.steps) + // Now, for the main event + // Let's get ready to rumble + startPoints := make([]int, 0) + for i := 0; i < len(n.singleN); i++ { + // Lets remove all bytes except last 8 + temp := n.singleN[i] & 255 + if (temp ^ 'A') == 0 { + startPoints = append(startPoints, i) + } + } + // Now, from the starting points, we should go and match until + // we find a path that ends in Z + n.allSteps = make([]int, len(startPoints)) + var wg sync.WaitGroup + for i := 0; i < len(startPoints); i++ { + wg.Add(1) + go n.findAll(i, startPoints, &wg) + } + wg.Wait() + result := 1 + for i := 0; i < len(n.allSteps); i++ { + result = LCM(result, n.allSteps[i]) + } + fmt.Printf("Steps: %d\n", result) +} diff --git a/day08/inputs b/day08/inputs new file mode 120000 index 0000000..a80bb2b --- /dev/null +++ b/day08/inputs @@ -0,0 +1 @@ +../inputs \ No newline at end of file