Skip to content

Commit 0955b5b

Browse files
Merge pull request #207 from amclin/feat/2021-day-09
Feat/2021 day 09
2 parents fa113b6 + 82914d0 commit 0955b5b

File tree

6 files changed

+338
-1
lines changed

6 files changed

+338
-1
lines changed

2021/day-09/basins.js

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
const isLow = (val, col, row, rows) => {
2+
// Collect points in each cardinal direction
3+
const points = []
4+
// TODO: If supporting diagonal checks, use this logic instead to loop
5+
// for (let x = -1; x <= 1; x++) {
6+
// for (let y = -1; y <= 1; y++) {
7+
// if(x != 0 && y != 0)
8+
// if(rows[row + y] && rows[row + y][col + x]
9+
// }
10+
// }
11+
if (rows[row - 1] && rows[row - 1][col]) { points.push(parseInt(rows[row - 1][col])) }
12+
if (rows[row + 1] && rows[row + 1][col]) { points.push(parseInt(rows[row + 1][col])) }
13+
if (rows[row] && rows[row][col - 1]) { points.push(parseInt(rows[row][col - 1])) }
14+
if (rows[row] && rows[row][col + 1]) { points.push(parseInt(rows[row][col + 1])) }
15+
16+
// NOTE - if the value is the same as a neighbor,
17+
// that isn't counted as a low (even though together, they can be a low)
18+
// ... this might be a concern for part 2 ....
19+
return (val < Math.min(...points)) // value should be lower than any other points
20+
}
21+
22+
const findLocalLows = (data) => {
23+
const lows = []
24+
const rows = data.split('\n')
25+
let checked = 0
26+
27+
rows.forEach((row, rowIdx) => {
28+
for (let c = 0; c < row.length; c++) {
29+
const cell = parseInt(row[c])
30+
if (isLow(cell, c, rowIdx, rows)) {
31+
lows.push(cell)
32+
console.debug(`Found low at ${c},${rowIdx}: ${cell}`)
33+
}
34+
checked++
35+
}
36+
})
37+
38+
console.debug(`Checked ${checked} cells`)
39+
return lows
40+
}
41+
42+
const flow = (col, row, map, data, source) => {
43+
// Don't test invalid points
44+
if (col < 0 || col >= map.coords[0].length) {
45+
console.debug(`${col},${row} is out of bounds`)
46+
// Exceeds map horizontally
47+
return {
48+
map,
49+
result: false
50+
}
51+
}
52+
if (row < 0 || row >= map.coords.length) {
53+
console.debug(`${col},${row} is out of bounds`)
54+
// Exceeds map vertically
55+
return {
56+
map,
57+
result: false
58+
}
59+
}
60+
61+
// If the point is a peak, register and don't continue
62+
if (parseInt(data[row][col]) === 9) {
63+
console.debug(`${col},${row} is a peak.`)
64+
// Peaks aren't part of basins
65+
map.coords[row][col] = 'p'
66+
return {
67+
map,
68+
result: false
69+
}
70+
}
71+
72+
// If the point is higher than the source, we can't drain
73+
// BIG ASSUMPTION here about equal-height points
74+
if (data[row][col] >= source) {
75+
console.debug(`${col},${row} is higher (${data[row][col]} >= ${source}). Water can't flow uphill.`)
76+
return {
77+
map,
78+
result: false
79+
}
80+
}
81+
82+
// If the point already mapped to a basin, don't recalculate its flow
83+
if (map.coords[row] && map.coords[row][col]) {
84+
console.debug(`${col},${row} is already known to be in basin ${map.coords[row][col]}`)
85+
return {
86+
map,
87+
result: map.coords[row][col]
88+
}
89+
}
90+
91+
// If we've reached a low point, stop tracing
92+
if (isLow(data[row][col], col, row, data)) {
93+
console.debug(`${col},${row} is a low point in basin.`)
94+
// register a basin with an area of 1
95+
map.basins.push(1)
96+
// mark the low point to the basin
97+
map.coords[row][col] = map.basins.length - 1
98+
console.debug(`registered basin ${map.basins.length - 1}`)
99+
return {
100+
map,
101+
result: map.coords[row][col]
102+
}
103+
// HUGE ASSUMPTION that each basin only has 1 low point
104+
}
105+
106+
console.debug(`checking where point ${col},${row} drains to`)
107+
108+
// Check the next points in each cardinal direction
109+
const drains = []
110+
let result = false
111+
result = flow(col + 1, row, map, data, data[row][col]) // right
112+
map = result.map
113+
drains.push(result.result)
114+
result = flow(col - 1, row, map, data, data[row][col]) // left
115+
map = result.map
116+
drains.push(result.result)
117+
result = flow(col, row - 1, map, data, data[row][col]) // up
118+
map = result.map
119+
drains.push(result.result)
120+
result = flow(col, row + 1, map, data, data[row][col]) // down
121+
map = result.map
122+
drains.push(result.result)
123+
124+
const results = drains.filter((c) => c !== false)
125+
if (results.length > 1) {
126+
console.warn('Point has more than one possilbe drain.')
127+
const uniqueDrains = [...new Set(results)]
128+
if (uniqueDrains.length > 1) {
129+
console.debug(drains)
130+
throw new Error('Point drains into multiple drains. Data might be bad.')
131+
}
132+
// Otherwise, all drains go to the same basin, so that's the same as having 1 drain
133+
}
134+
if (results.length === 0) {
135+
console.debug(drains)
136+
throw new Error('Point is not the low, but has no drains. Data might be bad.')
137+
}
138+
139+
const basin = parseInt(results[0])
140+
141+
// Mark the point as belonging to the basin it drains into
142+
map.coords[row][col] = basin
143+
// Track the area of the basin so we don't have to recalculate it later
144+
map.basins[basin]++
145+
146+
// return the findings recursively
147+
return {
148+
map,
149+
result: map.coords[row][col]
150+
}
151+
}
152+
153+
module.exports = {
154+
findLocalLows,
155+
flow
156+
}

