Skip to content

Commit f46f23a

Browse files
feat(2021-day-06): efficient methods for tracking lampfish schools at scale
solves part 2
1 parent b2eac5d commit f46f23a

File tree

3 files changed

+108
-6
lines changed

3 files changed

+108
-6
lines changed

2021/day-06/fish.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ const NewFishAge = 8 // age of newly spawned fish
44
const FishSpawnAge = 0 // age when the fish spawns
55
const ResetFishAge = 6 // age of the original fish after spawning
66

7+
// allocate an empty big school
8+
const _newBigSchool = () => [...new Array(NewFishAge + 1)].map(() => 0)
9+
let _bigSchool = _newBigSchool()
10+
711
const ageFish = (age) => {
812
if (age > NewFishAge) { throw new Error('Fish is too young') }
913
if (age < FishSpawnAge) { throw new Error('Fish is too old') }
@@ -17,6 +21,11 @@ const spawn = (qty) => {
1721
_fishes.push(...newFishes)
1822
}
1923

24+
const efficientSpawn = (qty) => {
25+
console.debug(`spawning ${qty} fish`)
26+
_bigSchool[NewFishAge] = qty
27+
}
28+
2029
const school = {
2130
get state () {
2231
return _fishes
@@ -35,8 +44,32 @@ const school = {
3544
}
3645
}
3746

47+
/**
48+
* The efficient school doesn't track the position of the fish.
49+
* It only cares about the total number of fish of each age
50+
*/
51+
const efficientSchool = {
52+
get state () {
53+
return _bigSchool
54+
},
55+
set state (state) {
56+
_bigSchool = _newBigSchool()
57+
state.forEach((fish) => { _bigSchool[fish]++ })
58+
},
59+
advance: () => {
60+
// Calculate how many will spawn (age 0) and shift the age groups in one quick step
61+
const toSpawn = _bigSchool.shift()
62+
// Iterate old fish back to young since they're spawning
63+
_bigSchool[ResetFishAge] += toSpawn
64+
// Spawn the new fish
65+
efficientSpawn(toSpawn)
66+
}
67+
}
68+
3869
module.exports = {
3970
school,
71+
efficientSchool,
4072
ageFish,
41-
spawn
73+
spawn,
74+
efficientSpawn
4275
}

2021/day-06/fish.test.js

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-env mocha */
22
const { expect } = require('chai')
3-
const { school, ageFish, spawn } = require('./fish')
3+
const { school, ageFish, spawn, efficientSchool, efficientSpawn } = require('./fish')
44

55
describe('--- Day 6: Lanternfish ---', () => {
66
describe('Part 1', () => {
@@ -71,7 +71,70 @@ describe('--- Day 6: Lanternfish ---', () => {
7171
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])
7272
school.advance()
7373
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])
74-
school.advance()
74+
})
75+
})
76+
})
77+
describe('Part 2', () => {
78+
beforeEach(() => {
79+
// ensure flushed state
80+
efficientSchool.state = [3, 4, 3, 1, 2]
81+
expect(efficientSchool.state).to.deep.equal([0, 1, 1, 2, 1, 0, 0, 0, 0])
82+
})
83+
describe('efficientSpawn()', () => {
84+
it('efficiently adds new fish to the school', () => {
85+
efficientSpawn(4)
86+
expect(efficientSchool.state).to.deep.equal([0, 1, 1, 2, 1, 0, 0, 0, 4])
87+
})
88+
})
89+
describe('efficientAdvance', () => {
90+
it('advances one day following the same pattern without tracking unique position', () => {
91+
const sum = (x, y) => x + y
92+
efficientSchool.state = [3, 4, 3, 1, 2]
93+
94+
efficientSchool.advance()
95+
expect(efficientSchool.state.reduce(sum)).to.equal([2, 3, 2, 0, 1].length)
96+
efficientSchool.advance()
97+
expect(efficientSchool.state.reduce(sum)).to.equal([1, 2, 1, 6, 0, 8].length)
98+
efficientSchool.advance()
99+
expect(efficientSchool.state.reduce(sum)).to.equal([0, 1, 0, 5, 6, 7, 8].length)
100+
efficientSchool.advance()
101+
expect(efficientSchool.state.reduce(sum)).to.equal([6, 0, 6, 4, 5, 6, 7, 8, 8].length)
102+
efficientSchool.advance()
103+
expect(efficientSchool.state.reduce(sum)).to.equal([5, 6, 5, 3, 4, 5, 6, 7, 7, 8].length)
104+
efficientSchool.advance()
105+
expect(efficientSchool.state.reduce(sum)).to.equal([4, 5, 4, 2, 3, 4, 5, 6, 6, 7].length)
106+
efficientSchool.advance()
107+
expect(efficientSchool.state.reduce(sum)).to.equal([3, 4, 3, 1, 2, 3, 4, 5, 5, 6].length)
108+
efficientSchool.advance()
109+
expect(efficientSchool.state.reduce(sum)).to.equal([2, 3, 2, 0, 1, 2, 3, 4, 4, 5].length)
110+
efficientSchool.advance()
111+
expect(efficientSchool.state.reduce(sum)).to.equal([1, 2, 1, 6, 0, 1, 2, 3, 3, 4, 8].length)
112+
efficientSchool.advance()
113+
expect(efficientSchool.state.reduce(sum)).to.equal([0, 1, 0, 5, 6, 0, 1, 2, 2, 3, 7, 8].length)
114+
efficientSchool.advance()
115+
expect(efficientSchool.state.reduce(sum)).to.equal([6, 0, 6, 4, 5, 6, 0, 1, 1, 2, 6, 7, 8, 8, 8].length)
116+
efficientSchool.advance()
117+
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)
118+
efficientSchool.advance()
119+
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)
120+
efficientSchool.advance()
121+
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)
122+
efficientSchool.advance()
123+
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)
124+
efficientSchool.advance()
125+
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)
126+
efficientSchool.advance()
127+
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)
128+
efficientSchool.advance()
129+
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)
130+
})
131+
it('advances efficiently for a large number of days', () => {
132+
efficientSchool.state = [3, 4, 3, 1, 2]
133+
for (let d = 1; d <= 256; d++) {
134+
efficientSchool.advance()
135+
}
136+
const sum = (x, y) => x + y
137+
efficientSchool.state.reduce(sum)
75138
})
76139
})
77140
})

2021/day-06/solution.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const fs = require('fs')
22
const path = require('path')
33
const filePath = path.join(__dirname, 'input.txt')
44
const { parseData } = require('../../2018/inputParser')
5-
const { school } = require('./fish')
5+
const { school, efficientSchool } = require('./fish')
66

77
fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => {
88
if (err) throw err
@@ -27,8 +27,14 @@ fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => {
2727

2828
const part2 = () => {
2929
const data = resetInput()
30-
console.debug(data)
31-
return 'No answer yet'
30+
efficientSchool.state = data
31+
// Advance the designated time
32+
for (let x = 0; x < 256; x++) {
33+
efficientSchool.advance()
34+
}
35+
// Count how many fish we have
36+
const sum = (x, y) => x + y
37+
return efficientSchool.state.reduce(sum)
3238
}
3339
const answers = []
3440
answers.push(part1())

0 commit comments

Comments
 (0)