Это 10-й пост в моей продолжающейся серии о Пришествии кода 2017. Я собираюсь описать свои решения, реализованные на JS (ES6+) и Node.js.

TL;DR: это очень простая проблема.

Описание проблемы здесь: http://adventofcode.com/2017/day/10, а ввод можно найти здесь: http://adventofcode.com/2017/day/10/input.

Первая часть

Проблема полностью заключается в реализации конкретного, четко определенного алгоритма, поэтому давайте быстро пробежимся по коду.

const { readFile, parseInt } = require("../lib");
const ls = readFile("./input")
  .split(",")
  .map(parseInt);

Просто получите ввод.

const L = 256;
const a = Array.from({ length: L }, (_, i) => i);

О, эти строки интересны. Мы используем новую функцию https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from для создания массива с помощью генератора. Первый аргумент может быть подобным массиву, поэтому мы передаем объект для определения длины массива. Вторая функция ведет себя так же, как если бы вы передавали ее методу Array.prototype.map.

let i = 0;
let skip = 0;

Состояние приложения.

const read = l => {
  let max = i + l;
  const r = a.slice(i, max);
  if (max < L) {
    return r;
  }
  max -= L;
  return r.concat(a.slice(0, max));
};

const write = b => {
  let k = i;
  for (const el of b) {
    a[k] = el;
    k++;
    if (k >= L) {
      k -= L;
    }
  }
};

const revert = l => {
  write(read(l).reverse());
};

Основные операции, указанные в постановке задачи. Первый читает фрагмент массива длиной l, при необходимости оборачиваясь вокруг конца массива. Второй выполняет мутации на месте, циклически записывая срез b из индекса i. Наконец, последний метод объединяет все вместе, реализуя один шаг хеширования, определяемый длиной l.

for (const l of ls) {
  revert(l);
  i = (i + l + skip) % L;
  skip++;
}

console.log(a[0] * a[1]);

Теперь мы следуем остальной части алгоритма, последовательно хэшируя для каждого l из ввода.

Полный код:



Часть вторая

Большая часть этого файла такая же, как и раньше, поэтому давайте проверим только те части, которые изменились.

const SUFFIX = [17, 31, 73, 47, 23];

const ls = readFile("./input")
  .split("")
  .map(c => c.charCodeAt(0))
  .concat(SUFFIX);

Здесь мы обрабатываем одни и те же входные данные по-разному, как символы, которые определяют числа, соответствующие их кодам ASCII, и добавляют предопределенный суффикс для каждого определения задачи.

const runRound = () => {
  for (const l of ls) {
    revert(l);
    i = (i + l + skip) % L;
    skip++;
  }
};

for (let j = 0; j < 64; j++) {
  runRound();
}

Здесь мы переместили один раунд хеширования в метод (он по-прежнему мутирует тот же глобальный экземпляр a). И запустите этот метод 64 раза в цикле. Это вычисляет разреженный хеш.

const xor = a => a.reduce((x, y) => x ^ y, 0);

const denseHash = new Array(16);
for (let j = 0; j < 16; j++) {
  denseHash[j] = xor(a.slice(j * 16, (j + 1) * 16));
}

Здесь мы определяем метод массива xor, подобно тому, как мы уже реализовывали метод sum в нескольких задачах ранее. Затем мы используем его для XOR чисел в последующих группах по 16 для вычисления плотного хэша.

const toHex = n => {
  const h = n.toString(16);
  if (h.length < 2) {
    return "0" + h;
  }
  return h;
};

console.log(denseHash.map(toHex).join(""));

Наконец, мы вычисляем строковое представление плотного хэша, определяя вспомогательный метод для преобразования числа в строку от 0 до 255 и добавления нуля слева, если это необходимо.

Полный код:



Бонус: Кложур