2021/day-09/basins.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* eslint-env mocha */
2+
const { expect } = require('chai')
3+
const { findLocalLows } = require('./basins')
4+
5+
const testData = `2199943210
6+
3987894921
7+
9856789892
8+
8767896789
9+
9899965678`
10+
11+
describe('--- Day 9: Smoke Basin ---', () => {
12+
describe('Part 1', () => {
13+
describe('findLocalLows()', () => {
14+
it('finds the local low points in the terrain', () => {
15+
// low points in the testData are 1, 0 on row 0
16+
// 5 on row 2
17+
// 5 on row 5
18+
const results = findLocalLows(testData)
19+
expect(results.length).to.equal(4)
20+
expect(results.reduce((a, b) => a + b)).to.equal(11)
21+
})
22+
})
23+
})
24+
})

2021/day-09/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-09/input.txt

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
5434456789899876598943212349988734679896554212468998765634987654567895454569876745567898765987654678
2+
4421345997789997987894103458976521398765432101457897664529876543988987353478965434456799974398766789
3+
3210237896698999876789214567895320129878543212346987543212989432499976212389876521234589867219878999
4+
8732456934567987655678925679976543234999657323657898952101296521276895423467987420145678954323989987
5+
7545678923979996544367899899997665379898775454767899843212398730145789439999876521266789765439896895
6+
8656989019899985431257912978989876456789896569898998754324579821234679598787987632348999976598765434
7+
9767893198769876542349943969878987567891987678959799867465679932348789987656899973467898899989754325
8+
9899954599356987653567899858767898998910198789345689879876898655459892199767898765989987678978965434
9+
0949765983245898974567986745656789429963239891256998989987998798767891019898999899899876569867896645
10+
1239899874123798765678965432345678998754356999969897896998919999898942129919899989798765432956789896
11+
2456999763235679976789876521012899569896567987898776645789423998989763298901698978679654321987899987
12+
3567998654345789697894997863246789345989679976897655434678999887678954397992497761598767210198989999
13+
4568998767658994598932987654959896559878998965689543212789989756545895986789986550349989321239878934
14+
5679109978767893459891098799898998698766987654599652101299876542136789975498765431256999932349867995
15+
6789299989878932398789129988797789797654598543688943212389987651012898996329765432367899893998656789
16+
7898989899989321239679939878646678976543987654567894345678999954123456789319876783456987679899543454
17+
8987674789996510398567898965434567895432398769878997656789298893256567892101989865597976545788932123
18+
9876543668897932987678987654325478999421239878989989897892196799347878943212399876789876434567893015
19+
6976542456789893498789798843412345678910129989095678998943985978958989954323498989899854323479992134
20+
5988764567898789999895699752103456989321298792134578949959874567899699895434597898998769436589789645
21+
3299985998976679891934987653212367895433397654236689939898765878934598789545986567899898998995679756
22+
4399876789765456790949998654354567976564579864346789795789876989423987678959876456789987999754598967
23+
5989987898874345689898998765667678987678679875467997674898997893219876579898965345699876899643457999
24+
9874399987654234889767249879799899398788799986578998543456789979101985498787976458798765678952348688
25+
9965456798982124678954123998989921299899899897789019432349892356919876987656897569987654569861234567
26+
9878767899971012567893299897678930987943945798899929656478901237895988995434789978999543498990195688
27+
4989879998753223456789987684567899876542134689967898767567892348954599986523678989898921987989989789
28+
3292998987654345567899876543454979987698745798756789888678943456795678987434567898797990196578878990
29+
5101997699865456878999987952123567898987656987645999999799764789989799398646678987686789987458767891
30+
3219876439876767989789854321018798909998789398739898998999875699978989109798789999575679654345656789
31+
4923987421988878994698765432134689212999899219898787887689987898967878919899899998434598743212345699
32+
9894996210199989543569876987656789329896998998997645986567898987657567894902999896565679754103456999
33+
8789765434567896532979987899769895498785987987987432345457899996543456793219899789678998765214667898
34+
7679987545698987649899998959878999597654596576796541012345678987532345789398798698989439886356778987
35+
6567899756789498998787899543999788999869989435987782134456989997431234678987654587892129987487899876
36+
1456789987891349987656987682101677896998764324598943245667892986520345679976743476789098987598989988
37+
2367891098942998876435699899323456795109875455799876368998901975421234567895432125678987798679769899
38+
3458932989959897652123799998754789894298989569899965467999219876532345689984321014789976689789543767
39+
6567899876798789943254889879875678989997898698999876567894334998765456796975432423899865579899632156
40+
7678999865987656798765679765996899569876679987987987678999549769976789895496676534998784465978943234
41+
8789489954698747899898789894597943456965569876756798989998998756987899932398987545797653234567894965
42+
9896567893497656789989899989698952349893457954348899999867897645698998651239998676789543123456789896
43+
4989678912399788997678969878999876599789569943235978987656798434569679962345999987898654234567898797
44+
3479989101989899234579953967899997987679979754101567896546795320197599843469899998939764346678999689
45+
2567893219876920123498842545798798996589898765243489998657896431989987654598788999323976487989898567
46+
1457954998985431245987721234989659987456799876756678998798986549879999867998687896534987578998766456
47+
0345699877896545679876510129878943298589890987987899989899598698768899979896556789649898789789654233
48+
1258789565987656798765431298967892129678992999398987667965498797658789998787445679998769897698763101
49+
3879895434598767899876532987656789098789979893219898548994349988546698987674324567898756989549874222
50+
4989999323569899987989749876545699989898767689456798656789239975234567896532014687996545678934965434
51+
5699888912479959876599898765434598878989954597997959897891098754123458976432123456789436789029896945
52+
6789767893569542987456999876523987654567893456789542998943987653012369897643234797897647892198789896
53+
7996556789698959898968998765219876543478932345698951019995698432123456797654365698998758943987668689
54+
8954345678987998759979897654327989657899321234567892129889997643264567899895776789019869959876547567
55+
9543287567896789543499789876456798798999872345678963498769898965459678999976798997998999898954323458
56+
8762123456795678932987678976578979899698765456789954989656789878678989689987899756987889767895444767
57+
6571012387894239321998489987678967989459889578997899876545995989789996577998987645986563456976655678
58+
8432123456943145939876569998989345678967998789756798765634894399898895456879995429876432356897779899
59+
7543234579432019899998678989492235678979679897645987654329795210956789327467896539994321237998989931
60+
8654345678973298799659989976320136789989536965534598965418689632345893214357987698789435398949995432
61+
9765566899954989578945698765431246789997645984323459876324596543456789301236798797698945459539876543
62+
9878987899869876457936789987543657894987659876775767988765797786569895212545679987567896579321997654
63+
9989398998998765346897894697654769923498767987899878999896789897678954323598789498678987678932398895
64+
9899459987679976456789923498767878939999989998910989999987894998789765499989892349989698989993979976
65+
8798967943568989769893212459878989998791299879322398989998943769999876987878991234596439498789765987
66+
7667899652456899878976493967989399876689399965443987878999542356789989776456789949697321334599654598
67+
6545678943597956989987989898996598985545989987559876769897669467894597654345699898989210123498963239
68+
9436789894989439997899876789987987694234567897698765458789798978932987543248998767678933234987892134
69+
8997898789878998876799985689998975543123458998789987345678997989321997654367999856577894549876789045
70+
6889997679767897545989654567899984321012567899898753234789986899439898765456897845456789698765692156
71+
5679986545656989439876543678920995443227679998999864565699875778999789996567986434345698799754589267
72+
4798765434745778924987762399439876654535789587998765699789564567987678987698954323236789988643678979
73+
3987654321434567995699843489597987765678996476899876789895423459876569299899875210127999876512889989
74+
2398776410123456789987654578976598978789875345789987899964312346975489101999986321239899974323690198
75+
1459898531234587898798767678965439989999954234598798979975423769876594312398765432349798765434569297
76+
2699876542346678997649878789432123499999876345987659567897569878987694324569898543498659896565678956
77+
9989987843656789989432989896543034578989985456798741456898689989299789455689987654987545987876789245
78+
8978998654567897678921299987652145989979876787899432346789799892109896596798998965696534698987992123
79+
7767899765678976567932349998543267898765998998989753456899896789512987989987899878987623499698993434
80+
6456789878789995478945678997654379987654989999978999567899945695423499879876789989998834988549889645
81+
2348995999998989599656789998765459998769877898765688979998956789534598766545679199876549876534678956
82+
1256893212987876989767899869876567899898756987654567999986899899976987655326989298987698765423459867
83+
2367999323986765878998987656987878998999967898323457899875798999897898543201399987598789879210678978
84+
3478998939765654569999896545698989667989899999212345899964567898799939954912568898449899984321289989
85+
4989997898754343456789765434349893459879798987463456789643456789688929899893456789234989965434567896
86+
5678976798773212345899984321256789599965667896554569896532567894567998789789598894345679876565778965
87+
6899995987654301236789876532367899987654356987789678997321013789679876545699679965956789987876789954
88+
8987989998765312447897987688456789998543234598898789398432123678989999433488989989897999998998899895
89+
9766767989876493558965499786567896987652105679999891298743454569999878521367893198769899899899966789
90+
7654555679997989967894329897898945698543212589899999987655677678998765310456789976556789765732455894
91+
6553334899989879898965469989999334987654323456789678998766788889429896321367899896345678954621234932
92+
5432124578978956789876598976989219999768454579894599659877899995434975433698998765234579543210129891
93+
6543023569864345699997986745878998789879767699923589547998998989565986844589987654357897654521656790
94+
9632123479983234567898965434567899667989888789013478936669987879976987865679999866468999865632347891
95+
8743244569874347689999874323456965456795999993123569425459986567899898979789899998567899876774458942
96+
8654456798765456790198765201597896568954987654256989312398765458968799989897789219778945988765667899
97+
9987587899976569891239877332387899878943298965345993201239954349355689997956678909899434199876789978
98+
1098788998988789932545976545456956989652129876556789713498765693234598766434567899974321019998893459
99+
3129999987699896543489988758969139898761034987679899654569876789345679654323489998765432198779902345
100+
4534567898799987654578999767878956789432156798789998775678987895468798765434591249976545698654323456

