diff --git a/source/day17.d b/source/day17.d index 8cec3de..59aadff 100644 --- a/source/day17.d +++ b/source/day17.d @@ -1,38 +1,68 @@ +import std.format; + import dayutil; version(unittest) import fluent.asserts; -struct Point { - int x, y, z; +immutable char[26] letters = "xyzabcdefghijklmnopqrstuvw"; - int opCmp(ref const Point p) const { - return x - p.x + y - p.y + z - p.z; +struct Point(size_t dim) { + int[dim] elements; + + int opIndex(size_t index) { + return elements[index]; + } + + void opIndexAssign(size_t index, int value) { + elements[index] = value; + } + + mixin template element(string name, size_t index) { + mixin("@property int %s() const { return elements[%d]; }".format(name, index)); + mixin("@property void %s(int newValue) { elements[%d] = newValue; }".format(name, index)); + } + static foreach(i; 0..dim) { + mixin element!([letters[i]], i); + } + + this(T...)(T args) { + foreach(idx, arg; args) { + elements[idx] = arg; + } } } Variant run(int part, File input, bool bigboy, string[] args) { - bool[Point] parsedInput = parseInput(input.byLine); + string[] lines = input.byLineCopy.array; return Variant(parts!int(part, - () => part1(parsedInput))); + () { + bool[Point!3] parsedInput3 = parseInput!3(lines); + return part1(parsedInput3); + }, + () { + bool[Point!4] parsedInput4 = parseInput!4(lines); + return part2!4(parsedInput4); + })); } -bool[Point] parseInput(Range)(Range range) if (isInputRange!Range && isSomeString!(ElementType!Range)){ - bool[Point] result; +bool[Point!dim] parseInput(size_t dim, Range)(Range range) if (isInputRange!Range && isSomeString!(ElementType!Range) && dim >= 2){ + bool[Point!dim] result; foreach(y, line; range.enumerate) { foreach(x, c; line) { if (c == '#') { - result[Point(cast(int) x, cast(int) y, 0)] = true; + result[Point!dim(cast(int) x, cast(int) y)] = true; } } } return result; } -int part1(bool[Point] start) { +int part1(bool[Point!3] start){ + alias DPoint = Point!3; immutable int BOOT_LENGTH = 6; - bool[Point] prev = start.dup; + bool[DPoint] prev = start.dup; foreach(i; 0..BOOT_LENGTH) { - bool[Point] newState = prev.dup; + bool[DPoint] newState = prev.dup; assert(newState == prev); int minX, minY, minZ, maxX, maxY, maxZ; @@ -44,17 +74,17 @@ int part1(bool[Point] start) { if (e.y > maxY) maxY = e.y; if (e.z > maxZ) maxZ = e.z; } - Point min = Point(minX - 1, minY - 1, minZ - 1); - Point max = Point(maxX + 2, maxY + 2, maxZ + 2); + DPoint min = DPoint(minX - 1, minY - 1, minZ - 1); + DPoint max = DPoint(maxX + 2, maxY + 2, maxZ + 2); foreach(z; min.z..max.z) { - writeln("z=", z); + // debug writeln("z=", z); foreach(y; min.y..max.y) { foreach(x; min.x..max.x) { // Point - bool active = prev.get(Point(x,y,z), false); + bool active = prev.get(DPoint(x,y,z), false); - if (active) { debug write("\x1B[42m"); } + // debug if (active) { write("\x1B[42m"); } //write(active ? '#' : '.'); int neighboursActive = 0; @@ -62,41 +92,126 @@ int part1(bool[Point] start) { foreach(dy; y-1..y+2) { foreach(dz; z-1..z+2) { if (dx == x && dy == y && dz == z) continue; - if (prev.get(Point(dx, dy, dz), false)) neighboursActive++; + if (prev.get(DPoint(dx, dy, dz), false)) neighboursActive++; } } } - if (neighboursActive > 9) { - write("+"); - } else { - write(neighboursActive); - } + /+debug { + if (neighboursActive > 9) { + write("+"); + } else { + write(neighboursActive); + } + }+/ if (active && (neighboursActive < 2 || neighboursActive > 3)) { - newState[Point(x,y,z)] = false; + newState[DPoint(x,y,z)] = false; } else if (!active && neighboursActive == 3) { - newState[Point(x,y,z)] = true; + newState[DPoint(x,y,z)] = true; } - debug write("\x1B[0m"); + //debug write("\x1B[0m"); } - writeln(); + // debug writeln(); } - writeln(); + // debug writeln(); } assert(newState != prev); - writeln("---"); + // debug writeln("---"); prev = newState; } return cast(int) prev.byValue.count!((x) => x); } +string GenLoops(size_t dim)() { + string code = ""; + string xyz = ""; + string dxyz = ""; + static foreach(j; 0..dim) { + xyz ~= [letters[j]]; + dxyz ~= "d" ~ [letters[j]]; + + if (j != dim - 1) { + xyz ~= ", "; + dxyz ~= ", "; + } + } + + static foreach(j; 0..dim) { + + code ~= "foreach(%1$s; min[%2$s]..max[%2$s]) {\n".format(letters[j], j); + } + code ~= "bool active = prev.get(DPoint(" ~ xyz ~ "), false);\n"; + code ~= "int neighboursActive = 0;\n"; + + static foreach(j; 0..dim) { + code ~= "foreach(d%1$s; %1$s-1..%1$s+2) {\n".format(letters[j]); + } + + code ~= "if ("; + static foreach(j; 0..dim) { + code ~= "%1s == d%1$s".format(letters[j]); + if (j != dim - 1) code ~= " && "; + } + code ~= ") continue;\n"; + + code ~= "if (prev.get(DPoint(" ~ dxyz ~ "), false)) neighboursActive++;\n"; + static foreach(j; 0..dim) { + code ~= "}\n"; + } + + code ~= "if (active && (neighboursActive < 2 || neighboursActive > 3)) {"; + code ~= " newState[DPoint(" ~ xyz ~ ")] = false;"; + code ~= "} else if (!active && neighboursActive == 3) {"; + code ~= " newState[DPoint(" ~ xyz ~ ")] = true;"; + code ~= "}\n"; + + static foreach(j; 0..dim) { + code ~= "}\n"; + } + return code; +} + +int part2(size_t dim)(bool[Point!dim] start){ + alias DPoint = Point!dim; + immutable int BOOT_LENGTH = 6; + + bool[DPoint] prev = start.dup; + foreach(i; 0..BOOT_LENGTH) { + bool[DPoint] newState = prev.dup; + assert(newState == prev); + + int[dim] min; + int[dim] max; + foreach(e; prev.byKey) { + static foreach(j; 0..dim) { + if (e[j] < min[j]) min[j] = e[j]; + if (e[j] > max[j]) max[j] = e[j]; + } + } + static foreach(j; 0..dim) { + min[j] -= 1; + max[j] += 2; + } + + //writeln(GenLoops!(dim)); + mixin(GenLoops!(dim)); + + prev = newState; + } + return cast(int) prev.byValue.count!((x) => x); +} + + unittest { string[] input = [ ".#.", "..#", "###" ]; - bool[Point] parsedInput = parseInput(input); + bool[Point!3] parsedInput = parseInput!3(input); + bool[Point!4] parsedInput4 = parseInput!4(input); part1(parsedInput).should.equal(112); + part2!3(parsedInput).should.equal(112); + part2!4(parsedInput4).should.equal(848); }