Skip to content

Commit 9c5fd5d

Browse files
Merge pull request #2 from amclin/feature/2018-day-12
Feature/2018 day 12
2 parents c151fa5 + 377d7fc commit 9c5fd5d

File tree

7 files changed

+505
-0
lines changed

7 files changed

+505
-0
lines changed

2018/day-10/helpers.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const parseLine = (input) => {
4444
}
4545

4646
module.exports = {
47+
listToProps: _listToProps,
4748
loadInput,
4849
parseLine
4950
}

2018/day-12/helpers.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const { listToProps } = require('../day-10/helpers')
2+
const fs = require('fs')
3+
const path = require('path')
4+
const filePath = path.join(__dirname, 'input.txt')
5+
const { linesToArray } = require('../inputParser')
6+
7+
const loadInput = (callback) => {
8+
fs.readFile(filePath, { encoding: 'utf8' }, (err, data) => {
9+
if (err) throw err
10+
11+
const list = linesToArray(data).map(parseLine)
12+
if (typeof callback === 'function') {
13+
callback(list)
14+
}
15+
})
16+
}
17+
18+
/**
19+
* Parses a line from the input into structured data
20+
* @param {String} input Line from input stream
21+
* @returns Structured object with patterns and states
22+
*/
23+
const parseLine = (input) => {
24+
return input.split(' => ').reduce((acc, curr, idx) => listToProps(acc, curr, idx, ['id', 'generate']), {})
25+
}
26+
27+
module.exports = {
28+
loadInput,
29+
parseLine
30+
}

2018/day-12/helpers.test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/* eslint-env mocha */
2+
const expect = require('chai').expect
3+
const {
4+
parseLine
5+
} = require('./helpers')
6+
7+
describe('--- Day 12: Subterranean Sustainability ---', () => {
8+
describe('Helpers:', () => {
9+
describe('parseLine(input)', () => {
10+
it('converts a line of the input into structured object', () => {
11+
const test = `..#.. => #`
12+
const expected = {
13+
id: '..#..',
14+
generate: '#'
15+
}
16+
const actual = parseLine(test)
17+
expect(actual).to.deep.equal(expected)
18+
})
19+
})
20+
})
21+
})

2018/day-12/input.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
.#### => .
2+
...#. => .
3+
.##.. => #
4+
#.##. => .
5+
#..## => .
6+
##### => #
7+
####. => #
8+
.##.# => #
9+
#.### => .
10+
...## => #
11+
.#.## => #
12+
#..#. => #
13+
#.#.. => #
14+
.###. => #
15+
##.## => #
16+
##..# => .
17+
.#... => #
18+
###.# => .
19+
..##. => .
20+
..... => .
21+
###.. => #
22+
..#.# => .
23+
.#..# => #
24+
##... => #
25+
#.... => .
26+
##.#. => .
27+
..#.. => #
28+
....# => .
29+
#...# => .
30+
#.#.# => #
31+
..### => .
32+
.#.#. => #

2018/day-12/plants.js

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
class Plants {
2+
constructor (initial, rules) {
3+
this.generations = []
4+
this.boundaryBuffers = [0, 0]
5+
this.rules = {}
6+
if (rules && rules.length > 0) {
7+
rules.forEach((rule) => {
8+
this.addRule(rule)
9+
})
10+
}
11+
this.boundaryBuffers = this.getBoundaryBuffers()
12+
this._setInitialGeneration(initial)
13+
}
14+
15+
_setInitialGeneration (initial) {
16+
this.generations.push(
17+
initial.split('').map((plant, idx) => {
18+
return {
19+
position: idx,
20+
state: plant
21+
}
22+
})
23+
)
24+
this.generations[0] = this.pad(this.generations[0])
25+
}
26+
27+
/**
28+
* Adds a rule to the rules list
29+
* @param {Object} rule {id: "..#..", generate: "#"}
30+
* @returns {Object} Plants instance for chaining
31+
*/
32+
addRule (rule) {
33+
this.rules[rule.id] = rule.generate
34+
return this // allow chaining
35+
}
36+
37+
/**
38+
* Determines the the pot state based on the pattern provided
39+
* @param {String} pattern "..#.."
40+
* @returns {String} "." or "#"
41+
*/
42+
predictPlant (pattern) {
43+
const result = this.rules[pattern] || '.'
44+
return result
45+
}
46+
47+
/**
48+
* Advances the plants by one generation
49+
* @returns {Array} The results of the new generation
50+
*/
51+
advance () {
52+
const prevGen = this.generations[this.generations.length - 1]
53+
54+
// Loop through all pots in the last generation to create a new generation
55+
let nextGen = prevGen.map((pot) => {
56+
// Assemble pattern for the given pot
57+
let pattern = ''
58+
for (let x = pot.position - 2; x <= pot.position + 2; x++) {
59+
let pp = prevGen.find((p) => p.position === x)
60+
pattern += (pp) ? pp.state : '.'
61+
}
62+
const state = this.predictPlant(pattern)
63+
64+
return {
65+
position: pot.position,
66+
state: state
67+
}
68+
})
69+
70+
nextGen = this.pad(nextGen)
71+
72+
// Store the new generation
73+
this.generations.push(nextGen)
74+
return this.generations[this.generations.length - 1]
75+
}
76+
77+
/**
78+
* Renders out a visual display of the pots in all generations
79+
* Wrapper for getDisplay()
80+
* @param {Number} start First pot to include
81+
* @param {Number} end Last pot to include
82+
*/
83+
display (start, end) {
84+
console.log(this.getDisplay(start, end))
85+
}
86+
87+
/**
88+
* Scans the generations to find the leftmost and rightmost pot with a plant
89+
* @returns {Array} [left, right] position of first and last pots
90+
*/
91+
findPotBoundaries () {
92+
return this.generations.reduce((acc, gen) => {
93+
let pots = gen.filter((p) => p.state === '#')
94+
return [
95+
acc[0] < pots[0].position ? acc[0] : pots[0].position,
96+
acc[1] < pots[pots.length - 1].postion ? acc[1] : pots[pots.length - 1].position
97+
]
98+
}, [0, 0])
99+
}
100+
101+
/**
102+
* Rules need empty pots lef/right of row for verification. Figures out the number of pots we need
103+
* to add to the left/right of a row
104+
* @returns {Array} [left, right] necessary buffer size of first and last pots
105+
*/
106+
getBoundaryBuffers () {
107+
let buffers = [0, 0]
108+
Object.keys(this.rules).filter((rule) => this.rules[rule] === '#').forEach((rule) => {
109+
// For left edge
110+
for (let x = 0; x < rule.length; x++) {
111+
if (rule.substr(x, 1) === '.') {
112+
let y = x + 1
113+
buffers[0] = Math.max(buffers[0], y)
114+
} else {
115+
// break the loop when we encounter a #
116+
x = rule.length
117+
break
118+
}
119+
}
120+
121+
for (let x = rule.length - 1; x >= 0; x--) {
122+
if (rule.substr(x, 1) === '.') {
123+
let y = rule.length - x
124+
buffers[1] = Math.max(buffers[1], y)
125+
} else {
126+
// break the loop when we encounter a #
127+
x = -1
128+
break
129+
}
130+
}
131+
})
132+
return buffers
133+
}
134+
135+
/**
136+
* Generates a visual display of the pots in all generations. Accepts optional boundaries.
137+
* @param {Number} start First pot to include
138+
* @param {Number} end Last pot to include.
139+
*/
140+
getDisplay (start, end) {
141+
// Find boundaries if not provided
142+
if (!start || !end) {
143+
const boundaries = this.findPotBoundaries()
144+
start = start || boundaries[0] - 1
145+
end = end || boundaries[1] + 1
146+
}
147+
let output = ''
148+
this.generations.forEach((gen) => {
149+
for (let p = start; p <= end; p++) {
150+
const plant = gen.find((pot) => pot.position === p)
151+
output += (plant) ? plant.state : '.'
152+
}
153+
output += '\n'
154+
})
155+
return output.trim()
156+
}
157+
158+
/**
159+
* Sums the number of plants present in all generations
160+
*/
161+
getPlantTotal () {
162+
return this.generations.reduce((gacc, g) => {
163+
return gacc + g.filter((p) => p.state === '#').length
164+
}, 0)
165+
}
166+
167+
/**
168+
* Generates a checksum calculated by summing the positions of all pots containing plants
169+
* in a specified generation
170+
* @param {Number} generation to generate checksum for
171+
*/
172+
getCheckSum (generation) {
173+
return this.generations[generation].filter((p) => p.state === '#')
174+
.reduce((pacc, p) => pacc + p.position, 0)
175+
}
176+
177+
/**
178+
* Pads the generation to the left and right so the pots are present to support future generations
179+
* @param {Array} generation List of pots representing a generation
180+
* @returns generation list with extra pots
181+
*/
182+
pad (generation) {
183+
for (let x = 1; x <= this.boundaryBuffers[0]; x++) {
184+
const first = generation[0].position
185+
generation.splice(0, 0, { position: first - 1, state: '.' })
186+
}
187+
for (let x = 1; x <= this.boundaryBuffers[1]; x++) {
188+
const last = generation[generation.length - 1].position
189+
generation.push({ position: last + 1, state: '.' })
190+
}
191+
return generation
192+
}
193+
}
194+
195+
module.exports = {
196+
Plants
197+
}

0 commit comments

Comments
 (0)