diff --git a/2018/day-14/recipes.js b/2018/day-14/recipes.js new file mode 100644 index 0000000..230880b --- /dev/null +++ b/2018/day-14/recipes.js @@ -0,0 +1,111 @@ +/** + * Circular linked list of recipes + */ +class Recipes { + constructor (recipe) { + this.head = null + this.tail = null + this.length = 0 + this.addFirst(recipe) + } + + addFirst (recipe) { + const newRecipe = { value: recipe } + newRecipe.next = newRecipe + newRecipe.prev = newRecipe + this.head = newRecipe + this.tail = newRecipe + this.length++ + return this + } + + /** + * Adds a recipe to the linked list + * @param {Number} recipe value + */ + addRecipe (recipe) { + const newRecipe = { value: recipe } + newRecipe.next = this.tail // link new recipe to tail + this.tail.prev = newRecipe + newRecipe.prev = this.head // link new recipe to old head + this.head.next = newRecipe + this.head = newRecipe // make new recipe the new head + this.length++ + return this.head + } + + /** + * Scoring the current recipes means adding new recipies base on the score value + * @param {Number} score of current recipe + */ + scoreRecipes (score) { + score.toString().split('').forEach((recipe) => { + this.addRecipe(parseInt(recipe)) + }) + } +} + +/** + * Takes an array of numbers and totals the digits + * @param + */ +const totalDigitsInArray = (arr) => { + return arr.reduce( + (acc, num) => acc + num.toString().split('') + .reduce((sub, digit) => sub + parseInt(digit), 0), 0) +} + +/** + * Loops the elves through the recipes list the specified number of times + * @param {Array} elves list of elves + * @param {LinkedList} recipes list of recipes + * @param {Numbe} repeat count of desired iterations + */ +const loopRecipesForElves = (elves, recipes, repeat) => { + for (let x = 1; x <= repeat; x++) { + const score = totalDigitsInArray(elves.map((elf) => elf.value)) + recipes.scoreRecipes(score) + elves.forEach((elf, idx) => { + const distance = elf.value + 1 + for (let x = 0; x < distance; x++) { + elf = elf.next + } + elves[idx] = elf + }) + } +} + +/** + * Determines the next X recipes after the elves have generated Y recipes + */ +const calculateXAfterY = (x, y, recipes, elves) => { + let iterator = recipes.head + while (recipes.length <= y) { + loopRecipesForElves(elves, recipes, 1) + } + + if (recipes.length === y + 1) { + iterator = recipes.head + } else { + // In case multidigit recipe results created more than Y + iterator = recipes.head.prev + } + + while (recipes.length < x + y) { + loopRecipesForElves(elves, recipes, 1) + } + + let result = '' + while (result.length < x) { + result += iterator.value.toString() + iterator = iterator.next + } + return result +} + +module.exports = { + calculateXAfterY, + loopRecipesForElves, + Recipes, + totalDigitsInArray +} diff --git a/2018/day-14/recipes.test.js b/2018/day-14/recipes.test.js new file mode 100644 index 0000000..4b09598 --- /dev/null +++ b/2018/day-14/recipes.test.js @@ -0,0 +1,140 @@ +/* eslint-env mocha */ +const expect = require('chai').expect +const { + calculateXAfterY, + loopRecipesForElves, + Recipes, + totalDigitsInArray +} = require('./recipes') + +describe('--- Day 14: Chocolate Charts ---', () => { + describe('Part 1:', () => { + describe('new Recipes()', () => { + it('builds a linked list', () => { + const recipes = new Recipes(0) + for (let x = 1; x <= 5; x++) { + recipes.addRecipe(x) + } + expect(recipes.length).to.equal(6) + expect(recipes.head.value).to.equal(5) + expect(recipes.tail.value).to.equal(0) + expect(recipes.tail.prev).to.equal(recipes.head) // circular linked list for prev + expect(recipes.head.next).to.equal(recipes.tail) // circular linked list for next + }) + describe('scoreRecipes()', () => { + it('adds new recipes based on the provided score', () => { + const recipes = new Recipes(0) + for (let x = 1; x <= 5; x++) { + recipes.addRecipe(x) + } + recipes.scoreRecipes(37) + expect(recipes.head.value).to.equal(7) + expect(recipes.head.prev.value).to.equal(3) + expect(recipes.head.prev.prev.value).to.equal(5) + expect(recipes.head.next).to.equal(recipes.tail) + }) + }) + }) + describe('totalDigitsInArray()', () => { + it('calculates the total value of all the digits of all the numbers in the provided array', () => { + const expected = 34 + const test = [1, 5, 13, 22, 3, 0, 971] + const actual = totalDigitsInArray(test) + expect(actual).to.equal(expected) + }) + }) + describe('loopRecipeForEleves()', () => { + it('loops through the recipe object for the specified elves the specified number of times', () => { + const expected = '37101012451589167792' // list of recipe values in the last iteration of the example + const elves = [3, 7] + const recipes = new Recipes(elves[0]) + let actual = '' + + elves.forEach((elf, idx) => { + if (idx === 0) { + elves[0] = recipes.head + } else { + elves[idx] = recipes.addRecipe(elf) + } + }) + + loopRecipesForElves(elves, recipes, 15) + + let iterator = recipes.tail.next + actual += recipes.tail.value.toString() + while (iterator !== recipes.tail) { + actual += iterator.value.toString() + iterator = iterator.next + } + + expect(expected).to.equal(actual) + }) + }) + describe('calculateXAfterY(x, y, recipe, elves)', () => { + it('predicts the next X results after the elves have executed Y', () => { + const elves = [3, 7] + const recipes = new Recipes(elves[0]) + let actual = '' + + elves.forEach((elf, idx) => { + if (idx === 0) { + elves[0] = recipes.head + } else { + elves[idx] = recipes.addRecipe(elf) + } + }) + + actual = calculateXAfterY(10, 9, recipes, elves) + expect(actual).to.equal('5158916779') + }) + it('predicts the next X results after the elves have executed Y', () => { + const elves = [3, 7] + const recipes = new Recipes(elves[0]) + let actual = '' + + elves.forEach((elf, idx) => { + if (idx === 0) { + elves[0] = recipes.head + } else { + elves[idx] = recipes.addRecipe(elf) + } + }) + + actual = calculateXAfterY(10, 5, recipes, elves) + expect(actual).to.equal('0124515891') + }) + it('predicts the next X results after the elves have executed Y', () => { + const elves = [3, 7] + const recipes = new Recipes(elves[0]) + let actual = '' + + elves.forEach((elf, idx) => { + if (idx === 0) { + elves[0] = recipes.head + } else { + elves[idx] = recipes.addRecipe(elf) + } + }) + + actual = calculateXAfterY(10, 18, recipes, elves) + expect(actual).to.equal('9251071085') + }) + it('predicts the next X results after the elves have executed Y', () => { + const elves = [3, 7] + const recipes = new Recipes(elves[0]) + let actual = '' + + elves.forEach((elf, idx) => { + if (idx === 0) { + elves[0] = recipes.head + } else { + elves[idx] = recipes.addRecipe(elf) + } + }) + + actual = calculateXAfterY(10, 2018, recipes, elves) + expect(actual).to.equal('5941429882') + }) + }) + }) +}) diff --git a/2018/day-14/solution.js b/2018/day-14/solution.js new file mode 100644 index 0000000..467d69e --- /dev/null +++ b/2018/day-14/solution.js @@ -0,0 +1,25 @@ +const { + calculateXAfterY, + Recipes +} = require('./recipes') + +const input = 540561 + +const elves = [3, 7] +const recipes = new Recipes(elves[0]) + +elves.forEach((elf, idx) => { + if (idx === 0) { + elves[0] = recipes.head + } else { + elves[idx] = recipes.addRecipe(elf) + } +}) + +const answer = calculateXAfterY(10, input, recipes, elves) +const answer2 = '' + +console.log(`-- Part 1 --`) +console.log(`Answer: ${answer}`) +console.log(`-- Part 2 --`) +console.log(`Answer: ${answer2}`) diff --git a/index.js b/index.js new file mode 100644 index 0000000..dcb17cf --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +require('./2018/day-14/solution')
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: