133 lines
3.4 KiB
JavaScript
133 lines
3.4 KiB
JavaScript
const { Glib, Vector, twodee } = require('../../lib');
|
|
|
|
const STATE = {
|
|
EMPTY: 'L',
|
|
FLOOR: '.',
|
|
OCCUPIED: '#',
|
|
NONE: 'X',
|
|
};
|
|
|
|
const step1 = (input) => {
|
|
let changes = 0n;
|
|
|
|
const output = input.glibEntries
|
|
.map(([pts, state]) => {
|
|
switch (state) {
|
|
case STATE.EMPTY:
|
|
if (
|
|
Vector.fromString(pts)
|
|
.neighbors()
|
|
.map((pt) => pt.string)
|
|
.lookupInMap(input, true)
|
|
.filter((neighborState) => neighborState === STATE.OCCUPIED)
|
|
.length === 0n
|
|
) {
|
|
changes++;
|
|
return [pts, STATE.OCCUPIED];
|
|
}
|
|
return [pts, STATE.EMPTY];
|
|
case STATE.OCCUPIED:
|
|
if (
|
|
Vector.fromString(pts)
|
|
.neighbors()
|
|
.map((pt) => pt.string)
|
|
.lookupInMap(input, true)
|
|
.filter((neighborState) => neighborState === STATE.OCCUPIED)
|
|
.length >= 4n
|
|
) {
|
|
changes++;
|
|
return [pts, STATE.EMPTY];
|
|
}
|
|
return [pts, STATE.OCCUPIED];
|
|
case STATE.FLOOR:
|
|
return [pts, STATE.FLOOR];
|
|
default:
|
|
console.log(pts, state);
|
|
throw new Error('invalid state');
|
|
}
|
|
})
|
|
.toMap();
|
|
|
|
return [output, changes];
|
|
};
|
|
|
|
const visibleNeighbor = (point, direction, state) => {
|
|
let next = point.add(direction);
|
|
while (true) {
|
|
const nextString = next.string;
|
|
if (!state.has(nextString)) {
|
|
return state.NONE;
|
|
}
|
|
const found = state.get(nextString);
|
|
if (found !== STATE.FLOOR) {
|
|
return found;
|
|
}
|
|
next = next.add(direction);
|
|
}
|
|
};
|
|
|
|
const step2 = (input) => {
|
|
let changes = 0n;
|
|
|
|
const output = input.glibEntries
|
|
.map(([pts, state]) => {
|
|
const pt = Vector.fromString(pts);
|
|
switch (state) {
|
|
case STATE.EMPTY:
|
|
if (
|
|
twodee.ORIGIN.neighbors()
|
|
.map((direction) => visibleNeighbor(pt, direction, input))
|
|
.filter((neighborState) => neighborState === STATE.OCCUPIED)
|
|
.length === 0n
|
|
) {
|
|
changes++;
|
|
return [pts, STATE.OCCUPIED];
|
|
}
|
|
return [pts, STATE.EMPTY];
|
|
case STATE.OCCUPIED:
|
|
if (
|
|
twodee.ORIGIN.neighbors()
|
|
.map((direction) => visibleNeighbor(pt, direction, input))
|
|
.filter((neighborState) => neighborState === STATE.OCCUPIED)
|
|
.length >= 5n
|
|
) {
|
|
changes++;
|
|
return [pts, STATE.EMPTY];
|
|
}
|
|
return [pts, STATE.OCCUPIED];
|
|
case STATE.FLOOR:
|
|
return [pts, STATE.FLOOR];
|
|
default:
|
|
console.log(pts, state);
|
|
throw new Error('invalid state');
|
|
}
|
|
})
|
|
.toMap();
|
|
|
|
return [output, changes];
|
|
};
|
|
|
|
const solve = (input, stepFn) => {
|
|
let state = Glib.fromLines(input)
|
|
.flatMap((line, y) =>
|
|
Glib.fromIterable(line).map((cell, x) => [new Vector(x, y).string, cell]),
|
|
)
|
|
.toMap();
|
|
|
|
// only run 1k iterations, it's a fairly simple automota
|
|
// I wonder it it's possible to make a glider...
|
|
for (let i = 0n; i < 1000n; i++) {
|
|
const [newState, changes] = stepFn(state);
|
|
if (changes === 0n) {
|
|
return state.glibValues.filter((e) => e === STATE.OCCUPIED).length;
|
|
}
|
|
state = newState;
|
|
}
|
|
throw new Error('too many iterations');
|
|
};
|
|
|
|
module.exports = {
|
|
1: (input) => solve(input, step1),
|
|
2: (input) => solve(input, step2),
|
|
};
|