diff --git a/README.md b/README.md index 93fdcce..12e452d 100644 --- a/README.md +++ b/README.md @@ -23,5 +23,5 @@ Day | Name | Type of Algo & Notes 14 | Space Stoichiometry | __Weighted Graph and Breadth First Traversals__
- Because not all of the products have a quantity of 1, it complicates the graph's data model. I ended up mapping the product/chemical name to a map of its stoichiometry where the product's number is positive & reactants were negative.
- part2: not just a simple division because of "extra byproducts" of generating each piece of fuel. I just let this brute force thing run for ~30 seconds... 15 | Oxygen System | YAY INTCODE 🙄
- Combination of __2D grid searching algo__, __backtracking algo__ and the the Intcode...
- I've realized that I really need to stop using x and y for 2D grids and start using row and col because mathematically x is horizontal and y is vertical... My brain is all jumbled up
- Created a Robot struct/class that has a computer inside of it. It goes and searches around, collecting data on the floor types at various coordinates. That data is transformed into a 2D grid/array, and then finally fed into a backtracking, searching algorithm to determine the shortest path (turns out there's only one path to the O2 tank...)
- part2 is fairly straight forward 2D grid traversing and tagging a spread of oxygen to valid tiles/hallway spaces 16 | Flawed Frequency Transmission | - Some really weird, contrived (as if this whole thing isn't) phase calculator?..
- part2 broke my brain. Optimally calculate subsets by __precalculating running sums__ (linear), then getting subsets by subtracting two of the precalculated running sums. i.e. sub[2:4] = sub[0:4] - sub[0:2]
- And also switching pattern recognition to make calculating a particular output O(nlogn)... -17 | Set and Forget | More Intcode...
Robot walking around a scaffolding... Fairly similar to previous algos, and a 2D grid traversal again
- I feel like I cheated part2 by finding the pattern by eye after printing what the 2D grid looks like. Then it was a matter of giving the intcode computer the corresponding inputs (in a weird format).
- I need a big refactor of the intcode compiler to make it iterative (instead of recursive) because it's blowing the call stack in "continuous video feed" mode :( +17 | Set and Forget | More Intcode...
Robot walking around a scaffolding... Fairly similar to previous algos, and a 2D grid traversal again
- I feel like I cheated part2 by finding the pattern by eye after printing what the 2D grid looks like. Then it was a matter of giving the intcode computer the corresponding inputs (in a weird format).
- Good example of __iterative approaches being better than recursive approaches__ because the recursive approach in "continuous video mode" causes a stack overflow very quickly diff --git a/day17/part2/main.go b/day17/part2/main.go index fc7a11f..eea129d 100644 --- a/day17/part2/main.go +++ b/day17/part2/main.go @@ -11,6 +11,7 @@ import ( "log" "strconv" "strings" + "time" ) func main() { @@ -92,8 +93,7 @@ func main() { } // finally, asks for continuous video feed or not 'y' or 'n' and a new line - // NOTE making this 'y' will cause a stack overflow with my recursive implementation :( - robot.computer.Step(int('n')) + robot.computer.Step(int('y')) robot.computer.Step(newline) fmt.Println("Dust collected", robot.computer.Outputs[len(robot.computer.Outputs)-1]) @@ -174,120 +174,103 @@ func MakeComputer(PuzzleInput []int) *Intcode { // Step will read the next 4 values in the input `sli` and make updates // according to the opcodes +// Update to run iteratively (while the computer is running) +// it will also return out if a -1 input is asked for +// then call Step again to provide the next input, or run with -1 from the start +// to run the computer until it asks for an input OR terminates func (comp *Intcode) Step(input int) { - // read the instruction, opcode and the indexes where the params point to - opcode, paramIndexes := comp.GetOpCodeAndParamIndexes() - param1, param2, param3 := paramIndexes[0], paramIndexes[1], paramIndexes[2] + for comp.IsRunning { + // read the instruction, opcode and the indexes where the params point to + opcode, paramIndexes := comp.GetOpCodeAndParamIndexes() + param1, param2, param3 := paramIndexes[0], paramIndexes[1], paramIndexes[2] - // ensure params are within the bounds of PuzzleInput, resize if necessary - switch opcode { - case 1, 2, 7, 8: - comp.ResizeMemory(param1, param2, param3) - case 5, 6: - comp.ResizeMemory(param1, param2) - case 3, 4, 9: - comp.ResizeMemory(param1) - } - - switch opcode { - case 99: // 99: Terminates program - fmt.Println("Terminating...") - comp.IsRunning = false - case 1: // 1: Add next two paramIndexes, store in third - comp.PuzzleInput[param3] = comp.PuzzleInput[param1] + comp.PuzzleInput[param2] - comp.InstructionIndex += 4 - comp.Step(input) - case 2: // 2: Multiply next two and store in third - comp.PuzzleInput[param3] = comp.PuzzleInput[param1] * comp.PuzzleInput[param2] - comp.InstructionIndex += 4 - comp.Step(input) - case 3: // 3: Takes one input and saves it to position of one parameter - // check if input has already been used (i.e. input == -1) - // if it's been used, return out to prevent further Steps - // NOTE: making a big assumption that -1 will never be an input... - if input == -1 { - return + // ensure params are within the bounds of PuzzleInput, resize if necessary + switch opcode { + case 1, 2, 7, 8: + comp.ResizeMemory(param1, param2, param3) + case 5, 6: + comp.ResizeMemory(param1, param2) + case 3, 4, 9: + comp.ResizeMemory(param1) } - // else recurse with a -1 to signal the initial input has been processed - comp.PuzzleInput[param1] = input - comp.InstructionIndex += 2 - comp.Step(-1) - case 4: // 4: outputs its input value - // set LastOutput of the computer & log it - comp.Outputs = append(comp.Outputs, comp.PuzzleInput[param1]) - // fmt.Println("Output", comp.PuzzleInput[param1]) + switch opcode { + case 99: // 99: Terminates program + fmt.Println("Terminating...") + comp.IsRunning = false + case 1: // 1: Add next two paramIndexes, store in third + comp.PuzzleInput[param3] = comp.PuzzleInput[param1] + comp.PuzzleInput[param2] + comp.InstructionIndex += 4 + case 2: // 2: Multiply next two and store in third + comp.PuzzleInput[param3] = comp.PuzzleInput[param1] * comp.PuzzleInput[param2] + comp.InstructionIndex += 4 + case 3: // 3: Takes one input and saves it to position of one parameter + // check if input has already been used (i.e. input == -1) + // if it's been used, return out to prevent further Steps + // NOTE: making a big assumption that -1 will never be an input... + if input == -1 { + return + } - // TODO come back to this b/c the entire Intcode machine needs to be made not-recursive - // todo for this to not blow the call stack - // // NOTE uncomment to view robot moving around floor - // if len(comp.Outputs) == 1967 { - // var row int - // floorGrid := [][]string{[]string{}} - // for _, v := range comp.Outputs { - // switch v { - // case 10: - // row++ - // floorGrid = append(floorGrid, []string{}) - // default: - // tileType := string(v) - // floorGrid[row] = append(floorGrid[row], tileType) - // } - // } + // else recurse with a -1 to signal the initial input has been processed + comp.PuzzleInput[param1] = input + comp.InstructionIndex += 2 - // for _, v := range floorGrid { - // fmt.Println(v) - // } - // fmt.Println("") - // comp.Outputs = []int{} - // time.Sleep(time.Second) - // } + // change the input value so the next time a 3 opcode is hit, will return out + input = -1 + case 4: // 4: outputs its input value + output := comp.PuzzleInput[param1] + // set LastOutput of the computer & log it + comp.Outputs = append(comp.Outputs, output) - comp.InstructionIndex += 2 + // NOTE this is specific to day17 to print the robot walking around the scaffold + // if the last two outputs are newlines (ASCII 10's), print out the output + // then just clear the output to make life easy + if output == 10 && comp.Outputs[len(comp.Outputs)-2] == 10 { + Print2DGrid(comp.Outputs) + // clear outputs slice, sleep for 100ms + comp.Outputs = []int{} + time.Sleep(time.Millisecond * 100) + } - // continue running until terminates or asks for another input - comp.Step(input) - // 5: jump-if-true: if first param != 0, move pointer to second param, else nothing - case 5: - if comp.PuzzleInput[param1] != 0 { - comp.InstructionIndex = comp.PuzzleInput[param2] - } else { - comp.InstructionIndex += 3 + comp.InstructionIndex += 2 + // 5: jump-if-true: if first param != 0, move pointer to second param, else nothing + case 5: + if comp.PuzzleInput[param1] != 0 { + comp.InstructionIndex = comp.PuzzleInput[param2] + } else { + comp.InstructionIndex += 3 + } + // 6: jump-if-false, if first param == 0 then set instruction pointer to 2nd param, else nothing + case 6: + if comp.PuzzleInput[param1] == 0 { + comp.InstructionIndex = comp.PuzzleInput[param2] + } else { + comp.InstructionIndex += 3 + } + // 7: less-than, if param1 < param2 then store 1 in postion of 3rd param, else store 0 + case 7: + if comp.PuzzleInput[param1] < comp.PuzzleInput[param2] { + comp.PuzzleInput[param3] = 1 + } else { + comp.PuzzleInput[param3] = 0 + } + comp.InstructionIndex += 4 + // 8: equals, if param1 == param2 then set position of 3rd param to 1, else store 0 + case 8: + if comp.PuzzleInput[param1] == comp.PuzzleInput[param2] { + comp.PuzzleInput[param3] = 1 + } else { + comp.PuzzleInput[param3] = 0 + } + comp.InstructionIndex += 4 + // 9: adjust relative base + case 9: + comp.RelativeBase += comp.PuzzleInput[param1] + comp.InstructionIndex += 2 + default: + log.Fatalf("Error: unknown opcode %v at index %v", opcode, comp.PuzzleInput[comp.InstructionIndex]) } - comp.Step(input) - // 6: jump-if-false, if first param == 0 then set instruction pointer to 2nd param, else nothing - case 6: - if comp.PuzzleInput[param1] == 0 { - comp.InstructionIndex = comp.PuzzleInput[param2] - } else { - comp.InstructionIndex += 3 - } - comp.Step(input) - // 7: less-than, if param1 < param2 then store 1 in postion of 3rd param, else store 0 - case 7: - if comp.PuzzleInput[param1] < comp.PuzzleInput[param2] { - comp.PuzzleInput[param3] = 1 - } else { - comp.PuzzleInput[param3] = 0 - } - comp.InstructionIndex += 4 - comp.Step(input) - // 8: equals, if param1 == param2 then set position of 3rd param to 1, else store 0 - case 8: - if comp.PuzzleInput[param1] == comp.PuzzleInput[param2] { - comp.PuzzleInput[param3] = 1 - } else { - comp.PuzzleInput[param3] = 0 - } - comp.InstructionIndex += 4 - comp.Step(input) - // 9: adjust relative base - case 9: - comp.RelativeBase += comp.PuzzleInput[param1] - comp.InstructionIndex += 2 - comp.Step(input) - default: - log.Fatalf("Error: unknown opcode %v at index %v", opcode, comp.PuzzleInput[comp.InstructionIndex]) } } @@ -345,3 +328,23 @@ func (comp *Intcode) ResizeMemory(sizes ...int) { comp.PuzzleInput = resizedPuzzleInput } } + +// Print2DGrid is day17 specific. allValues are ASCII ints including 10 for newline +func Print2DGrid(allValues []int) { + var row int + floorGrid := [][]string{[]string{}} + for _, v := range allValues { + switch v { + case 10: + row++ + floorGrid = append(floorGrid, []string{}) + default: + tileType := string(v) + floorGrid[row] = append(floorGrid[row], tileType) + } + } + + for _, v := range floorGrid { + fmt.Println(v) + } +}