Skip to content

Instantly share code, notes, and snippets.

@royling
Last active December 26, 2016 12:41
Show Gist options
  • Save royling/90cf2cebab6d5ad769532c6d4b2bbcfc to your computer and use it in GitHub Desktop.
Save royling/90cf2cebab6d5ad769532c6d4b2bbcfc to your computer and use it in GitHub Desktop.
Advent of Code 2016 - http://adventofcode.com/2016
const parseInput = input => input.split(', ').map(value => {
return { turn: value[0], blocks: Number(value.slice(1)) };
});
const turnToDirection = {
N: { L: 'W', R: 'E' },
S: { L: 'E', R: 'W' },
E: { L: 'N', R: 'S' },
W: { L: 'S', R: 'N' }
};
const visited = new Set(['0,0']);
const findVisited = ({x1, y1}, {x2, y2}) => {
let y = y1, x = x1;
if (x1 === x2) {
let step = y1 < y2 ? 1 : -1;
while (y !== y2) {
y += step;
let path = `${x},${y}`;
if (visited.has(path)) {
return { x, y };
} else {
visited.add(path);
}
}
} else if (y1 === y2) {
let step = x1 < x2 ? 1 : -1;
while (x !== x2) {
x += step;
let path = `${x},${y}`;
if (visited.has(path)) {
return { x, y };
} else {
visited.add(path);
}
}
}
return null;
};
const solve = puzzle => {
let result = parseInput(puzzle).reduce((result, step, index) => {
let direction = result.direction = turnToDirection[result.direction][step.turn];
let p1 = { x1: result.x, y1: result.y };
switch (direction) {
case 'E':
result.x += step.blocks; break;
case 'W':
result.x -= step.blocks; break;
case 'N':
result.y += step.blocks; break;
case 'S':
result.y -= step.blocks; break;
default: break;
}
if (result.firstLoc === null) {
result.firstLoc = findVisited(p1, { x2: result.x, y2: result.y });
}
return result;
}, { direction: 'N', x: 0, y: 0, firstLoc: null });
return {
dist: Math.abs(result.x) + Math.abs(result.y),
firstLocDist: Math.abs(result.firstLoc.x) + Math.abs(result.firstLoc.y)
};
};
console.dir(solve('R4, R5, L5, L5, L3, R2, R1, R1, L5, R5, R2, L1, L3, L4, R3, L1, L1, R2, R3, R3, R1, L3, L5, R3, R1, L1, R1, R2, L1, L4, L5, R4, R2, L192, R5, L2, R53, R1, L5, R73, R5, L5, R186, L3, L2, R1, R3, L3, L3, R1, L4, L2, R3, L5, R4, R3, R1, L1, R5, R2, R1, R1, R1, R3, R2, L1, R5, R1, L5, R2, L2, L4, R3, L1, R4, L5, R4, R3, L5, L3, R4, R2, L5, L5, R2, R3, R5, R4, R2, R1, L1, L5, L2, L3, L4, L5, L4, L5, L1, R3, R4, R5, R3, L5, L4, L3, L1, L4, R2, R5, R5, R4, L2, L4, R3, R1, L2, R5, L5, R1, R1, L1, L5, L5, L2, L1, R5, R2, L4, L1, R4, R3, L3, R1, R5, L1, L4, R2, L3, R5, R3, R1, L3'));
class Bot {
constructor(id) {
this.id = id;
this.chips = [];
}
receive(chip) {
if (this.chips.length === 2) throw new Error('There are two chips already!');
this.chips.push(chip);
this.chips.sort();
}
outputLow(output, index) {
output[index] = this.chips[0];
}
outputHigh(output, index) {
output[index] = this.chips[1];
}
giveBotLow(bot) {
bot.receive(this.chips[0]);
}
giveBotHigh(bot) {
bot.receive(this.chips[1]);
}
}
const valueRegexp = /value (\d+) goes to bot (\d+)/;
const giveRegexp = /bot (\d+) gives low to (bot|output) (\d+) and high to (bot|output) (\d+)/;
class Factory {
constructor() {
this.bots = [];
this.output = [];
}
getBot(id) {
if (!this.bots[id]) {
this.bots[id] = new Bot(id);
}
return this.bots[id];
}
exec(input) {
let unresolved = input.split('\n');
while (unresolved.length > 0) {
unresolved = unresolved.filter(inst => {
let valueInst, giveInst;
if (valueInst = valueRegexp.exec(inst)) {
let bot = this.getBot(parseInt(valueInst[2], 10));
bot.receive(parseInt(valueInst[1], 10));
return false; // resolved
} else if (giveInst = giveRegexp.exec(inst)) {
let fromBot = this.getBot(parseInt(giveInst[1], 10));
if (fromBot.chips.length === 2) {
let lowTarget = giveInst[2];
let lowId = parseInt(giveInst[3], 10);
if (lowTarget === 'bot') {
fromBot.giveBotLow(this.getBot(lowId));
} else if (lowTarget === 'output') {
fromBot.outputLow(this.output, lowId);
}
let highTarget = giveInst[4];
let highId = parseInt(giveInst[5], 10);
if (highTarget === 'bot') {
fromBot.giveBotHigh(this.getBot(highId));
} else if (highTarget === 'output') {
fromBot.outputHigh(this.output, highId);
}
return false; // resolved
} else {
return true;
}
} else {
throw new Error('Unknown instruction: ' + inst);
}
});
}
return this;
}
findBot(low, high) {
return this.bots.find(bot => bot.chips[0] === low && bot.chips[1] === high);
}
listBots() {
this.bots.forEach(bot => console.log(bot));
}
}
let f = new Factory();
const input = require('fs').readFileSync('d10.txt', 'utf8');
// f.exec(input).listBots();
console.log(`bot: ${f.exec(input).findBot(17, 61).id}`);
const input = [
'URULLLLLRLDDUURRRULLLDURRDRDRDLURURURLDLLLLRUDDRRLUDDDDDDLRLRDDDUUDUDLDULUDLDURDULLRDDURLLLRRRLLRURLLUDRDLLRRLDDRUDULRRDDLUUUDRLDLURRRULURRDLLLDDDLUDURDDRLDDDLLRULDRUDDDLUDLURUDLLRURRUURUDLLLUUUUDDURDRDDDLDRRUDURDLLLULUDURURDUUULRULUDRUUUUDLRLUUUUUDDRRDDDURULLLRRLDURLDLDRDLLLUULLRRLLLLDRLRDRRDRRUDDLULUUDDDDRRUUDDLURLRDUUDRRLDUDLRRRLRRUUDURDRULULRDURDRRRDLDUUULRDDLRLRDLUUDDUDDRLRRULLLULULLDDDRRDUUUDDRURDDURDRLRDLDRDRULRLUURUDRLULRLURLRRULDRLRDUDLDURLLRLUDLUDDURDUURLUDRLUL',
'LLLUUURUULDDDULRRDLRLLLLLLLLRURRDLURLUDRRDDULDRRRRRRLDURRULDDULLDDDRUUDLUDULLDLRRLUULULRULURDURLLDULURDUDLRRLRLLDULLRLDURRUULDLDULLRDULULLLULDRLDLDLDLDDLULRLDUDRULUDDRDDRLRLURURRDULLUULLDRRDRRDLDLLRDLDDUUURLUULDDRRRUULDULDDRDDLULUDRURUULLUDRURDRULDRUULLRRDURUDDLDUULLDDRLRRDUDRLRRRLDRLRULDRDRRUDRLLLDDUDLULLURRURRLUURDRLLDLLDUDLUUURRLRDDUDRLUDLLRULLDUUURDLUUUDUDULRLDLDRUUDULRDRRUDLULRLRDLDRRDDDUDLDLDLRUURLDLLUURDLDLRDLDRUDDUURLLLRDRDRRULLRLRDULUDDDLUDURLDUDLLRULRDURDRDLLULRRDLLLDUURRDUDDLDDRULRRRRLRDDRURLLRRLLL',
'DRURLDDDDRLUDRDURUDDULLRRLLRLDDRLULURLDURRLDRRLRLUURDDRRDLRDLDLULDURUDRLRUDULRURURLRUDRLLDDUDDRDLDRLLDDLRRDRUUULDUUDRUULRLLDLLULLLRRDRURDLDDRRDDUDDULLDUUULDRUDLDLURLDRURUDLRDDDURRLRDDUDLLLRRUDRULRULRRLLUUULDRLRRRLLLDLLDUDDUUDRURLDLRRUUURLUDDDRRDDLDDDDLUURDDULDRLRURLULLURRDRLLURLLLURDURLDLUDUUDUULLRLDLLLLULRDDLDUDUDDDUULURRLULDLDRLRDRLULLUDDUUUUURDRURLDUULDRRDULUDUDLDDRDLUDDURUDURLDULRUDRRDLRLRDRRURLDLURLULULDDUUDLRLLLLURRURULDDRUUULLDULDRDULDDDLLLRLULDDUDLRUDUDUDURLURLDDLRULDLURD',
'DRUDRDURUURDLRLUUUUURUDLRDUURLLDUULDUULDLURDDUULDRDDRDULUDDDRRRRLDDUURLRDLLRLRURDRRRDURDULRLDRDURUDLLDDULRDUDULRRLLUDLLUUURDULRDDLURULRURDDLRLLULUDURDRRUDLULLRLDUDLURUDRUULDUDLRDUDRRDULDDLDRLRRULURULUURDULRRLDLDULULRUUUUULUURLURLRDLLRRRRLURRUDLRLDDDLDRDRURLULRDUDLRLURRDRRLRLLDLDDLLRRULRLRLRUDRUUULLDUULLDDRLUDDRURLRLDLULDURLLRRLDLLRDDDUDDUULLUDRUDURLLRDRUDLUDLLUDRUUDLRUURRRLLUULLUUURLLLRURUULLDLLDURUUUULDDDLRLURDRLRRRRRRUDLLLRUUULDRRDLRDLLDRDLDDLDLRDUDLDDRDDDDRULRRLRDULLDULULULRULLRRLLUURUUUDLDLUDUDDDLUUDDDDUDDDUURUUDRDURRLUULRRDUUDDUDRRRDLRDRLDLRRURUUDRRRUUDLDRLRDURD',
'DDDLRURUDRRRURUUDLRLRDULDRDUULRURRRUULUDULDDLRRLLRLDDLURLRUDRLRRLRDLRLLDDLULDLRRURDDRDLLDDRUDRRRURRDUDULUDDULRRDRLDUULDLLLDRLUDRDURDRRDLLLLRRLRLLULRURUUDDRULDLLRULDRDLUDLULDDDLLUULRRLDDUURDLULUULULRDDDLDUDDLLLRRLLLDULRDDLRRUDDRDDLLLLDLDLULRRRDUDURRLUUDLLLLDUUULDULRDRULLRDRUDULRUUDULULDRDLDUDRRLRRDRLDUDLULLUDDLURLUUUDRDUDRULULDRDLRDRRLDDRRLUURDRULDLRRLLRRLDLRRLDLDRULDDRLURDULRRUDURRUURDUUURULUUUDLRRLDRDLULDURUDUDLUDDDULULRULDRRRLRURLRLRLUDDLUUDRRRLUUUDURLDRLRRDRRDURLLL'
];
const simpleKeypad = {
'1': { U: '1', D: '4', L: '1', R: '2'},
'2': { U: '2', D: '5', L: '1', R: '3' },
'3': { U: '3', D: '6', L: '2', R: '3' },
'4': { U: '1', D: '7', L: '4', R: '5' },
'5': { U: '2', D: '8', L: '4', R: '6' },
'6': { U: '3', D: '9', L: '5', R: '6' },
'7': { U: '4', D: '7', L: '7', R: '8' },
'8': { U: '5', D: '8', L: '7', R: '9' },
'9': { U: '6', D: '9', L: '8', R: '9' }
};
const hardKeypad = {
'1': { U: '1', D: '3', L: '1', R: '1' },
'2': { U: '2', D: '6', L: '2', R: '3' },
'3': { U: '1', D: '7', L: '2', R: '4' },
'4': { U: '4', D: '8', L: '3', R: '4' },
'5': { U: '5', D: '5', L: '5', R: '6' },
'6': { U: '2', D: 'A', L: '5', R: '7' },
'7': { U: '3', D: 'B', L: '6', R: '8' },
'8': { U: '4', D: 'C', L: '7', R: '9' },
'9': { U: '9', D: '9', L: '8', R: '9' },
'A': { U: '6', D: 'A', L: 'A', R: 'B' },
'B': { U: '7', D: 'D', L: 'A', R: 'C' },
'C': { U: '8', D: 'C', L: 'B', R: 'C' },
'D': { U: 'B', D: 'D', L: 'D', R: 'D' }
};
const solve = (input, keypad) => input.reduce((result, line, index) => {
let start = result[index];
result.push(line.split('').reduce((start, direction) => keypad[start][direction], start));
return result;
}, [5]).slice(1).join('');
console.log(`Simple keypad: ${solve(input, simpleKeypad)}`);
console.log(`Hard keypad: ${solve(input, hardKeypad)}`);
const isValidTriangle = (a, b, c) => (a + b > c) && (a + c > b) && (b + c > a);
const parseNumsInLine = line => line.trim().split(/\s+/).map(str => parseInt(str, 10));
const solve = input => input.split('\n').filter(line => isValidTriangle(...parseNumsInLine(line))).length;
const solve2 = input => {
let data = input.split('\n').map(parseNumsInLine);
let valid = 0;
for (let i = 0; i+2 < data.length; i+=3) {
for (let j = 0; j < 3; j++) {
let a = data[i][j], b = data[i+1][j], c = data[i+2][j];
if (isValidTriangle(a, b, c)) {
valid++;
}
}
}
return valid;
};
console.log(`Valid triangle: ${solve(input)}`);
console.log(`Valid vertical triangle: ${solve2(input)}`);
const parseInput = input => {
let lastDash = input.lastIndexOf('-');
let [sectorId, checksum] = /(\d+)\[([a-z]+)\]/.exec(input.substring(lastDash)).slice(1, 3);
let name = input.substring(0, lastDash);
let charCountMap = name.split('').reduce((m, c) => {
if (c === '-') return m;
if (!m[c]) {
m[c] = 1;
} else {
m[c] += 1;
}
return m;
}, Object.create(null));
let isRealRoom = Object.keys(charCountMap).map(c => [c, charCountMap[c]].join('')).sort((a, b) => {
let ca = a.substring(1), cb = b.substring(1);
if (ca === cb) {
return b[0] < a[0] ? 1 : (b[0] > a[0] ? -1 : 0);
} else {
return cb - ca;
}
}).slice(0, 5).map(c => c[0]).join('') === checksum;
sectorId = isRealRoom ? parseInt(sectorId, 10) : 0;
let realName = isRealRoom ? decryptName(name, sectorId % 26) : null;
return {
isRealRoom,
sectorId,
name,
realName
};
};
const solve = (input) => {
return input.split('\n').map(line => parseInput(line).sectorId).reduce((sum, sid) => sum + sid, 0);
};
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
const decryptName = (name, shift) => name.split('').map(c => c === '-' ? ' ' : alphabet[(alphabet.indexOf(c) + shift) % 26]).join('');
const solve2 = input => {
for (let line of input.split('\n')) {
let v = parseInput(line);
if (v.isRealRoom && v.realName.indexOf('north') > -1) {
return v.sectorId;
}
}
return -1;
};
const crypto = require('crypto');
const md5Hex = str => crypto.createHash('md5').update(str).digest('hex');
const solve = doorId => {
let pwd = [];
for (let i = 0, j = 0; j < 8; i++) {
let hex = md5Hex(doorId + i);
if (hex.slice(0,5) === '00000') {
j++;
pwd.push(hex[5]);
}
}
return pwd.join('');
}
console.log(`Part1 answer: ${solve('uqwqemis')}`);
const getPosition = c => {
let n = parseInt(c, 10);
if (n >= 0 && n <= 7) {
return n;
} else {
return -1;
}
}
const solve2 = doorId => {
let pwd = [], mark = [];
for (let i = 0, j = 0; j < 8; i++) {
let hex = md5Hex(doorId + i), pos;
if (hex.slice(0,5) === '00000' && (pos = getPosition(hex[5])) > -1 && !mark[pos]) {
j++;
pwd[pos] = hex[6];
mark[pos] = true;
}
}
return pwd.join('');
}
console.log(`Part2 answer: ${solve2('uqwqemis')}`);
const input = require('fs').readFileSync('d6.txt', 'utf8');
const solve = (input, mostCommon) => input.split('\n').reduce((res, line) => line.split('').reduce((res, c, index) => {
let col = res[index] || {};
col[c] = (col[c] || 0) + 1;
res[index] = col;
return res;
}, res), []).map(col => {
let commonChar;
for (let c in col) {
if (!commonChar || (mostCommon && col[c] > col[commonChar]) ||
(!mostCommon && col[c] < col[commonChar])) {
commonChar = c;
}
}
return commonChar;
}).join('');
console.log(`part1 answer: ${solve(input, true)}`);
console.log(`part2 answer: ${solve(input, false)}`);
const isABBA = str => str[0] !== str[1] && str[1] === str[2] && str[0] === str[3];
const hasABBA = str => {
if (str.length < 4) return false;
return isABBA(str) || hasABBA(str.substring(1));
}
const isIPSupportingTLS = raw => {
let hasSupernetABBA = false;
while (true) {
let s = raw.indexOf('[');
if (s > -1) {
let supernet = raw.substring(0, s);
hasSupernetABBA = hasSupernetABBA || hasABBA(supernet);
let e = raw.indexOf(']');
let hypernet = raw.substring(s+1, e);
if (hasABBA(hypernet)) return false;
raw = raw.substring(e+1);
} else {
return hasSupernetABBA || hasABBA(raw);
}
}
}
const solve = input => input.split('\n').filter(isIPSupportingTLS).length;
const input = require('fs').readFileSync('d7.txt', 'utf8');
console.log(`part1: ${solve(input)}`);
class Screen {
constructor(wide, tall) {
this.x = wide;
this.y = tall;
this._initPixels();
}
_initPixels() {
this.pixels = [];
for (let i = 0; i < this.y; i++) { // row
this.pixels[i] = [];
for (let j = 0; j < this.x; j++) { // col
this.pixels[i][j] = '.';
}
}
}
rect(x, y) {
if (x > this.x) x = this.x;
if (y > this.y) y = this.y;
for (let i = 0; i < y; i++) {
for (let j = 0; j < x; j++) {
this.pixels[i][j] = '#';
}
}
}
rotateRow(y, right) {
let row = this.pixels[y], wide = row.length;
this.pixels[y] = row.reduce((after, p, x) => {
after[(x + right) % wide] = p;
return after;
}, []);
}
rotateCol(x, down) {
let tall = this.pixels.length;
let col = this.pixels.reduce((after, row, y) => {
after[(y + down) % tall] = row[x];
return after;
}, []);
for (let y = 0; y < this.y; y++) {
this.pixels[y][x] = col[y];
}
}
countLights() {
let count = 0;
for (let i = 0; i < this.y; i++) {
for (let j = 0; j < this.x; j++) {
if (this.pixels[i][j] === '#') {
count++;
}
}
}
return count;
}
print() {
for (let i = 0; i < this.y; i++) {
console.log(`${this.pixels[i].join('')}`);
}
}
}
const rectRegexp = /rect (\d+)x(\d+)/;
const rotateRowRegexp = /rotate row y=(\d+) by (\d+)/;
const rotateColRegexp = /rotate column x=(\d+) by (\d+)/;
const parseInstruction = instruction => {
let rect = rectRegexp.exec(instruction);
if (rect) {
return { method: 'rect', params: rect.slice(1, 3) };
}
let rotateRow = rotateRowRegexp.exec(instruction);
if (rotateRow) {
return { method: 'rotateRow', params: rotateRow.slice(1, 3) };
}
let rotateCol = rotateColRegexp.exec(instruction);
if (rotateCol) {
return { method: 'rotateCol', params: rotateCol.slice(1, 3) };
}
throw new Error('Unknown instruction: ' + instruction);
}
const solve = input => {
let screen = new Screen(50, 6);
input.split('\n').forEach(line => {
if (!line) return;
let inst = parseInstruction(line);
screen[inst.method].apply(screen, inst.params.map(n => parseInt(n, 10)));
});
screen.print();
return screen.countLights();
}
const input = require('fs').readFileSync('d8.txt', 'utf8');
console.log(`part1 answer: ${solve(input)}`);
const markerRegexp = /\((\d+)x(\d+)\)/;
const decompress = (input, deep) => {
let len = 0;
let marker = null;
do {
marker = markerRegexp.exec(input);
if (marker === null) {
len += input.length;
break;
}
let pos = marker.index;
if (pos > 0) {
len += pos;
}
let chars = parseInt(marker[1], 10),
times = parseInt(marker[2], 10);
if (!deep) {
len += chars * times;
} else {
let str = input.substr(pos + marker[0].length, chars);
len += decompress(str, true) * times;
}
input = input.substring(pos + marker[0].length + chars);
marker = null;
} while(true);
return len;
}
const input = require('fs').readFileSync('d9.txt', 'utf8'); // no \n at EOF
//const input = `(25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN`;
console.log(`result: \n${decompress(input, true)}`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment