|
| 1 | +let mirror = t => t.map(line => line.split("").reverse().join("")); |
| 2 | +let rotate = t => mirror(t.map((_, i) => t.map(x => x[i]).join(""))); |
| 3 | + |
| 4 | +function allRotations(image) { |
| 5 | + let rotations = [ |
| 6 | + image, |
| 7 | + rotate(image), |
| 8 | + rotate(rotate(image)), |
| 9 | + rotate(rotate(rotate(image))), |
| 10 | + ].flatMap(x => [x, mirror(x)]); |
| 11 | + let unique = rotations |
| 12 | + .map(r => r.join("\n")) |
| 13 | + .filter((v, i, a) => a.indexOf(v) === i) |
| 14 | + .map(r => r.split("\n")); |
| 15 | + return unique; |
| 16 | +} |
| 17 | + |
| 18 | +function place(space, shape, x, y) { |
| 19 | + let newSpace = space.slice(0); |
| 20 | + for (let sy = 0; sy < shape.length; sy++) { |
| 21 | + for (let sx = 0; sx < shape[0].length; sx++) { |
| 22 | + if (shape[sy][sx] !== "#") continue; |
| 23 | + if (newSpace[y + sy][x + sx] === "#") return null; |
| 24 | + let s = newSpace[y + sy].split(""); |
| 25 | + s[x + sx] = "#"; |
| 26 | + newSpace[y + sy] = s.join(""); |
| 27 | + } |
| 28 | + } |
| 29 | + return newSpace; |
| 30 | +} |
| 31 | + |
| 32 | +function solvable(area) { |
| 33 | + let count = 0; |
| 34 | + if (area.shapes.length === 0) return true; |
| 35 | + let shape = area.shapes[0]; |
| 36 | + let rotations = allRotations(shape); |
| 37 | + for (let rotation of rotations) { |
| 38 | + for (let y = 0; y <= area.space.length - rotation.length; y++) { |
| 39 | + for (let x = 0; x <= area.space[0].length - rotation[0].length; x++) { |
| 40 | + let space = place(area.space, rotation, x, y); |
| 41 | + if (!space) continue; |
| 42 | + if (++count > 20) return false; |
| 43 | + if (solvable({ space, shapes: area.shapes.slice(1) })) { |
| 44 | + return true; |
| 45 | + } |
| 46 | + } |
| 47 | + } |
| 48 | + } |
| 49 | + return false; |
| 50 | +} |
| 51 | + |
| 52 | +function check(area) { |
| 53 | + let { shapes } = area; |
| 54 | + shapes = shapes.map(shape => shape.join("").matchAll(/#/g).toArray().length); |
| 55 | + let space = shapes.reduce((sum, n) => sum + n, 0); |
| 56 | + let remaining = area.width * area.height - space; |
| 57 | + if (remaining < 0) return false; |
| 58 | + if (remaining > 400) return true; // can safely assume it's solvable with lots of space remaining |
| 59 | + return solvable(area); |
| 60 | +} |
| 61 | + |
1 | 62 | export function part1(input) { |
2 | 63 | let shapes = input.split("\n\n"); |
3 | 64 | let areas = shapes.pop().split("\n"); |
4 | | - shapes = shapes.map(shape => shape.matchAll(/#/g).toArray().length); |
5 | | - areas = areas.filter(area => { |
| 65 | + shapes = shapes.map(shape => shape.split("\n").slice(1)); |
| 66 | + areas = areas.map(area => { |
6 | 67 | let [size, ...indices] = area.split(" "); |
7 | 68 | size = size.replace(":", "").split("x").map(Number); |
8 | | - let space = indices.reduce((sum, n, i) => sum + shapes[i] * +n, 0); |
9 | | - let remaining = size[0] * size[1] - space; |
10 | | - return remaining > 0 && remaining !== 11; // cheating to make the example pass |
| 69 | + return { |
| 70 | + width: size[0], |
| 71 | + height: size[1], |
| 72 | + space: new Array(size[1]) |
| 73 | + .fill(0) |
| 74 | + .map(() => new Array(size[0]).fill(".").join("")), |
| 75 | + shapes: indices.flatMap((n, i) => { |
| 76 | + if (n === "0") return []; |
| 77 | + return Array(Number(n)).fill(shapes[+i]); |
| 78 | + }), |
| 79 | + }; |
11 | 80 | }); |
12 | | - return areas.length; |
| 81 | + return areas.filter(check).length; |
13 | 82 | } |
14 | 83 |
|
15 | 84 | export function part2() { |
|
0 commit comments