From b2eac5d86e6bf3c50b920a414d64ecba95cf9edc Mon Sep 17 00:00:00 2001 From: Anthony McLin Date: Tue, 14 Dec 2021 05:12:34 -0800 Subject: [PATCH 1/2] feat(2021-day-06): count how many laternfish exist after 80 days solves part 1 --- 2021/day-06/fish.js | 42 ++++++++++++++++++++++ 2021/day-06/fish.test.js | 78 ++++++++++++++++++++++++++++++++++++++++ 2021/day-06/index.js | 3 ++ 2021/day-06/input.txt | 1 + 2021/day-06/solution.js | 41 +++++++++++++++++++++ index.js | 2 +- 6 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 2021/day-06/fish.js create mode 100644 2021/day-06/fish.test.js create mode 100644 2021/day-06/index.js create mode 100644 2021/day-06/input.txt create mode 100644 2021/day-06/solution.js diff --git a/2021/day-06/fish.js b/2021/day-06/fish.js new file mode 100644 index 0000000..f3413d1 --- /dev/null +++ b/2021/day-06/fish.js @@ -0,0 +1,42 @@ + +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 + +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 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) + } +} + +module.exports = { + school, + ageFish, + spawn +} diff --git a/2021/day-06/fish.test.js b/2021/day-06/fish.test.js new file mode 100644 index 0000000..2d46687 --- /dev/null +++ b/2021/day-06/fish.test.js @@ -0,0 +1,78 @@ +/* eslint-env mocha */ +const { expect } = require('chai') +const { school, ageFish, spawn } = 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]) + school.advance() + }) + }) + }) +}) 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..e8be44d --- /dev/null +++ b/2021/day-06/solution.js @@ -0,0 +1,41 @@ +const fs = require('fs') +const path = require('path') +const filePath = path.join(__dirname, 'input.txt') +const { parseData } = require('../../2018/inputParser') +const { school } = 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() + console.debug(data) + return 'No answer yet' + } + 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') From f46f23a1c5c0b7535cbca75dc6156315155532bb Mon Sep 17 00:00:00 2001 From: Anthony McLin Date: Tue, 14 Dec 2021 06:09:38 -0800 Subject: [PATCH 2/2] feat(2021-day-06): efficient methods for tracking lampfish schools at scale solves part 2 --- 2021/day-06/fish.js | 35 ++++++++++++++++++++- 2021/day-06/fish.test.js | 67 ++++++++++++++++++++++++++++++++++++++-- 2021/day-06/solution.js | 12 +++++-- 3 files changed, 108 insertions(+), 6 deletions(-) diff --git a/2021/day-06/fish.js b/2021/day-06/fish.js index f3413d1..f6f5cbc 100644 --- a/2021/day-06/fish.js +++ b/2021/day-06/fish.js @@ -4,6 +4,10 @@ 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') } @@ -17,6 +21,11 @@ const spawn = (qty) => { _fishes.push(...newFishes) } +const efficientSpawn = (qty) => { + console.debug(`spawning ${qty} fish`) + _bigSchool[NewFishAge] = qty +} + const school = { get state () { return _fishes @@ -35,8 +44,32 @@ const school = { } } +/** + * 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 + spawn, + efficientSpawn } diff --git a/2021/day-06/fish.test.js b/2021/day-06/fish.test.js index 2d46687..bfdb0be 100644 --- a/2021/day-06/fish.test.js +++ b/2021/day-06/fish.test.js @@ -1,6 +1,6 @@ /* eslint-env mocha */ const { expect } = require('chai') -const { school, ageFish, spawn } = require('./fish') +const { school, ageFish, spawn, efficientSchool, efficientSpawn } = require('./fish') describe('--- Day 6: Lanternfish ---', () => { describe('Part 1', () => { @@ -71,7 +71,70 @@ describe('--- Day 6: Lanternfish ---', () => { 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]) - school.advance() + }) + }) + }) + 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/solution.js b/2021/day-06/solution.js index e8be44d..5fe66a1 100644 --- a/2021/day-06/solution.js +++ b/2021/day-06/solution.js @@ -2,7 +2,7 @@ const fs = require('fs') const path = require('path') const filePath = path.join(__dirname, 'input.txt') const { parseData } = require('../../2018/inputParser') -const { school } = require('./fish') +const { school, efficientSchool } = require('./fish') fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => { if (err) throw err @@ -27,8 +27,14 @@ fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => { const part2 = () => { const data = resetInput() - console.debug(data) - return 'No answer yet' + 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()) 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