mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
day21 solution, boolean/assembly logic is weird
This commit is contained in:
@@ -27,3 +27,4 @@ Day | Name | Type of Algo & Notes
|
||||
18 | Many-Worlds Interpretation | - Oof, this is the hardest algo so far I think... Combination of __Best Pathfinding Algo__ (going to use a BFS/Dijkstra's) and a weighted __Graph Traversal__ with "locked" edges <br> - I toyed with the idea of using some kind of bitmap to track keys that were found, but ditched it so I wouldn't have to write my own wrapper around bitwise logic... Time complexity comparisons are roughly similar for comparing two maps as comparing two sets of bits, the space complexity is probably much better for 27-bits (@ and a-z) compared to a hashmap, but OH WELL. <br> - The graph traversal required memoizing in order to run in any reasonable amount of time. <br> - My part 2 assumptions would not work for every example which is unfortunate :/ I assumed that within a particular quadrant, if the quadrant does not have the key for a door, just remove the door.
|
||||
19 | Tractor Beam | - Another Intcode outputting to a 2D grid <br> - __THERE NEEDS TO BE A CONVENTION FOR X AND Y WHEN WORKING WITH 2D GRIDS__ This is awful, should it be mathematical? Should it represent rows and columns? Who knows?! <br> - Little geometry (drawing squares) for part2 but nothing crazy
|
||||
20 | Donut Maze | __Breadth first search__ path finding algo. Dijkstra's Algorithm to find the shortest path given a maze with "portals." <br> Part 2 is kind of wild with the maze become 3D, but the solution is effectively the same, just more complex to implement <br> - I remapped this in my head to more of a 3D cube instead of a donut... <br> - That was really cool to get working... Dare I say fun...
|
||||
21 | Springdroid Adventure | Another simple-ish Intcode based problem, this time using some weird __Assembly language__ that gets inputs via writing ASCII values to the Intcode computer.
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
Intcode struct is defined within this file
|
||||
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"adventofcode/util"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// read the input file, modify it to a slice of numbers
|
||||
inputFile := util.ReadFile("../input.txt")
|
||||
|
||||
splitStrings := strings.Split(inputFile, ",")
|
||||
|
||||
inputNumbers := make([]int, len(splitStrings))
|
||||
for i, v := range splitStrings {
|
||||
inputNumbers[i], _ = strconv.Atoi(v)
|
||||
}
|
||||
|
||||
comp := MakeComputer(inputNumbers)
|
||||
|
||||
// Boolean, assembly logic...
|
||||
// if B OR C are holes but D is safe. jump
|
||||
// otherwise jump if A is a hole
|
||||
instruction := convertStringToASCII("NOT B T\nNOT C J\nOR J T\nAND D J\nAND J T\n")
|
||||
instruction = append(instruction, convertStringToASCII("NOT A J\nOR T J\nWALK\n")...)
|
||||
for _, v := range instruction {
|
||||
// fmt.Println(v)
|
||||
comp.Step(v)
|
||||
}
|
||||
|
||||
// Print the output TEXT
|
||||
for _, v := range comp.Outputs {
|
||||
fmt.Printf("%v", string(v))
|
||||
}
|
||||
|
||||
fmt.Printf("Hull damage: %v\n", comp.Outputs[len(comp.Outputs)-1])
|
||||
}
|
||||
|
||||
// helper function to convert a string to its character's ASCII values
|
||||
func convertStringToASCII(input string) []int {
|
||||
result := make([]int, len(input))
|
||||
for i, v := range input {
|
||||
// cast rune to int
|
||||
result[i] = int(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
Intcode is an OOP approach *************************************************
|
||||
MakeComputer is equivalent to the constructor
|
||||
Step takes in an input int and updates properties in the computer:
|
||||
- InstructionIndex: where to read the next instruction from
|
||||
- LastOutput, what the last opcode 4 outputted
|
||||
- PuzzleIndex based if the last instruction modified the puzzle at all
|
||||
****************************************************************************/
|
||||
type Intcode struct {
|
||||
PuzzleInput []int // file/puzzle input parsed into slice of ints
|
||||
InstructionIndex int // stores the index where the next instruction is
|
||||
RelativeBase int // relative base for opcode 9 and param mode 2
|
||||
Outputs []int // stores all outputs
|
||||
IsRunning bool // will be true until a 99 opcode is hit
|
||||
}
|
||||
|
||||
// MakeComputer initializes a new comp
|
||||
func MakeComputer(PuzzleInput []int) *Intcode {
|
||||
puzzleInputCopy := make([]int, len(PuzzleInput))
|
||||
copy(puzzleInputCopy, PuzzleInput)
|
||||
|
||||
comp := Intcode{
|
||||
puzzleInputCopy,
|
||||
0,
|
||||
0,
|
||||
make([]int, 0),
|
||||
true,
|
||||
}
|
||||
return &comp
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// else recurse with a -1 to signal the initial input has been processed
|
||||
comp.PuzzleInput[param1] = input
|
||||
comp.InstructionIndex += 2
|
||||
|
||||
// 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
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GetOpCodeAndParamIndexes will parse the instruction at comp.PuzzleInput[comp.InstructionIndex]
|
||||
- opcode will be the left two digits, mod by 100 will get that
|
||||
- rest of instructions will be grabbed via mod 10
|
||||
- these also have to be parsed for the
|
||||
*/
|
||||
func (comp *Intcode) GetOpCodeAndParamIndexes() (int, [3]int) {
|
||||
instruction := comp.PuzzleInput[comp.InstructionIndex]
|
||||
|
||||
// opcode is the lowest two digits, so mod by 100
|
||||
opcode := instruction % 100
|
||||
instruction /= 100
|
||||
|
||||
// assign the indexes that need to be read by reading the parameter modes
|
||||
var paramIndexes [3]int
|
||||
for i := 1; i <= 3 && comp.InstructionIndex+i < len(comp.PuzzleInput); i++ {
|
||||
// grab the mode with a mod, last digit
|
||||
mode := instruction % 10
|
||||
instruction /= 10
|
||||
|
||||
switch mode {
|
||||
case 0: // position mode, index will be the value at the index
|
||||
paramIndexes[i-1] = comp.PuzzleInput[comp.InstructionIndex+i]
|
||||
case 1: // immediate mode, the index itself
|
||||
paramIndexes[i-1] = comp.InstructionIndex + i
|
||||
case 2: // relative mode, like position mode but index is added to relative base
|
||||
paramIndexes[i-1] = comp.PuzzleInput[comp.InstructionIndex+i] + comp.RelativeBase
|
||||
}
|
||||
}
|
||||
|
||||
return opcode, paramIndexes
|
||||
}
|
||||
|
||||
// ResizeMemory will take any number of integers and resize the computer's memory appropriately
|
||||
func (comp *Intcode) ResizeMemory(sizes ...int) {
|
||||
// get largest of input sizes
|
||||
maxArg := sizes[0]
|
||||
for _, v := range sizes {
|
||||
if v > maxArg {
|
||||
maxArg = v
|
||||
}
|
||||
}
|
||||
|
||||
// resize if PuzzleInput's length is shorter
|
||||
if maxArg >= len(comp.PuzzleInput) {
|
||||
// make empty slice to copy into, of the new, larger size
|
||||
resizedPuzzleInput := make([]int, maxArg+1)
|
||||
// copy old puzzle input values in
|
||||
copy(resizedPuzzleInput, comp.PuzzleInput)
|
||||
|
||||
// overwrite puzzle input
|
||||
comp.PuzzleInput = resizedPuzzleInput
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
Intcode struct is defined within this file
|
||||
Helper function that converts strings to ASCII codes to be written to the computer
|
||||
it could all be combined together into a new computer...
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"adventofcode/util"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// read the input file, modify it to a slice of numbers
|
||||
inputFile := util.ReadFile("../input.txt")
|
||||
|
||||
splitStrings := strings.Split(inputFile, ",")
|
||||
|
||||
inputNumbers := make([]int, len(splitStrings))
|
||||
for i, v := range splitStrings {
|
||||
inputNumbers[i], _ = strconv.Atoi(v)
|
||||
}
|
||||
|
||||
comp := MakeComputer(inputNumbers)
|
||||
|
||||
// Boolean, assembly logic...
|
||||
// if B OR C are holes but D is safe. jump
|
||||
// modify this logic to also include "is H ground"
|
||||
instruction := convertStringToASCII("NOT B J\nNOT C T\nOR T J\nAND D J\nAND H J\n")
|
||||
// otherwise jump if A is a hole
|
||||
instruction = append(instruction, convertStringToASCII("NOT A T\nOR T J\n")...)
|
||||
instruction = append(instruction, convertStringToASCII("RUN\n")...)
|
||||
|
||||
// write instructions to intcode/ascii computer
|
||||
for _, v := range instruction {
|
||||
// fmt.Println(v)
|
||||
comp.Step(v)
|
||||
}
|
||||
|
||||
// Print the output TEXT
|
||||
for _, v := range comp.Outputs {
|
||||
fmt.Printf("%v", string(v))
|
||||
}
|
||||
|
||||
fmt.Printf("Hull damage: %v\n", comp.Outputs[len(comp.Outputs)-1])
|
||||
}
|
||||
|
||||
// helper function to convert a string to its character's ASCII values
|
||||
func convertStringToASCII(input string) []int {
|
||||
result := make([]int, len(input))
|
||||
for i, v := range input {
|
||||
// cast rune to int
|
||||
result[i] = int(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
Intcode is an OOP approach *************************************************
|
||||
MakeComputer is equivalent to the constructor
|
||||
Step takes in an input int and updates properties in the computer:
|
||||
- InstructionIndex: where to read the next instruction from
|
||||
- LastOutput, what the last opcode 4 outputted
|
||||
- PuzzleIndex based if the last instruction modified the puzzle at all
|
||||
****************************************************************************/
|
||||
type Intcode struct {
|
||||
PuzzleInput []int // file/puzzle input parsed into slice of ints
|
||||
InstructionIndex int // stores the index where the next instruction is
|
||||
RelativeBase int // relative base for opcode 9 and param mode 2
|
||||
Outputs []int // stores all outputs
|
||||
IsRunning bool // will be true until a 99 opcode is hit
|
||||
}
|
||||
|
||||
// MakeComputer initializes a new comp
|
||||
func MakeComputer(PuzzleInput []int) *Intcode {
|
||||
puzzleInputCopy := make([]int, len(PuzzleInput))
|
||||
copy(puzzleInputCopy, PuzzleInput)
|
||||
|
||||
comp := Intcode{
|
||||
puzzleInputCopy,
|
||||
0,
|
||||
0,
|
||||
make([]int, 0),
|
||||
true,
|
||||
}
|
||||
return &comp
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// else recurse with a -1 to signal the initial input has been processed
|
||||
comp.PuzzleInput[param1] = input
|
||||
comp.InstructionIndex += 2
|
||||
|
||||
// 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
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GetOpCodeAndParamIndexes will parse the instruction at comp.PuzzleInput[comp.InstructionIndex]
|
||||
- opcode will be the left two digits, mod by 100 will get that
|
||||
- rest of instructions will be grabbed via mod 10
|
||||
- these also have to be parsed for the
|
||||
*/
|
||||
func (comp *Intcode) GetOpCodeAndParamIndexes() (int, [3]int) {
|
||||
instruction := comp.PuzzleInput[comp.InstructionIndex]
|
||||
|
||||
// opcode is the lowest two digits, so mod by 100
|
||||
opcode := instruction % 100
|
||||
instruction /= 100
|
||||
|
||||
// assign the indexes that need to be read by reading the parameter modes
|
||||
var paramIndexes [3]int
|
||||
for i := 1; i <= 3 && comp.InstructionIndex+i < len(comp.PuzzleInput); i++ {
|
||||
// grab the mode with a mod, last digit
|
||||
mode := instruction % 10
|
||||
instruction /= 10
|
||||
|
||||
switch mode {
|
||||
case 0: // position mode, index will be the value at the index
|
||||
paramIndexes[i-1] = comp.PuzzleInput[comp.InstructionIndex+i]
|
||||
case 1: // immediate mode, the index itself
|
||||
paramIndexes[i-1] = comp.InstructionIndex + i
|
||||
case 2: // relative mode, like position mode but index is added to relative base
|
||||
paramIndexes[i-1] = comp.PuzzleInput[comp.InstructionIndex+i] + comp.RelativeBase
|
||||
}
|
||||
}
|
||||
|
||||
return opcode, paramIndexes
|
||||
}
|
||||
|
||||
// ResizeMemory will take any number of integers and resize the computer's memory appropriately
|
||||
func (comp *Intcode) ResizeMemory(sizes ...int) {
|
||||
// get largest of input sizes
|
||||
maxArg := sizes[0]
|
||||
for _, v := range sizes {
|
||||
if v > maxArg {
|
||||
maxArg = v
|
||||
}
|
||||
}
|
||||
|
||||
// resize if PuzzleInput's length is shorter
|
||||
if maxArg >= len(comp.PuzzleInput) {
|
||||
// make empty slice to copy into, of the new, larger size
|
||||
resizedPuzzleInput := make([]int, maxArg+1)
|
||||
// copy old puzzle input values in
|
||||
copy(resizedPuzzleInput, comp.PuzzleInput)
|
||||
|
||||
// overwrite puzzle input
|
||||
comp.PuzzleInput = resizedPuzzleInput
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
--- Day 21: Springdroid Adventure ---
|
||||
You lift off from Pluto and start flying in the direction of Santa.
|
||||
|
||||
While experimenting further with the tractor beam, you accidentally pull an asteroid directly into your ship! It deals significant damage to your hull and causes your ship to begin tumbling violently.
|
||||
|
||||
You can send a droid out to investigate, but the tumbling is causing enough artificial gravity that one wrong step could send the droid through a hole in the hull and flying out into space.
|
||||
|
||||
The clear choice for this mission is a droid that can jump over the holes in the hull - a springdroid.
|
||||
|
||||
You can use an Intcode program (your puzzle input) running on an ASCII-capable computer to program the springdroid. However, springdroids don't run Intcode; instead, they run a simplified assembly language called springscript.
|
||||
|
||||
While a springdroid is certainly capable of navigating the artificial gravity and giant holes, it has one downside: it can only remember at most 15 springscript instructions.
|
||||
|
||||
The springdroid will move forward automatically, constantly thinking about whether to jump. The springscript program defines the logic for this decision.
|
||||
|
||||
Springscript programs only use Boolean values, not numbers or strings. Two registers are available: T, the temporary value register, and J, the jump register. If the jump register is true at the end of the springscript program, the springdroid will try to jump. Both of these registers start with the value false.
|
||||
|
||||
Springdroids have a sensor that can detect whether there is ground at various distances in the direction it is facing; these values are provided in read-only registers. Your springdroid can detect ground at four distances: one tile away (A), two tiles away (B), three tiles away (C), and four tiles away (D). If there is ground at the given distance, the register will be true; if there is a hole, the register will be false.
|
||||
|
||||
There are only three instructions available in springscript:
|
||||
|
||||
AND X Y sets Y to true if both X and Y are true; otherwise, it sets Y to false.
|
||||
OR X Y sets Y to true if at least one of X or Y is true; otherwise, it sets Y to false.
|
||||
NOT X Y sets Y to true if X is false; otherwise, it sets Y to false.
|
||||
In all three instructions, the second argument (Y) needs to be a writable register (either T or J). The first argument (X) can be any register (including A, B, C, or D).
|
||||
|
||||
For example, the one-instruction program NOT A J means "if the tile immediately in front of me is not ground, jump".
|
||||
|
||||
Or, here is a program that jumps if a three-tile-wide hole (with ground on the other side of the hole) is detected:
|
||||
|
||||
NOT A J
|
||||
NOT B T
|
||||
AND T J
|
||||
NOT C T
|
||||
AND T J
|
||||
AND D J
|
||||
The Intcode program expects ASCII inputs and outputs. It will begin by displaying a prompt; then, input the desired instructions one per line. End each line with a newline (ASCII code 10). When you have finished entering your program, provide the command WALK followed by a newline to instruct the springdroid to begin surveying the hull.
|
||||
|
||||
If the springdroid falls into space, an ASCII rendering of the last moments of its life will be produced. In these, @ is the springdroid, # is hull, and . is empty space. For example, suppose you program the springdroid like this:
|
||||
|
||||
NOT D J
|
||||
WALK
|
||||
This one-instruction program sets J to true if and only if there is no ground four tiles away. In other words, it attempts to jump into any hole it finds:
|
||||
|
||||
.................
|
||||
.................
|
||||
@................
|
||||
#####.###########
|
||||
|
||||
.................
|
||||
.................
|
||||
.@...............
|
||||
#####.###########
|
||||
|
||||
.................
|
||||
..@..............
|
||||
.................
|
||||
#####.###########
|
||||
|
||||
...@.............
|
||||
.................
|
||||
.................
|
||||
#####.###########
|
||||
|
||||
.................
|
||||
....@............
|
||||
.................
|
||||
#####.###########
|
||||
|
||||
.................
|
||||
.................
|
||||
.....@...........
|
||||
#####.###########
|
||||
|
||||
.................
|
||||
.................
|
||||
.................
|
||||
#####@###########
|
||||
However, if the springdroid successfully makes it across, it will use an output instruction to indicate the amount of damage to the hull as a single giant integer outside the normal ASCII range.
|
||||
|
||||
Program the springdroid with logic that allows it to survey the hull without falling into space. What amount of hull damage does it report?
|
||||
|
||||
Your puzzle answer was 19350938.
|
||||
|
||||
--- Part Two ---
|
||||
There are many areas the springdroid can't reach. You flip through the manual and discover a way to increase its sensor range.
|
||||
|
||||
Instead of ending your springcode program with WALK, use RUN. Doing this will enable extended sensor mode, capable of sensing ground up to nine tiles away. This data is available in five new read-only registers:
|
||||
|
||||
Register E indicates whether there is ground five tiles away.
|
||||
Register F indicates whether there is ground six tiles away.
|
||||
Register G indicates whether there is ground seven tiles away.
|
||||
Register H indicates whether there is ground eight tiles away.
|
||||
Register I indicates whether there is ground nine tiles away.
|
||||
All other functions remain the same.
|
||||
|
||||
Successfully survey the rest of the hull by ending your program with RUN. What amount of hull damage does the springdroid now report?
|
||||
|
||||
Your puzzle answer was 1142986901.
|
||||
|
||||
Both parts of this puzzle are complete! They provide two gold stars: **
|
||||
Reference in New Issue
Block a user