Skip to content

Commit 62cd42a

Browse files
Merge pull request #204 from amclin/feat/2021-day-06
Feat/2021 day 06
2 parents 6272342 + f46f23a commit 62cd42a

File tree

6 files changed

+268
-1
lines changed

6 files changed

+268
-1
lines changed

2021/day-06/fish.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
2+
let _fishes = []
3+
const NewFishAge = 8 // age of newly spawned fish
4+
const FishSpawnAge = 0 // age when the fish spawns
5+
const ResetFishAge = 6 // age of the original fish after spawning
6+
7+
// allocate an empty big school
8+
const _newBigSchool = () => [...new Array(NewFishAge + 1)].map(() => 0)
9+
let _bigSchool = _newBigSchool()
10+
11+
const ageFish = (age) => {
12+
if (age > NewFishAge) { throw new Error('Fish is too young') }
13+
if (age < FishSpawnAge) { throw new Error('Fish is too old') }
14+
if (age === FishSpawnAge) { return ResetFishAge }
15+
return age - 1
16+
}
17+
18+
const spawn = (qty) => {
19+
console.debug(`spawning ${qty} fish`)
20+
const newFishes = [...new Array(qty)].map(() => NewFishAge)
21+
_fishes.push(...newFishes)
22+
}
23+
24+
const efficientSpawn = (qty) => {
25+
console.debug(`spawning ${qty} fish`)
26+
_bigSchool[NewFishAge] = qty
27+
}
28+
29+
const school = {
30+
get state () {
31+
return _fishes
32+
},
33+
set state (state) {
34+
_fishes = state
35+
},
36+
37+
advance: () => {
38+
// Calculate how many will spawn
39+
const toSpawn = _fishes.filter((x) => x === FishSpawnAge).length
40+
// Iterate each fish
41+
_fishes = _fishes.map(ageFish)
42+
// Spawn the new fish
43+
spawn(toSpawn)
44+
}
45+
}
46+
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+
69+
module.exports = {
70+
school,
71+
efficientSchool,
72+
ageFish,
73+
spawn,
74+
efficientSpawn
75+
}

2021/day-06/fish.test.js

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/* eslint-env mocha */
2+
const { expect } = require('chai')
3+
const { school, ageFish, spawn, efficientSchool, efficientSpawn } = require('./fish')
4+
5+
describe('--- Day 6: Lanternfish ---', () => {
6+
describe('Part 1', () => {
7+
beforeEach(() => {
8+
// ensure flushed state
9+
school.state = [3, 4, 3, 1, 2]
10+
expect(school.state).to.deep.equal([3, 4, 3, 1, 2])
11+
})
12+
describe('spawn()', () => {
13+
it('adds new fish to the end of the list', () => {
14+
spawn(4)
15+
expect(school.state).to.deep.equal([3, 4, 3, 1, 2, 8, 8, 8, 8])
16+
})
17+
})
18+
describe('ageFish()', () => {
19+
it('ages a particular fish', () => {
20+
expect(ageFish(6)).to.equal(5)
21+
expect(ageFish(5)).to.equal(4)
22+
expect(ageFish(4)).to.equal(3)
23+
expect(ageFish(3)).to.equal(2)
24+
expect(ageFish(2)).to.equal(1)
25+
expect(ageFish(1)).to.equal(0)
26+
expect(ageFish(0)).to.equal(6)
27+
expect(ageFish(8)).to.equal(7)
28+
expect(ageFish(7)).to.equal(6)
29+
})
30+
it('throws an error if the fish is out of range', () => {
31+
expect(() => { ageFish(9) }).to.throw('Fish is too young')
32+
expect(() => { ageFish(-1) }).to.throw('Fish is too old')
33+
})
34+
})
35+
describe('advance()', () => {
36+
it('advances one day', () => {
37+
school.state = [3, 4, 3, 1, 2]
38+
school.advance()
39+
expect(school.state).to.deep.equal([2, 3, 2, 0, 1])
40+
school.advance()
41+
expect(school.state).to.deep.equal([1, 2, 1, 6, 0, 8])
42+
school.advance()
43+
expect(school.state).to.deep.equal([0, 1, 0, 5, 6, 7, 8])
44+
school.advance()
45+
expect(school.state).to.deep.equal([6, 0, 6, 4, 5, 6, 7, 8, 8])
46+
school.advance()
47+
expect(school.state).to.deep.equal([5, 6, 5, 3, 4, 5, 6, 7, 7, 8])
48+
school.advance()
49+
expect(school.state).to.deep.equal([4, 5, 4, 2, 3, 4, 5, 6, 6, 7])
50+
school.advance()
51+
expect(school.state).to.deep.equal([3, 4, 3, 1, 2, 3, 4, 5, 5, 6])
52+
school.advance()
53+
expect(school.state).to.deep.equal([2, 3, 2, 0, 1, 2, 3, 4, 4, 5])
54+
school.advance()
55+
expect(school.state).to.deep.equal([1, 2, 1, 6, 0, 1, 2, 3, 3, 4, 8])
56+
school.advance()
57+
expect(school.state).to.deep.equal([0, 1, 0, 5, 6, 0, 1, 2, 2, 3, 7, 8])
58+
school.advance()
59+
expect(school.state).to.deep.equal([6, 0, 6, 4, 5, 6, 0, 1, 1, 2, 6, 7, 8, 8, 8])
60+
school.advance()
61+
expect(school.state).to.deep.equal([5, 6, 5, 3, 4, 5, 6, 0, 0, 1, 5, 6, 7, 7, 7, 8, 8])
62+
school.advance()
63+
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])
64+
school.advance()
65+
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])
66+
school.advance()
67+
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])
68+
school.advance()
69+
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])
70+
school.advance()
71+
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])
72+
school.advance()
73+
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+
})
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)
138+
})
139+
})
140+
})
141+
})

2021/day-06/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// eslint-disable-next-line no-unused-vars
2+
const console = require('../helpers')
3+
require('./solution')

2021/day-06/input.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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

2021/day-06/solution.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const filePath = path.join(__dirname, 'input.txt')
4+
const { parseData } = require('../../2018/inputParser')
5+
const { school, efficientSchool } = require('./fish')
6+
7+
fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => {
8+
if (err) throw err
9+
10+
initData = parseData(initData.trim())
11+
12+
const resetInput = () => {
13+
// Deep copy to ensure we aren't mutating the original data
14+
return JSON.parse(JSON.stringify(initData))
15+
}
16+
17+
const part1 = () => {
18+
const data = resetInput()
19+
school.state = data
20+
// Advance the designated time
21+
for (let x = 0; x < 80; x++) {
22+
school.advance()
23+
}
24+
// Count how many fish we have
25+
return school.state.length
26+
}
27+
28+
const part2 = () => {
29+
const data = resetInput()
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)
38+
}
39+
const answers = []
40+
answers.push(part1())
41+
answers.push(part2())
42+
43+
answers.forEach((ans, idx) => {
44+
console.info(`-- Part ${idx + 1} --`)
45+
console.info(`Answer: ${ans}`)
46+
})
47+
})

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
require('./2021/day-05/solution')
1+
require('./2021/day-06/solution')

0 commit comments

Comments
 (0)
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