diff --git a/2021/day-06/fish.js b/2021/day-06/fish.js new file mode 100644 index 0000000..f6f5cbc --- /dev/null +++ b/2021/day-06/fish.js @@ -0,0 +1,75 @@ + +let _fishes = [] +const NewFishAge = 8 // age of newly spawned fish +const FishSpawnAge = 0 // age when the fish spawns +const ResetFishAge = 6 // age of the original fish after spawning + +// allocate an empty big school +const _newBigSchool = () => [...new Array(NewFishAge + 1)].map(() => 0) +let _bigSchool = _newBigSchool() + +const ageFish = (age) => { + if (age > NewFishAge) { throw new Error('Fish is too young') } + if (age < FishSpawnAge) { throw new Error('Fish is too old') } + if (age === FishSpawnAge) { return ResetFishAge } + return age - 1 +} + +const spawn = (qty) => { + console.debug(`spawning ${qty} fish`) + const newFishes = [...new Array(qty)].map(() => NewFishAge) + _fishes.push(...newFishes) +} + +const efficientSpawn = (qty) => { + console.debug(`spawning ${qty} fish`) + _bigSchool[NewFishAge] = qty +} + +const school = { + get state () { + return _fishes + }, + set state (state) { + _fishes = state + }, + + advance: () => { + // Calculate how many will spawn + const toSpawn = _fishes.filter((x) => x === FishSpawnAge).length + // Iterate each fish + _fishes = _fishes.map(ageFish) + // Spawn the new fish + spawn(toSpawn) + } +} + +/** + * The efficient school doesn't track the position of the fish. + * It only cares about the total number of fish of each age + */ +const efficientSchool = { + get state () { + return _bigSchool + }, + set state (state) { + _bigSchool = _newBigSchool() + state.forEach((fish) => { _bigSchool[fish]++ }) + }, + advance: () => { + // Calculate how many will spawn (age 0) and shift the age groups in one quick step + const toSpawn = _bigSchool.shift() + // Iterate old fish back to young since they're spawning + _bigSchool[ResetFishAge] += toSpawn + // Spawn the new fish + efficientSpawn(toSpawn) + } +} + +module.exports = { + school, + efficientSchool, + ageFish, + spawn, + efficientSpawn +} diff --git a/2021/day-06/fish.test.js b/2021/day-06/fish.test.js new file mode 100644 index 0000000..bfdb0be --- /dev/null +++ b/2021/day-06/fish.test.js @@ -0,0 +1,141 @@ +/* eslint-env mocha */ +const { expect } = require('chai') +const { school, ageFish, spawn, efficientSchool, efficientSpawn } = require('./fish') + +describe('--- Day 6: Lanternfish ---', () => { + describe('Part 1', () => { + beforeEach(() => { + // ensure flushed state + school.state = [3, 4, 3, 1, 2] + expect(school.state).to.deep.equal([3, 4, 3, 1, 2]) + }) + describe('spawn()', () => { + it('adds new fish to the end of the list', () => { + spawn(4) + expect(school.state).to.deep.equal([3, 4, 3, 1, 2, 8, 8, 8, 8]) + }) + }) + describe('ageFish()', () => { + it('ages a particular fish', () => { + expect(ageFish(6)).to.equal(5) + expect(ageFish(5)).to.equal(4) + expect(ageFish(4)).to.equal(3) + expect(ageFish(3)).to.equal(2) + expect(ageFish(2)).to.equal(1) + expect(ageFish(1)).to.equal(0) + expect(ageFish(0)).to.equal(6) + expect(ageFish(8)).to.equal(7) + expect(ageFish(7)).to.equal(6) + }) + it('throws an error if the fish is out of range', () => { + expect(() => { ageFish(9) }).to.throw('Fish is too young') + expect(() => { ageFish(-1) }).to.throw('Fish is too old') + }) + }) + describe('advance()', () => { + it('advances one day', () => { + school.state = [3, 4, 3, 1, 2] + school.advance() + expect(school.state).to.deep.equal([2, 3, 2, 0, 1]) + school.advance() + expect(school.state).to.deep.equal([1, 2, 1, 6, 0, 8]) + school.advance() + expect(school.state).to.deep.equal([0, 1, 0, 5, 6, 7, 8]) + school.advance() + expect(school.state).to.deep.equal([6, 0, 6, 4, 5, 6, 7, 8, 8]) + school.advance() + expect(school.state).to.deep.equal([5, 6, 5, 3, 4, 5, 6, 7, 7, 8]) + school.advance() + expect(school.state).to.deep.equal([4, 5, 4, 2, 3, 4, 5, 6, 6, 7]) + school.advance() + expect(school.state).to.deep.equal([3, 4, 3, 1, 2, 3, 4, 5, 5, 6]) + school.advance() + expect(school.state).to.deep.equal([2, 3, 2, 0, 1, 2, 3, 4, 4, 5]) + school.advance() + expect(school.state).to.deep.equal([1, 2, 1, 6, 0, 1, 2, 3, 3, 4, 8]) + school.advance() + expect(school.state).to.deep.equal([0, 1, 0, 5, 6, 0, 1, 2, 2, 3, 7, 8]) + school.advance() + expect(school.state).to.deep.equal([6, 0, 6, 4, 5, 6, 0, 1, 1, 2, 6, 7, 8, 8, 8]) + school.advance() + expect(school.state).to.deep.equal([5, 6, 5, 3, 4, 5, 6, 0, 0, 1, 5, 6, 7, 7, 7, 8, 8]) + school.advance() + expect(school.state).to.deep.equal([4, 5, 4, 2, 3, 4, 5, 6, 6, 0, 4, 5, 6, 6, 6, 7, 7, 8, 8]) + school.advance() + expect(school.state).to.deep.equal([3, 4, 3, 1, 2, 3, 4, 5, 5, 6, 3, 4, 5, 5, 5, 6, 6, 7, 7, 8]) + school.advance() + expect(school.state).to.deep.equal([2, 3, 2, 0, 1, 2, 3, 4, 4, 5, 2, 3, 4, 4, 4, 5, 5, 6, 6, 7]) + school.advance() + expect(school.state).to.deep.equal([1, 2, 1, 6, 0, 1, 2, 3, 3, 4, 1, 2, 3, 3, 3, 4, 4, 5, 5, 6, 8]) + school.advance() + expect(school.state).to.deep.equal([0, 1, 0, 5, 6, 0, 1, 2, 2, 3, 0, 1, 2, 2, 2, 3, 3, 4, 4, 5, 7, 8]) + school.advance() + expect(school.state).to.deep.equal([6, 0, 6, 4, 5, 6, 0, 1, 1, 2, 6, 0, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 8, 8, 8]) + }) + }) + }) + describe('Part 2', () => { + beforeEach(() => { + // ensure flushed state + efficientSchool.state = [3, 4, 3, 1, 2] + expect(efficientSchool.state).to.deep.equal([0, 1, 1, 2, 1, 0, 0, 0, 0]) + }) + describe('efficientSpawn()', () => { + it('efficiently adds new fish to the school', () => { + efficientSpawn(4) + expect(efficientSchool.state).to.deep.equal([0, 1, 1, 2, 1, 0, 0, 0, 4]) + }) + }) + describe('efficientAdvance', () => { + it('advances one day following the same pattern without tracking unique position', () => { + const sum = (x, y) => x + y + efficientSchool.state = [3, 4, 3, 1, 2] + + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([2, 3, 2, 0, 1].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([1, 2, 1, 6, 0, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([0, 1, 0, 5, 6, 7, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([6, 0, 6, 4, 5, 6, 7, 8, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([5, 6, 5, 3, 4, 5, 6, 7, 7, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([4, 5, 4, 2, 3, 4, 5, 6, 6, 7].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([3, 4, 3, 1, 2, 3, 4, 5, 5, 6].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([2, 3, 2, 0, 1, 2, 3, 4, 4, 5].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([1, 2, 1, 6, 0, 1, 2, 3, 3, 4, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([0, 1, 0, 5, 6, 0, 1, 2, 2, 3, 7, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([6, 0, 6, 4, 5, 6, 0, 1, 1, 2, 6, 7, 8, 8, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([5, 6, 5, 3, 4, 5, 6, 0, 0, 1, 5, 6, 7, 7, 7, 8, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([4, 5, 4, 2, 3, 4, 5, 6, 6, 0, 4, 5, 6, 6, 6, 7, 7, 8, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([3, 4, 3, 1, 2, 3, 4, 5, 5, 6, 3, 4, 5, 5, 5, 6, 6, 7, 7, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([2, 3, 2, 0, 1, 2, 3, 4, 4, 5, 2, 3, 4, 4, 4, 5, 5, 6, 6, 7].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([1, 2, 1, 6, 0, 1, 2, 3, 3, 4, 1, 2, 3, 3, 3, 4, 4, 5, 5, 6, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([0, 1, 0, 5, 6, 0, 1, 2, 2, 3, 0, 1, 2, 2, 2, 3, 3, 4, 4, 5, 7, 8].length) + efficientSchool.advance() + expect(efficientSchool.state.reduce(sum)).to.equal([6, 0, 6, 4, 5, 6, 0, 1, 1, 2, 6, 0, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 8, 8, 8].length) + }) + it('advances efficiently for a large number of days', () => { + efficientSchool.state = [3, 4, 3, 1, 2] + for (let d = 1; d <= 256; d++) { + efficientSchool.advance() + } + const sum = (x, y) => x + y + efficientSchool.state.reduce(sum) + }) + }) + }) +}) diff --git a/2021/day-06/index.js b/2021/day-06/index.js new file mode 100644 index 0000000..af7e035 --- /dev/null +++ b/2021/day-06/index.js @@ -0,0 +1,3 @@ +// eslint-disable-next-line no-unused-vars +const console = require('../helpers') +require('./solution') diff --git a/2021/day-06/input.txt b/2021/day-06/input.txt new file mode 100644 index 0000000..ff0b23a --- /dev/null +++ b/2021/day-06/input.txt @@ -0,0 +1 @@ +1,1,3,5,1,3,2,1,5,3,1,4,4,4,1,1,1,3,1,4,3,1,2,2,2,4,1,1,5,5,4,3,1,1,1,1,1,1,3,4,1,2,2,5,1,3,5,1,3,2,5,2,2,4,1,1,1,4,3,3,3,1,1,1,1,3,1,3,3,4,4,1,1,5,4,2,2,5,4,5,2,5,1,4,2,1,5,5,5,4,3,1,1,4,1,1,3,1,3,4,1,1,2,4,2,1,1,2,3,1,1,1,4,1,3,5,5,5,5,1,2,2,1,3,1,2,5,1,4,4,5,5,4,1,1,3,3,1,5,1,1,4,1,3,3,2,4,2,4,1,5,5,1,2,5,1,5,4,3,1,1,1,5,4,1,1,4,1,2,3,1,3,5,1,1,1,2,4,5,5,5,4,1,4,1,4,1,1,1,1,1,5,2,1,1,1,1,2,3,1,4,5,5,2,4,1,5,1,3,1,4,1,1,1,4,2,3,2,3,1,5,2,1,1,4,2,1,1,5,1,4,1,1,5,5,4,3,5,1,4,3,4,4,5,1,1,1,2,1,1,2,1,1,3,2,4,5,3,5,1,2,2,2,5,1,2,5,3,5,1,1,4,5,2,1,4,1,5,2,1,1,2,5,4,1,3,5,3,1,1,3,1,4,4,2,2,4,3,1,1 diff --git a/2021/day-06/solution.js b/2021/day-06/solution.js new file mode 100644 index 0000000..5fe66a1 --- /dev/null +++ b/2021/day-06/solution.js @@ -0,0 +1,47 @@ +const fs = require('fs') +const path = require('path') +const filePath = path.join(__dirname, 'input.txt') +const { parseData } = require('../../2018/inputParser') +const { school, efficientSchool } = require('./fish') + +fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => { + if (err) throw err + + initData = parseData(initData.trim()) + + const resetInput = () => { + // Deep copy to ensure we aren't mutating the original data + return JSON.parse(JSON.stringify(initData)) + } + + const part1 = () => { + const data = resetInput() + school.state = data + // Advance the designated time + for (let x = 0; x < 80; x++) { + school.advance() + } + // Count how many fish we have + return school.state.length + } + + const part2 = () => { + const data = resetInput() + efficientSchool.state = data + // Advance the designated time + for (let x = 0; x < 256; x++) { + efficientSchool.advance() + } + // Count how many fish we have + const sum = (x, y) => x + y + return efficientSchool.state.reduce(sum) + } + const answers = [] + answers.push(part1()) + answers.push(part2()) + + answers.forEach((ans, idx) => { + console.info(`-- Part ${idx + 1} --`) + console.info(`Answer: ${ans}`) + }) +}) diff --git a/index.js b/index.js index ed39a00..0d94d20 100644 --- a/index.js +++ b/index.js @@ -1 +1 @@ -require('./2021/day-05/solution') +require('./2021/day-06/solution') pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy