AoC 2017: Day 8

Eugene Mirotin
3 min readJan 9, 2018

--

This is the 8th post in my ongoing series about Advent of Code 2017. I’m going to describe my solutions implemented in JS (ES6+) and Node.js.

TL;DR: this problem is more about parsing the input and organizing your code than building sophisticated algorithms.

The problem description is here: http://adventofcode.com/2017/day/8, and the input can be found here: http://adventofcode.com/2017/day/8/input.

Part One

The full code can be found here:

const { readLines, parseInt } = require("../lib");

const parse = line => {
const m = line.match(
/^(\w+) (inc|dec) (-?\d+) if (\w+) (<|>|<=|>=|==|!=) (-?\d+)$/
);
if (!m) {
throw new Error(`WTF ${line}`);
}
return {
reg: m[1],
dec: m[2] === "dec",
val: parseInt(m[3]),
condReg: m[4],
cond: m[5],
condVal: parseInt(m[6])
};
};

const data = readLines("./input").map(parse);

We use a regular expression to parse each line. We also parse two integers met there, and convert the inc/dec operation into a boolean flag.

const reg = {};

const get = name => reg[name] || 0;

const update = (name, dec, val) => {
reg[name] = get(name) + val * (dec ? -1 : 1);
};

We could scan the entire program for all possible register names and init them with 0s, but instead I’m doing lazy evaluation here. The get method reads the registry and falls back to 0. The update method updates (increments or decrements) the value in the registry.

const condHolds = (name, cond, val) => {
const x = get(name);
switch (cond) {
case "<":
return x < val;
case "<=":
return x <= val;
case ">":
return x > val;
case ">=":
return x >= val;
case "==":
return x == val;
case "!=":
return x != val;
default:
throw new Error(`WTF unknown cond ${cond} for ${name}`);
}
};

This function given the register name, the condition (as string) and the target value checks if the condition holds for the current value in the registry.

data.forEach(({ reg, dec, val, condReg, cond, condVal }) => {
if (condHolds(condReg, cond, condVal)) {
update(reg, dec, val);
}
});

console.log(Math.max(...Object.values(reg)));

Finally this runs all the instructions one by one, then gets all the values from the registry and prints the max one.

Part Two

The second part differs very lightly: now we want to find the max value ever. It’s clear that the only place where the new max can happen is the update method, and this new value is the only candidate.

So here’s the updated update:

let max = 0;

const update = (name, dec, val) => {
max = Math.max(max, (reg[name] = get(name) + val * (dec ? -1 : 1)));
};

And the full solution:

Bonus: Clojure

The cool thing about Clojure is that operators are simply functions, so our condition check now looks like this:

(def ops {
"<" <
"<=" <=
">" >
">=" >=
"==" ==
"!=" not=
})
(defn cond-holds [reg name cnd val]
(let [
reg-val (get-val reg name)
op (get ops cnd)
] (if-not op
(throw (ex-info "WTF unknown cond" {:cnd cnd :name name}))
(op reg-val val))))

--

--