Puzzle Part One:
This year, Santa brought little Bobby Tables a set of wires and bitwise logic gates! Unfortunately, little Bobby is a little under the recommended age range, and he needs help assembling the circuit.
Each wire has an identifier (some lowercase letters) and can carry a 16-bit signal (a number from 0
to 65535
). A signal is provided to each wire by a gate, another wire, or some specific value. Each wire can only get a signal from one source, but can provide its signal to multiple destinations. A gate provides no signal until all of its inputs have a signal.
The included instructions booklet describes how to connect the parts together: x AND y -> z
means to connect wires x
and y
to an AND gate, and then connect its output to wire z
.
For example:
123 -> x
means that the signal123
is provided to wirex
.x AND y -> z
means that the bitwise AND of wirex
and wirey
is provided to wirez
.p LSHIFT 2 -> q
means that the value from wirep
is left-shifted by2
and then provided to wireq
.NOT e -> f
means that the bitwise complement of the value from wiree
is provided to wiref
.
Other possible gates include OR
(bitwise OR) and RSHIFT
(right-shift). If, for some reason, you'd like to emulate the circuit instead, almost all programming languages (for example, C, JavaScript, or Python) provide operators for these gates.
For example, here is a simple circuit:
123 -> x
456 -> y
x AND y -> d
x OR y -> e
x LSHIFT 2 -> f
y RSHIFT 2 -> g
NOT x -> h
NOT y -> i
After it is run, these are the signals on the wires:
d: 72
e: 507
f: 492
g: 114
h: 65412
i: 65079
x: 123
y: 456
In little Bobby's kit's instructions booklet (provided as your puzzle input), what signal is ultimately provided to wire a
?
My Solution:
Module Import and Variable Creation: It imports the
input
module and theHashMap
library fromstd::collections
. Then, it creates twoHashMaps
:operations
to store operations andoperations_solved
to keep track of the results of solved operations.Reading and Preparing Data:
Reads an input string (possibly bitwise operation instructions) from
input::get_input()
.Removes "->" characters from the input string.
Splits the input into lines and each line into words, storing these words in
operations
. Each key inoperations
is a wire identifier, and the value is a vector of words representing an operation.
Executing Operations:
Calls the
find_value
function for wire "a" and passes the operations and the solved operationsHashMap
as references.Prints the result of this operation.
Function
is_numeric
:- Checks if a string is numeric, returning
true
if all characters in the string are digits.
- Checks if a string is numeric, returning
Function
find_value
:Recursive, used to find and compute the value for a given key (a wire identifier).
First, it looks in
operations_solved
to see if the value has already been computed.If not, it parses and executes the specified operation in
operations
, which can be a numeric value, NOT, AND, OR, RSHIFT, or LSHIFT.Computes the value, stores it in
operations_solved
, and returns it.
mod input;
use std::collections::HashMap;
fn main() {
let input = input::get_input();
let mut operations: HashMap<&str, Vec<&str>> = HashMap::new();
let mut operations_solved: HashMap<&str, u16> = HashMap::new();
let binding = input.replace("->", "");
// reading input
for operation_string in binding.split("\n") {
// divide usefull words in a vector
let mut words: Vec<&str> = operation_string.trim().split(" ").collect();
// get the character identifier for wire
let key = words.pop().expect("No key found");
// remove las element '->'
words.pop();
// save wire and it's bitwise operation in a hasmap
operations.insert(key, words);
}
//excute operation
let result = find_value("a", &operations, &mut operations_solved);
println!("{}", result)
}
fn is_numeric(value: &str) -> bool {
value.chars().all(char::is_numeric)
}
fn find_value<'a>(
key: &'a str,
operations: &'a HashMap<&str, Vec<&str>>,
operations_solved: &mut HashMap<&'a str, u16>,
) -> u16 {
if let Some(op) = operations_solved.get(key) {
return *op;
}
if let Some(op) = &operations.get(key) {
let result = match op.as_slice() {
[value] => {
if is_numeric(value) {
value.parse::<u16>().expect("conversion error")
} else {
find_value(value, operations, operations_solved)
}
}
["NOT", value] => {
let value_u16 = if is_numeric(value) {
value.parse::<u16>().expect("conversion error")
} else {
find_value(value, operations, operations_solved)
};
!value_u16
}
[left_value, op, right_value] => {
let left_value_u16 = if is_numeric(left_value) {
left_value.parse::<u16>().expect("conversion error")
} else {
find_value(left_value, operations, operations_solved)
};
let right_value_u16 = if is_numeric(right_value) {
right_value.parse::<u16>().expect("conversion error")
} else {
find_value(right_value, operations, operations_solved)
};
match *op {
"AND" => left_value_u16 & right_value_u16,
"OR" => left_value_u16 | right_value_u16,
"RSHIFT" => left_value_u16 >> right_value_u16,
"LSHIFT" => left_value_u16 << right_value_u16,
_ => {
println!("not found");
0
}
}
}
_ => {
println!("not found");
0
}
};
operations_solved.insert(key, result);
result
} else {
println!("not found");
0
}
}
input.rs
(for testing)
pub fn get_input() -> &'static str {
"123 -> x
NOT y -> i
x LSHIFT 2 -> f
x AND y -> d
456 -> y
x OR y -> e
b RSHIFT 2 -> g
NOT x -> h
g -> a
h RSHIFT 2 -> b"
}
Part Two:
Now, take the signal you got on wire a
, override wire b
to that signal, and reset the other wires (including wire a
). What new signal is ultimately provided to wire a
?
My Solution:
The same code is fine, joust only changes the b value.