Advent of Code 2015
2026-03-09
Puzzle: https://adventofcode.com/2015/day/7
My solution: https://codeberg.org/adfoucart/aocode15/src/branch/main/src/day7.c
In part 1, we didn’t even attempt yet to solve the puzzle. We are, however, building a fairly solid foundation to do so, so let’s keep going. We ended with saying that we need:
So let’s get started with that.
Don’t forget to use ** for the wires, as we want a list
of pointers:
typedef struct {
wire_t** wires;
int n;
int capacity;
} list_wires_t;With methods to create, destroy, and add a wire to the list:
list_wires_t* list_wires_create(int initial_capacity){
list_wires_t* list = malloc(sizeof(list_wires_t));
list->n = 0;
list->capacity = initial_capacity;
list->wires = malloc(sizeof(wire_t*)*list->capacity);
return list;
}
void list_wires_destroy(list_wires_t* list){
for (int i=0; i < list->n; i++)
wire_destroy(list->wires[i]);
free(list->wires);
free(list);
}
void list_wires_add(list_wires_t* list, wire_t* wire){
if (list->n == list->capacity){
list->wires = realloc(list->wires, sizeof(wire_t*)*list->capacity*2);
list->capacity *= 2;
}
list->wires[list->n] = wire;
list->n++;
}To test it, we can update the test_dependency_map test,
adding also a print method for convenience:
void list_wires_print(list_wires_t* list){
printf("List of %d wires:\n", list->n);
for (int i=0; i < list->n; i++){
wire_print(list->wires[i]);
}
}
void test_dependency_map(){
list_wires_t* list = list_wires_create(6);
wire_t* w123 = wire_create("w123", (uint16_t) 123, true);
wire_t* w4 = wire_create("w4", (uint16_t) 4, true);
wire_t* c = wire_create("c", 0, false);
wire_t* dd = wire_create("dd", 0, false);
wire_t* bb = wire_create("bb", 0, false);
wire_t* a = wire_create("a", 0, false);
list_wires_add(list, w123);
list_wires_add(list, w4);
list_wires_add(list, c);
list_wires_add(list, dd);
list_wires_add(list, bb);
list_wires_add(list, a);
wire_add_dependency(c, a);
wire_add_dependency(c, bb);
wire_add_dependency(dd, c);
wire_add_dependency(dd, w123);
wire_add_dependency(bb, w4);
wire_add_dependency(a, bb);
list_wires_print(list);
list_wires_destroy(list);
}Nothing breaks, everything prints.
I was initially thinking about going through the wire list multiple times, solving only wires which only depends on already-solved wires, until all are solved. But since the puzzle only asks for the solution of one specific wire, I think I’d rather go recursive: try to solve the wire, and recursively solve the dependencies as needed.
I realize as I started writing this function that I may have
forgotten a little detail: I don’t record the operation
anywhere in the wire_t at the moment. Oops. Before worrying
about that, though, I’d still write the solver. I’ll just assume that
every operation is the binary AND for the moment:
void wire_solve(wire_t* wire){
if (wire->is_set)
return;
for (int i = 0; i < wire->n_dependencies; i++)
if (!wire->dependencies[i]->is_set)
wire_solve(wire->dependencies[i]);
// apply the operation on the wire. Let's just do a "&" for now
uint16_t value = wire->dependencies[0]->value;
for (int i = 1; i < wire->n_dependencies; i++){
value = value & wire->dependencies[i]->value;
}
wire->value = value;
wire->is_set = true;
return;
}I can try it on my test input, but now I have to determine the result
manually for all &. Let’s go back to the wiring and use
the new definition:
a AND bb -> c
c AND 123 -> dd
4 -> bb
bb AND 3 -> a
Solving for c:
a: 4 & 3 = 100 & 011 = 000 = 0
c: 0 & 4 = 0
Perhaps not the best example, but at least we’ll see if it completely breaks.
At the end of the test, if I do:
list_wires_print(list);
wire_solve(c);
printf("After solving c:\n");
list_wires_print(list);I should have values for c, bb and
a, but not for dd, which is not necessary for
solving c.
Nothing crashes, but I get:
After solving c:
List of 6 wires:
w123: [], value=123
w4: [], value=4
c: [a,bb,], value=4
dd: [c,w123,], value=?
bb: [w4,], value=4
a: [bb,], value=4Which… well, I see that I didn’t write my example properly, I only
made a depend on bb. Adding:
void test_dependency_map(){
//...
wire_t* w3 = wire_create("w3", (uint16_t) 3, true);
//...
list_wires_add(list, w3);
//...
wire_add_dependency(a, w3);
}Running again, what do we get?
List of 7 wires:
w123: [], value=123
w4: [], value=4
c: [a,bb,], value=0
dd: [c,w123,], value=?
bb: [w4,], value=4
a: [bb,w3,], value=0
w3: [], value=3Looks good to me. Not the cleanest test, but I think we have a good shot at solving this… Once we store the operation and parse the input…
We’ll store the operations as integers, so let’s define them first,
adding a NOOP for the wires where the value is set
directly:
#define NOOP 0
#define LOGICAL_AND 1
#define LOGICAL_OR 2
#define LSHIFT 3
#define RSHIFT 4
#define LOGICAL_NOT 5Add to wire_t and to the related methods:
struct wire_t {
// ...
int operation;
};
wire_t* wire_create(char* name, uint16_t value, int operation, bool is_set){
if (operation < 0 || operation > 5){
printf("ERROR: invalid operation");
exit(1);
}
// ...
wire->operation = operation;
// ...
}And now in the test I can do:
wire_t* w123 = wire_create("w123", (uint16_t) 123, NOOP, true);
wire_t* w4 = wire_create("w4", (uint16_t) 4, NOOP, true);
wire_t* c = wire_create("c", 0, LOGICAL_AND, false);
wire_t* dd = wire_create("dd", 0, LOGICAL_OR, false);
wire_t* bb = wire_create("bb", 0, NOOP, false);
wire_t* a = wire_create("a", 0, LSHIFT, false);
wire_t* w3 = wire_create("w3", (uint16_t) 3, NOOP, true);We don’t take the operations into account yet, but everything runs. I don’t really like how we set the operation before the dependencies, but I don’t see an easy way to avoid that yet. I’ll think about it later. Let’s put the operations in their own little function now to make things easier.
void dependencies_and(wire_t* wire){
uint16_t value = wire->dependencies[0]->value;
for (int i = 1; i < wire->n_dependencies; i++){
value = value & wire->dependencies[i]->value;
}
wire->value = value;
wire->is_set = true;
}
void dependencies_or(wire_t* wire){
uint16_t value = wire->dependencies[0]->value;
for (int i = 1; i < wire->n_dependencies; i++){
value = value | wire->dependencies[i]->value;
}
wire->value = value;
wire->is_set = true;
}
void dependencies_lshift(wire_t* wire){
if (wire->n_dependencies != 2){
printf("ERROR: LSHIFT needs exactly two operands\n");
exit(1);
}
wire->value = wire->dependencies[0]->value << wire->dependencies[1]->value;
wire->is_set = true;
}
void dependencies_rshift(wire_t* wire){
if (wire->n_dependencies != 2){
printf("ERROR: RSHIFT needs exactly two operands\n");
exit(1);
}
wire->value = wire->dependencies[0]->value >> wire->dependencies[1]->value;
wire->is_set = true;
}
void dependencies_not(wire_t* wire){
if (wire->n_dependencies != 1){
printf("ERROR: NOT needs exactly one operand\n");
exit(1);
}
wire->value = ~wire->dependencies[0]->value;
wire->is_set = true;
}
void dependencies_noop(wire_t* wire){
if (wire->n_dependencies == 1){
wire->value = wire->dependencies[0]->value;
}
}Not loving that at all, some refactoring will probably be needed later. But I fear I’m too close to actually solving the puzzle to resist moving forward.
In the solver:
void wire_solve(wire_t* wire){
// ...
if (wire->operation == NOOP)
dependencies_noop(wire);
else if (wire->operation == LOGICAL_AND)
dependencies_and(wire);
else if (wire->operation == LOGICAL_OR)
dependencies_or(wire);
else if (wire->operation == LSHIFT)
dependencies_lshift(wire);
else if (wire->operation == RSHIFT)
dependencies_rshift(wire);
// ...
}Let’s try solving for dd this time, since that’s what I
computed manually for my test before. I get a value of 123,
which is what I was expecting. Great!
On to parsing, then. Next time.