Skip to content

Feat/2021 day 06 #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions 2021/day-06/fish.js
Original file line number Diff line number Diff line change
@@ -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
}
141 changes: 141 additions & 0 deletions 2021/day-06/fish.test.js
Original file line number Diff line number Diff line change
@@ -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)
})
})
})
})
3 changes: 3 additions & 0 deletions 2021/day-06/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// eslint-disable-next-line no-unused-vars
const console = require('../helpers')
require('./solution')
1 change: 1 addition & 0 deletions 2021/day-06/input.txt
Original file line number Diff line number Diff line change
@@ -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
47 changes: 47 additions & 0 deletions 2021/day-06/solution.js
Original file line number Diff line number Diff line change
@@ -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}`)
})
})
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
require('./2021/day-05/solution')
require('./2021/day-06/solution')