Kaynağa Gözat

Solved day12a

Aleksei Dorokhov 7 ay önce
ebeveyn
işleme
b973ca5c35
4 değiştirilmiş dosya ile 213 ekleme ve 258 silme
  1. 158 0
      day12/areacache.zig
  2. 0 10
      day12/build.zig
  3. 55 102
      day12/day12a.zig
  4. 0 146
      day12/day12b.zig

+ 158 - 0
day12/areacache.zig

@@ -0,0 +1,158 @@
+const std = @import("std");
+
+const areaModule = @import("area.zig");
+
+const Area = areaModule.Area;
+
+pub const AreaHashContext = struct {
+    pub fn hash(ctx: AreaHashContext, key: Area) u64 {
+        _ = ctx;
+
+        var hasher = std.hash.Wyhash.init(0);
+        hasher.update(std.mem.asBytes(&key.rows));
+        hasher.update(std.mem.asBytes(&key.cols));
+        hasher.update(key.data);
+        return hasher.final();
+    }
+
+    pub fn eql(ctx: AreaHashContext, a: Area, b: Area) bool {
+        _ = ctx;
+
+        if (a.rows != b.rows) {
+            return false;
+        }
+
+        if (a.cols != b.cols) {
+            return false;
+        }
+
+        return std.mem.eql(u8, a.data, b.data);
+    }
+};
+
+const AreaCacheEntry = struct {
+    possibleShapeCounts: std.ArrayList([]i32) = std.ArrayList([]i32).empty,
+    impossibleShapeCounts: std.ArrayList([]i32) = std.ArrayList([]i32).empty,
+};
+
+const AreaHashMap = std.hash_map.HashMap(Area, AreaCacheEntry, AreaHashContext,
+                                         std.hash_map.default_max_load_percentage);
+
+pub const AreaCacheResult = enum {
+    kNotFound,
+    kPossible,
+    kImpossible
+};
+
+fn matchesPossible(shapeCounts: []i32, possibleShapeCounts: [][]i32) bool {
+    for (possibleShapeCounts) |possibleShapeCount| {
+        var isPossible = true;
+
+        for (0 .. shapeCounts.len) |i| {
+            if (shapeCounts[i] > possibleShapeCount[i]) {
+                isPossible = false;
+                break;
+            }
+        }
+
+        if (isPossible) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+fn matchesImpossible(shapeCounts: []i32, impossibleShapeCounts: [][]i32) bool {
+    for (impossibleShapeCounts) |impossibleShapeCount| {
+        var isImpossible = true;
+
+        for (0 .. shapeCounts.len) |i| {
+            if (shapeCounts[i] < impossibleShapeCount[i]) {
+                isImpossible = false;
+                break;
+            }
+        }
+
+        if (isImpossible) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+
+pub const AreaCache = struct {
+    childAllocator: std.mem.Allocator,
+    arenaAllocator: *std.heap.ArenaAllocator,
+    allocator: std.mem.Allocator,
+    context: AreaHashContext,
+    entries: AreaHashMap,
+    hitStat: u64,
+    totalStat: u64,
+
+
+
+    pub fn init(childAllocator: std.mem.Allocator) !AreaCache {
+        var areaCache: AreaCache = undefined;
+        areaCache.childAllocator = childAllocator;
+        areaCache.arenaAllocator = try childAllocator.create(std.heap.ArenaAllocator);
+        areaCache.arenaAllocator.* = std.heap.ArenaAllocator.init(childAllocator);
+        areaCache.allocator = areaCache.arenaAllocator.allocator();
+        areaCache.context = .{};
+        areaCache.entries = AreaHashMap.initContext(areaCache.allocator, areaCache.context);
+        areaCache.hitStat = 0;
+        areaCache.totalStat = 0;
+
+        return areaCache;
+    }
+
+    pub fn deinit(self: *const AreaCache) void {
+        self.arenaAllocator.deinit();
+        self.childAllocator.destroy(self.arenaAllocator);
+    }
+
+    pub fn addPossibleCacheEntry(self: *AreaCache, area: *const Area, shapeCounts: []i32) !void {
+        var result = try self.entries.getOrPut(area.*);
+        if (!result.found_existing) {
+            result.key_ptr.data = try self.allocator.alloc(u8, area.data.len);
+            std.mem.copyForwards(u8, result.key_ptr.data, area.data);
+            result.value_ptr.* = .{};
+        }
+
+        const newShapeCounts = try result.value_ptr.possibleShapeCounts.addOne(self.allocator);
+        newShapeCounts.* = try self.allocator.alloc(i32, shapeCounts.len);
+        std.mem.copyForwards(i32, newShapeCounts.*, shapeCounts);
+    }
+
+    pub fn addImpossibleCacheEntry(self: *AreaCache, area: *const Area, shapeCounts: []i32) !void {
+        var result = try self.entries.getOrPut(area.*);
+        if (!result.found_existing) {
+            result.key_ptr.data = try self.allocator.alloc(u8, area.data.len);
+            std.mem.copyForwards(u8, result.key_ptr.data, area.data);
+            result.value_ptr.* = .{};
+        }
+
+        const newShapeCounts = try result.value_ptr.impossibleShapeCounts.addOne(self.allocator);
+        newShapeCounts.* = try self.allocator.alloc(i32, shapeCounts.len);
+        std.mem.copyForwards(i32, newShapeCounts.*, shapeCounts);
+    }
+
+    pub fn check(self: *AreaCache, area: *const Area, shapeCounts: []i32) AreaCacheResult {
+        self.totalStat += 1;
+        if (self.entries.get(area.*)) |cacheEntry| {
+            if (matchesPossible(shapeCounts, cacheEntry.possibleShapeCounts.items)) {
+                self.hitStat += 1;
+                return AreaCacheResult.kPossible;
+            }
+
+            if (matchesImpossible(shapeCounts, cacheEntry.impossibleShapeCounts.items)) {
+                self.hitStat += 1;
+                return AreaCacheResult.kImpossible;
+            }
+        }
+
+        return AreaCacheResult.kNotFound;
+    }
+};

+ 0 - 10
day12/build.zig

@@ -10,14 +10,4 @@ pub fn build(b: *std.Build) void {
     });
 
     b.installArtifact(exe1);
-
-    const exe2 = b.addExecutable(.{
-        .name = "day12b",
-        .root_module = b.createModule(.{
-            .root_source_file = b.path("day12b.zig"),
-            .target = b.graph.host,
-        }),
-    });
-    
-    b.installArtifact(exe2);
 }

+ 55 - 102
day12/day12a.zig

@@ -5,51 +5,21 @@ const inputModule = @import("input.zig");
 const areaModule = @import("area.zig");
 const coordModule = @import("coord.zig");
 const unionFindModule = @import("unionfind.zig");
+const areacacheModule = @import("areacache.zig");
 
 const Shape = shapeModule.Shape;
 const Area = areaModule.Area;
 const Coord = coordModule.Coord;
 const UnionFind = unionFindModule.UnionFind;
+const AreaCache = areacacheModule.AreaCache;
+const AreaCacheResult = areacacheModule.AreaCacheResult;
+const AreaHashContext = areacacheModule.AreaHashContext;
+
 
 const kShapeSize = shapeModule.kShapeSize;
 const readInputIntoString = inputModule.readInputIntoString;
 const parseInput = inputModule.parseInput;
 
-pub fn getEligibleCoords(allocator: std.mem.Allocator,
-                         area: *const Area,
-                         eligibleCoords: *std.ArrayList(Coord)) !void {
-    eligibleCoords.clearRetainingCapacity();
-
-    for (0 .. @intCast(area.rows)) |row| {
-        for (0 .. @intCast(area.cols)) |col|  {
-            if (area.getCell(@intCast(row), @intCast(col)) == '.') {
-                var shouldAdd = false;
-                if (row > 0 and area.getCell(@intCast(row-1), @intCast(col)) != '.') {
-                    shouldAdd = true;
-                } else if (col > 0 and area.getCell(@intCast(row), @intCast(col-1)) != '.') {
-                    shouldAdd = true;
-                } else if (row < area.rows - 1 and area.getCell(@intCast(row+1), @intCast(col)) != '.') {
-                    shouldAdd = true;
-                } else if (col < area.cols - 1 and area.getCell(@intCast(row), @intCast(col+1)) != '.') {
-                    shouldAdd = true;
-                }
-
-                if (shouldAdd) {
-                    var newEligibleCoord = try eligibleCoords.addOne(allocator);
-                    newEligibleCoord.row = @intCast(row);
-                    newEligibleCoord.col = @intCast(col);
-                }
-            }
-        }
-    }
-
-    if (eligibleCoords.items.len == 0 and area.getCell(0, 0) == '.') {
-        var newEligibleCoord = try eligibleCoords.addOne(allocator);
-        newEligibleCoord.row = 0;
-        newEligibleCoord.col = 0;
-    }
-}
-
 pub fn calculateShapeSizes(allocator: std.mem.Allocator, shapeTransforms: []const []const Shape) ![]usize{
     var shapeSizes = try allocator.alloc(usize, shapeTransforms.len);
     for (0..shapeTransforms.len) |i| {
@@ -75,21 +45,20 @@ pub fn calcShapesTotalSize(shapeCounts: []i32, shapeSizes: []usize) usize {
 
 pub fn canContainShapes(allocator: std.mem.Allocator, area: *const Area,
                         shapeCounts: []i32, shapeTransforms: []const []const Shape,
-                        shapeSizes: []usize,
-                        minShapeSize: usize) !bool {
-    // try std.fs.File.stdout().writeAll("canContainShapes: [");
-    // for (shapeCounts) |shapeCount| {
-    //     try std.fs.File.stdout().writeAll(
-    //       try std.fmt.allocPrint(allocator, " {d} ", .{shapeCount}));
-    // }
-    // try std.fs.File.stdout().writeAll("]\n");
+                        shapeSizes: []usize, minShapeSize: usize,
+                        areaCache: *AreaCache) !bool {
 
-    if (calcShapesTotalSize(shapeCounts, shapeSizes) > area.totalFreeSize()) {
+    const cacheCheckResult = areaCache.check(area, shapeCounts);
+    if (cacheCheckResult == AreaCacheResult.kPossible) {
+        return true;
+    } else if (cacheCheckResult == AreaCacheResult.kPossible) {
         return false;
     }
 
-    var eligibleCoords = std.ArrayList(Coord).empty;
-    defer eligibleCoords.deinit(allocator);
+    if (calcShapesTotalSize(shapeCounts, shapeSizes) > area.totalFreeSize()) {
+        try areaCache.addImpossibleCacheEntry(area, shapeCounts);
+        return false;
+    }
     
     var allEmpty = true;
     for (shapeCounts) |shapeCount| {
@@ -99,7 +68,6 @@ pub fn canContainShapes(allocator: std.mem.Allocator, area: *const Area,
         }
     }
     if (allEmpty) {
-        try area.print(allocator);
         return true;
     }
 
@@ -109,55 +77,48 @@ pub fn canContainShapes(allocator: std.mem.Allocator, area: *const Area,
         }
 
         for (0..shapeTransforms[shapeIndex].len) |shapeVariant| {
-            try getEligibleCoords(allocator, area, &eligibleCoords);
-
-            for (eligibleCoords.items) |coord| {
-                const row = coord.row;
-                const col = coord.col;
-
-                if (try area.appendShape(
-                      allocator,
-                      &shapeTransforms[shapeIndex][shapeVariant],
-                      @intCast(row), @intCast(col), '#')) |*newArea| {
-                    defer newArea.deinit();
-
-                    // try std.fs.File.stdout().writeAll("Before union find: \n");
-                    // try newArea.print(allocator);
-                    // try std.fs.File.stdout().writeAll("\n");
-
-                    var mutableArea: Area = newArea.*;
-                    var unionFind = try UnionFind.init(allocator, &mutableArea);
-                    for (0 .. @intCast(mutableArea.rows)) |unionRow| {
-                        for (0 .. @intCast(mutableArea.cols)) |unionCol| {
-                            if (mutableArea.getCell(@intCast(unionRow), @intCast(unionCol)) == '.') {
-                                const nodeAIndex:usize = @intCast(unionRow*@abs(mutableArea.cols) + unionCol);
-                                const nodeARootIndex = unionFind.Find(nodeAIndex);
-                                if (unionFind.nodes[nodeARootIndex].size < minShapeSize) {
-                                    mutableArea.setCell(@intCast(unionRow), @intCast(unionCol), '#');
+            for (0 .. @intCast(area.rows)) |row| {
+                for (0 .. @intCast(area.cols)) |col|  {
+                    if (try area.appendShape(
+                          allocator,
+                          &shapeTransforms[shapeIndex][shapeVariant],
+                          @intCast(row), @intCast(col), '#')) |*newArea| {
+                        defer newArea.deinit();
+                    
+                        var mutableArea: Area = newArea.*;
+                        var unionFind = try UnionFind.init(allocator, &mutableArea);
+                        for (0 .. @intCast(mutableArea.rows)) |unionRow| {
+                            for (0 .. @intCast(mutableArea.cols)) |unionCol| {
+                                if (mutableArea.getCell(@intCast(unionRow), @intCast(unionCol)) == '.') {
+                                    const nodeAIndex:usize = @intCast(unionRow*@abs(mutableArea.cols) + unionCol);
+                                    const nodeARootIndex = unionFind.Find(nodeAIndex);
+                                    if (unionFind.nodes[nodeARootIndex].size < minShapeSize) {
+                                        mutableArea.setCell(@intCast(unionRow), @intCast(unionCol), '#');
+                                    }
                                 }
                             }
                         }
-                    }
-                    unionFind.deinit();
-
-                    // try std.fs.File.stdout().writeAll("After union find: \n");
-                    // try newArea.print(allocator);
-                    // try std.fs.File.stdout().writeAll("\n");
-
-                    var newShapeCounts = try allocator.alloc(i32, shapeCounts.len);
-                    defer allocator.free(newShapeCounts);
-                    std.mem.copyForwards(i32, newShapeCounts, shapeCounts);
-                    newShapeCounts[shapeIndex] -= 1;
-
-                    if (try canContainShapes(allocator, newArea, newShapeCounts,
-                                             shapeTransforms, shapeSizes, minShapeSize)) {
-                        return true;
+                        unionFind.deinit();
+                    
+                    
+                        var newShapeCounts = try allocator.alloc(i32, shapeCounts.len);
+                        defer allocator.free(newShapeCounts);
+                        std.mem.copyForwards(i32, newShapeCounts, shapeCounts);
+                        newShapeCounts[shapeIndex] -= 1;
+                    
+                        if (try canContainShapes(allocator, newArea, newShapeCounts,
+                                                 shapeTransforms, shapeSizes, minShapeSize,
+                                                 areaCache)) {
+                            try areaCache.addPossibleCacheEntry(area, newShapeCounts);
+                            return true;
+                        }
                     }
                 }
             }
         }
     }
 
+    try areaCache.addImpossibleCacheEntry(area, shapeCounts);
     return false;
 }
 
@@ -167,16 +128,6 @@ pub fn main() !void {
 
     const inputBuffer = try readInputIntoString(allocator, "input.txt");
     const input = try parseInput(allocator, inputBuffer);
-    
-    for (input.areas) |area| {
-         try std.fs.File.stdout().writeAll(
-           try std.fmt.allocPrint(allocator, "area: rows={d}, cols={d}, shapes=[ ", .{area.rows, area.cols}));
-         for (area.shapesCount) |shapeCount| {
-             try std.fs.File.stdout().writeAll(
-               try std.fmt.allocPrint(allocator, "{d}, ", .{shapeCount}));
-         }
-         try std.fs.File.stdout().writeAll("]\n");
-    }
 
     var shapeTransforms: [][]Shape = try allocator.alloc([]Shape, input.shapes.len);
     for (0..input.shapes.len) |i| {
@@ -203,18 +154,20 @@ pub fn main() !void {
         }
     }
 
+    var areaCache = try AreaCache.init(allocator);
+    defer areaCache.deinit();
+
     var counter: usize = 0;
     for (0..input.areas.len) |i| {
         const area = try Area.init(allocator, input.areas[i].rows, input.areas[i].cols);
         defer area.deinit();
         
         if(try canContainShapes(allocator, &area, input.areas[i].shapesCount,
-                                shapeTransforms, shapeSizes, minShapeSize)) {
-            try std.fs.File.stdout().writeAll("Can contain shapes!\n");
+                                shapeTransforms, shapeSizes, minShapeSize, &areaCache)) {
             counter += 1;
-        }
-        else {
-            try std.fs.File.stdout().writeAll("Can NOT contain shapes!\n");
+            try std.fs.File.stdout().writeAll("can contain shapes\n");
+        } else {
+            try std.fs.File.stdout().writeAll("CAN't contain shapes\n");
         }
     }
 

+ 0 - 146
day12/day12b.zig

@@ -1,146 +0,0 @@
-const std = @import("std");
-
-var printBuffer: [1024]u8 = undefined;
-var allocatorBuffer: [100*1024*1024]u8 = undefined;
-
-const Junction = struct {
-    x: i64 = 0,
-    y: i64 = 0,
-    z: i64 = 0,
-};
-
-const JunctionPair = struct {
-    a: usize = 0,
-    b: usize = 0,
-
-    pub fn distanceSquared(self: JunctionPair, junctions: []Junction) i64 {
-        const ja = &junctions[self.a];
-        const jb = &junctions[self.b];
-        const dx = ja.x - jb.x;
-        const dy = ja.y - jb.y;
-        const dz = ja.z - jb.z;
-        return  dx * dx + dy * dy + dz * dz;
-    }
-};
-
-const JunctionIdNode = struct {
-    id: usize = 0,
-    node: std.DoublyLinkedList.Node = .{},
-};
-
-const Circuit = struct {
-    junctions: std.DoublyLinkedList = .{},
-    node: std.DoublyLinkedList.Node = .{},
-};
-
-fn junctionPairCompareLT(junctions: []Junction, left: JunctionPair, right: JunctionPair) bool {
-    return left.distanceSquared(junctions) < right.distanceSquared(junctions);
-}
-
-const Error = error {
-    JunctionIdNotFound,
-};
-
-fn findCircuitForJunction(circuits: std.DoublyLinkedList, junctionId: usize) !*std.DoublyLinkedList.Node {
-    var circuitIter = circuits.first;
-    while (circuitIter) |circuitNode| : (circuitIter = circuitNode.next) {
-        const circuit: *Circuit = @fieldParentPtr("node", circuitNode);
-
-        var found = false;
-        var junctionIdIter = circuit.junctions.first;
-        while (junctionIdIter) |node| : (junctionIdIter = node.next) {
-            const junctionIdNode: *JunctionIdNode = @fieldParentPtr("node", node);
-            if (junctionIdNode.id == junctionId) {
-                found = true;
-                break;
-            }
-        }
-
-        if (found) {
-            return circuitNode;
-        }
-    }
-
-    return Error.JunctionIdNotFound;
-}
-
-pub fn main() !void {
-    var fb_alloc = std.heap.FixedBufferAllocator.init(&allocatorBuffer);
-    var allocator = fb_alloc.allocator();
-
-    const inputFile = try std.fs.cwd().openFile(
-        "input.txt",
-        .{
-            .mode = .read_only,
-        },
-    );
-    defer inputFile.close();
-
-    var inputFileBuffer: [1024]u8 = undefined;
-    var inputFileReader = inputFile.reader(&inputFileBuffer);
-    const inputFileSize = try inputFileReader.getSize();
-
-    const inputBuffer = try allocator.alloc(u8, inputFileSize);
-    try inputFileReader.interface.readSliceAll(inputBuffer);
-
-    var junctions = std.ArrayList(Junction).empty;
-
-    var i = std.mem.tokenizeScalar(u8, inputBuffer, '\n');
-    while (i.next()) |newJunctionText| {
-        var j = std.mem.tokenizeAny(u8, newJunctionText, ",\n");
-
-        var newJunction = try junctions.addOne(allocator);
-        newJunction.x = try std.fmt.parseInt(i64, j.next().?, 10);
-        newJunction.y = try std.fmt.parseInt(i64, j.next().?, 10);
-        newJunction.z = try std.fmt.parseInt(i64, j.next().?, 10);
-    }
-
-    var junctionPairs = std.ArrayList(JunctionPair).empty;
-    for (0..junctions.items.len) |a| {
-        for (a+1..junctions.items.len) |b| {
-            var newJunctionPair = try junctionPairs.addOne(allocator);
-            newJunctionPair.a = a;
-            newJunctionPair.b = b;
-        }
-    }
-    std.sort.block(JunctionPair, junctionPairs.items, junctions.items, junctionPairCompareLT);
-
-    var junctionIdNodeStorage = try std.ArrayList(JunctionIdNode).initCapacity(allocator, junctions.items.len);
-    var circuitsStorage = try std.ArrayList(Circuit).initCapacity(allocator, junctions.items.len);
-    var circuits: std.DoublyLinkedList = .{};
-    for (0..junctions.items.len) |a| {
-        var newJunctionIdNode = try junctionIdNodeStorage.addOne(allocator);
-        newJunctionIdNode.id = a;
-
-        var newCircuit = try circuitsStorage.addOne(allocator);
-        newCircuit.junctions = .{};
-        newCircuit.junctions.append(&newJunctionIdNode.node);
-        circuits.append(&newCircuit.node);
-    }
-
-    var circuitsCount:usize = junctions.items.len;
-    for (0..junctionPairs.items.len) |wire| {
-        const a = junctionPairs.items[wire].a;
-        const b = junctionPairs.items[wire].b;
-
-        const circuitANode = try findCircuitForJunction(circuits, a);
-        const circuitBNode = try findCircuitForJunction(circuits, b);
-        if (circuitANode == circuitBNode) {
-            continue;
-        }
-
-        const circuitA: *Circuit = @fieldParentPtr("node", circuitANode);
-        const circuitB: *Circuit = @fieldParentPtr("node", circuitBNode);
-
-        circuitA.junctions.concatByMoving(&circuitB.junctions);
-        circuits.remove(circuitBNode);
-
-        circuitsCount -= 1;
-        if (circuitsCount == 1) {
-            try std.fs.File.stdout().writeAll(
-              try std.fmt.bufPrint(&printBuffer, "{}\n", .{junctions.items[a].x*junctions.items[b].x}));
-            break;
-        }
-    }
-}
-