2021/day-09/solution.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const filePath = path.join(__dirname, 'input.txt')
4+
const { linesToArray } = require('../../2018/inputParser')
5+
const { findLocalLows, flow } = require('./basins')
6+
7+
fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => {
8+
if (err) throw err
9+
10+
initData = linesToArray(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().join('\n')
19+
const lows = findLocalLows(data)
20+
console.debug(lows.length)
21+
// risk profiles is the sum of each height + 1, which is the same as adding the count of lows
22+
return lows.reduce((a, b) => a + b) + lows.length
23+
}
24+
25+
const part2 = () => {
26+
const data = resetInput()
27+
let map = {
28+
coords: [...Array(data.length)].map(() => Array(data[0].length)), // allocate a map
29+
basins: [0] // fake first basin with no area to avoid having to figure out JS loose-type false/truthy and <> bullshit
30+
}
31+
32+
for (let x = 0; x < data[0].length; x++) {
33+
for (let y = 0; y < data.length; y++) {
34+
console.debug(`Starting flow trace at ${x},${y}`)
35+
map = flow(x, y, map, data, 9).map // 9 is a magic number for assuming we start with fresh drains
36+
}
37+
}
38+
39+
console.debug(`Found ${map.basins.length} basins.`)
40+
41+
// Multiply the area of the 3 largest basins
42+
return map.basins.sort((a, b) => a - b)
43+
.slice(map.basins.length - 3)
44+
.reduce((a, b) => a * b)
45+
}
46+
const answers = []
47+
answers.push(part1())
48+
answers.push(part2())
49+
50+
answers.forEach((ans, idx) => {
51+
console.info(`-- Part ${idx + 1} --`)
52+
console.info(`Answer: ${ans}`)
53+
})
54+
})

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
require('./2021/day-08/solution')
1+
require('./2021/day-09/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