day12a.zig 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. const std = @import("std");
  2. const shapeModule = @import("shape.zig");
  3. const inputModule = @import("input.zig");
  4. const areaModule = @import("area.zig");
  5. const coordModule = @import("coord.zig");
  6. const unionFindModule = @import("unionfind.zig");
  7. const Shape = shapeModule.Shape;
  8. const Area = areaModule.Area;
  9. const Coord = coordModule.Coord;
  10. const UnionFind = unionFindModule.UnionFind;
  11. const kShapeSize = shapeModule.kShapeSize;
  12. const readInputIntoString = inputModule.readInputIntoString;
  13. const parseInput = inputModule.parseInput;
  14. pub fn getEligibleCoords(allocator: std.mem.Allocator,
  15. area: *const Area,
  16. eligibleCoords: *std.ArrayList(Coord)) !void {
  17. eligibleCoords.clearRetainingCapacity();
  18. for (0 .. @intCast(area.rows)) |row| {
  19. for (0 .. @intCast(area.cols)) |col| {
  20. if (area.getCell(@intCast(row), @intCast(col)) == '.') {
  21. var shouldAdd = false;
  22. if (row > 0 and area.getCell(@intCast(row-1), @intCast(col)) != '.') {
  23. shouldAdd = true;
  24. } else if (col > 0 and area.getCell(@intCast(row), @intCast(col-1)) != '.') {
  25. shouldAdd = true;
  26. } else if (row < area.rows - 1 and area.getCell(@intCast(row+1), @intCast(col)) != '.') {
  27. shouldAdd = true;
  28. } else if (col < area.cols - 1 and area.getCell(@intCast(row), @intCast(col+1)) != '.') {
  29. shouldAdd = true;
  30. }
  31. if (shouldAdd) {
  32. var newEligibleCoord = try eligibleCoords.addOne(allocator);
  33. newEligibleCoord.row = @intCast(row);
  34. newEligibleCoord.col = @intCast(col);
  35. }
  36. }
  37. }
  38. }
  39. if (eligibleCoords.items.len == 0 and area.getCell(0, 0) == '.') {
  40. var newEligibleCoord = try eligibleCoords.addOne(allocator);
  41. newEligibleCoord.row = 0;
  42. newEligibleCoord.col = 0;
  43. }
  44. }
  45. pub fn calculateShapeSizes(allocator: std.mem.Allocator, shapeTransforms: []const []const Shape) ![]usize{
  46. var shapeSizes = try allocator.alloc(usize, shapeTransforms.len);
  47. for (0..shapeTransforms.len) |i| {
  48. shapeSizes[i] = 0;
  49. for (shapeTransforms[i][0].data) |d| {
  50. if (d != '.') {
  51. shapeSizes[i] += 1;
  52. }
  53. }
  54. }
  55. return shapeSizes;
  56. }
  57. pub fn calcShapesTotalSize(shapeCounts: []i32, shapeSizes: []usize) usize {
  58. var totalSize: usize = 0;
  59. for (0..shapeCounts.len) |i| {
  60. totalSize += shapeSizes[i] * @as(usize, @abs(shapeCounts[i]));
  61. }
  62. return totalSize;
  63. }
  64. pub fn canContainShapes(allocator: std.mem.Allocator, area: *const Area,
  65. shapeCounts: []i32, shapeTransforms: []const []const Shape,
  66. shapeSizes: []usize,
  67. minShapeSize: usize) !bool {
  68. // try std.fs.File.stdout().writeAll("canContainShapes: [");
  69. // for (shapeCounts) |shapeCount| {
  70. // try std.fs.File.stdout().writeAll(
  71. // try std.fmt.allocPrint(allocator, " {d} ", .{shapeCount}));
  72. // }
  73. // try std.fs.File.stdout().writeAll("]\n");
  74. if (calcShapesTotalSize(shapeCounts, shapeSizes) > area.totalFreeSize()) {
  75. return false;
  76. }
  77. var eligibleCoords = std.ArrayList(Coord).empty;
  78. defer eligibleCoords.deinit(allocator);
  79. var allEmpty = true;
  80. for (shapeCounts) |shapeCount| {
  81. if (shapeCount != 0) {
  82. allEmpty = false;
  83. break;
  84. }
  85. }
  86. if (allEmpty) {
  87. try area.print(allocator);
  88. return true;
  89. }
  90. for (0 .. shapeCounts.len) |shapeIndex| {
  91. if (shapeCounts[shapeIndex] == 0) {
  92. continue;
  93. }
  94. for (0..shapeTransforms[shapeIndex].len) |shapeVariant| {
  95. try getEligibleCoords(allocator, area, &eligibleCoords);
  96. for (eligibleCoords.items) |coord| {
  97. const row = coord.row;
  98. const col = coord.col;
  99. if (try area.appendShape(
  100. allocator,
  101. &shapeTransforms[shapeIndex][shapeVariant],
  102. @intCast(row), @intCast(col), '#')) |*newArea| {
  103. defer newArea.deinit();
  104. // try std.fs.File.stdout().writeAll("Before union find: \n");
  105. // try newArea.print(allocator);
  106. // try std.fs.File.stdout().writeAll("\n");
  107. var mutableArea: Area = newArea.*;
  108. var unionFind = try UnionFind.init(allocator, &mutableArea);
  109. for (0 .. @intCast(mutableArea.rows)) |unionRow| {
  110. for (0 .. @intCast(mutableArea.cols)) |unionCol| {
  111. if (mutableArea.getCell(@intCast(unionRow), @intCast(unionCol)) == '.') {
  112. const nodeAIndex:usize = @intCast(unionRow*@abs(mutableArea.cols) + unionCol);
  113. const nodeARootIndex = unionFind.Find(nodeAIndex);
  114. if (unionFind.nodes[nodeARootIndex].size < minShapeSize) {
  115. mutableArea.setCell(@intCast(unionRow), @intCast(unionCol), '#');
  116. }
  117. }
  118. }
  119. }
  120. unionFind.deinit();
  121. // try std.fs.File.stdout().writeAll("After union find: \n");
  122. // try newArea.print(allocator);
  123. // try std.fs.File.stdout().writeAll("\n");
  124. var newShapeCounts = try allocator.alloc(i32, shapeCounts.len);
  125. defer allocator.free(newShapeCounts);
  126. std.mem.copyForwards(i32, newShapeCounts, shapeCounts);
  127. newShapeCounts[shapeIndex] -= 1;
  128. if (try canContainShapes(allocator, newArea, newShapeCounts,
  129. shapeTransforms, shapeSizes, minShapeSize)) {
  130. return true;
  131. }
  132. }
  133. }
  134. }
  135. }
  136. return false;
  137. }
  138. pub fn main() !void {
  139. var gpa = std.heap.GeneralPurposeAllocator(.{}){};
  140. const allocator = gpa.allocator();
  141. const inputBuffer = try readInputIntoString(allocator, "input.txt");
  142. const input = try parseInput(allocator, inputBuffer);
  143. for (input.areas) |area| {
  144. try std.fs.File.stdout().writeAll(
  145. try std.fmt.allocPrint(allocator, "area: rows={d}, cols={d}, shapes=[ ", .{area.rows, area.cols}));
  146. for (area.shapesCount) |shapeCount| {
  147. try std.fs.File.stdout().writeAll(
  148. try std.fmt.allocPrint(allocator, "{d}, ", .{shapeCount}));
  149. }
  150. try std.fs.File.stdout().writeAll("]\n");
  151. }
  152. var shapeTransforms: [][]Shape = try allocator.alloc([]Shape, input.shapes.len);
  153. for (0..input.shapes.len) |i| {
  154. var allTransforms = try input.shapes[i].getAllTransforms(allocator);
  155. defer allTransforms.deinit();
  156. shapeTransforms[i] = try allocator.alloc(Shape, allTransforms.count());
  157. var j: usize = 0;
  158. var it = allTransforms.keyIterator();
  159. while (it.next()) |shapeTransform| {
  160. shapeTransforms[i][j] = shapeTransform.*;
  161. j += 1;
  162. }
  163. }
  164. const shapeSizes = try calculateShapeSizes(allocator, shapeTransforms);
  165. defer allocator.free(shapeSizes);
  166. var minShapeSize = shapeSizes[0];
  167. for (shapeSizes) |shapeSize| {
  168. if (shapeSize < minShapeSize) {
  169. minShapeSize = shapeSize;
  170. }
  171. }
  172. var counter: usize = 0;
  173. for (0..input.areas.len) |i| {
  174. const area = try Area.init(allocator, input.areas[i].rows, input.areas[i].cols);
  175. defer area.deinit();
  176. if(try canContainShapes(allocator, &area, input.areas[i].shapesCount,
  177. shapeTransforms, shapeSizes, minShapeSize)) {
  178. try std.fs.File.stdout().writeAll("Can contain shapes!\n");
  179. counter += 1;
  180. }
  181. else {
  182. try std.fs.File.stdout().writeAll("Can NOT contain shapes!\n");
  183. }
  184. }
  185. try std.fs.File.stdout().writeAll(
  186. try std.fmt.allocPrint(allocator, "Answer: {d}\n", .{counter}));
  187. }