diff --git a/inputs/10/real.txt b/inputs/10/real.txt new file mode 100644 index 0000000..dbdbad8 --- /dev/null +++ b/inputs/10/real.txt @@ -0,0 +1,55 @@ +0123450128945212434498107654876212322345432110787870123 +1214563287654302345347278923945003411276741025696983254 +2307874390901201436256361010232156500985891234899874765 +3458965481896543457101454322121267898764210348760565856 +4547889332787612898654456541010128709654321409651487943 +9656971245610502348742363456723489214545670512342396512 +8746560330541461059231072019894876523039987651229603401 +0130450101232878762108981923785955432128896560318712101 +1221321432543969456676870831276843201017698431105657892 +4349898540158950387985106740189652122012587120234598761 +0456797643267341293234203651076561043403436011231034650 +1245687653212210982178312312345878654312345190345125141 +4334567064307807891089425405658969763233438987656776032 +5321018165616906702376596534787439890120107678949889120 +6910789278965215610345687321096521763011234589030672101 +7821898347654334321256786543212310652101347654121543432 +6734787658901223456105897610105432543276978903443430563 +2105676987911010897834978923076501034789877412352321694 +3456989876854323708929870134589632385676966543261430785 +4367810105763432612210165245678745690123457898170567014 +3210121234176501523878954354776544787430342347089698923 +2345665893080787438965410167889432156561231456798789654 +1058756702191898341014321054974321047892120321887018763 +0569845212012567634323015123125410430121011010986323454 +6578954307623498765487654304034543221030345654345432125 +5434356788545349854599001216787652107845210787216701034 +0125643699237898703678104325894567856956789891209834345 +7876212765103203612363215454383898945787698900340125876 +0980101894454114503054356965212432430694543215489876965 +1098234583467023212125407870106541021583210676098920145 +2347899602898908763256910187017865652678701587187813236 +3256678711743219854567823298894976569549652490296704367 +0100345620651278343289654350765987478230743321345410198 +9251201234230341230178760541034300300121890120034326789 +8349212945145650789078921632133211212010581631128965632 +7658767876054787632107634780124504321123498745489874541 +6107323945763096549616543995435665210898589654788103450 +5236014539892124328723012876343786789867670123694012367 +4345867622101015610654322301212891270184561054543231018 +2109988913412126789961001454309750301293432163210102309 +3458776804569234697872156545678543432789430673456943212 +4567566543678985586543267836787612545676521982987856103 +0103457012987876487434586927898707654568701201276547894 +1212388967876567393325698810989898965439632320567030985 +0327890658905058212016784543298781012344543011498121076 +9456541243014149801134569650185632307655676322399876125 +8767632332123232100123678745670546998764985431087565436 +3498234501104343034598988764321457884643891056016501098 +2567107698612352125667639058901210745012342347121432167 +1989278786783961012787540147654323654321435218930345236 +0876989695894878109896039236569456788760324306543210145 +0105874504185769854385128545478998699354413457850105256 +1234763213096854763014537654360187543263509766969876567 +2303452342187943212323456963201236984102678876878103498 +3212301056789810103410567870102345676101278987989012567 diff --git a/inputs/10/test.txt b/inputs/10/test.txt new file mode 100644 index 0000000..cada9b3 --- /dev/null +++ b/inputs/10/test.txt @@ -0,0 +1,8 @@ +89010123 +78121874 +87430965 +96549874 +45678903 +32019012 +01329801 +10456732 diff --git a/scratch.zig b/scratch.zig index 8e092c5..a08f869 100644 --- a/scratch.zig +++ b/scratch.zig @@ -3,24 +3,29 @@ const print = std.debug.print; const expect = @import("std").testing.expect; -test "Len of an iterator is not the same as size" { +test ".toOwnedSlice does not seem to make deinit unnecessary" { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); - var map = std.AutoHashMap(u8, u8).init(allocator); - try map.put('a', 'b'); - try map.put('c', 'd'); - try map.put('e', 'f'); + var listOfLists = std.ArrayList([]u32).init(allocator); + try listOfLists.append(try buildAList(0, allocator)); + try listOfLists.append(try buildAList(1, allocator)); - var keyIterator = map.keyIterator(); - const length = keyIterator.len; - var counted_length: usize = 0; - while (keyIterator.next()) |_| { - counted_length += 1; + const outer_slice = try listOfLists.toOwnedSlice(); + print("{any}\n", .{outer_slice}); + for (outer_slice) |inner_slice| { + allocator.free(inner_slice); } - print("DEBUG - length is {} and counted_length is {}\n", .{ length, counted_length }); - try expect(length == counted_length); + allocator.free(outer_slice); +} + +fn buildAList(val: u32, allocator: std.mem.Allocator) ![]u32 { + var list = std.ArrayList(u32).init(allocator); + + try list.append(val); + + return list.toOwnedSlice(); } pub fn main() !void { diff --git a/solutions/09.zig b/solutions/09.zig index beaed93..67f81fd 100644 --- a/solutions/09.zig +++ b/solutions/09.zig @@ -184,11 +184,11 @@ fn hasSpaceToMove(disk: std.ArrayList(?u32), pointer: usize, file_size: usize) b const expect = std.testing.expect; -// test "part_one" { -// const part_one_response = try part_one(true); -// print("DEBUG - part_one_response is {}\n", .{part_one_response}); -// try expect(part_one_response == 1928); -// } +test "part_one" { + const part_one_response = try part_one(true); + print("DEBUG - part_one_response is {}\n", .{part_one_response}); + try expect(part_one_response == 1928); +} test "part_two" { const part_two_response = try part_two(true); diff --git a/solutions/10.zig b/solutions/10.zig new file mode 100644 index 0000000..e88078c --- /dev/null +++ b/solutions/10.zig @@ -0,0 +1,140 @@ +const std = @import("std"); +const print = std.debug.print; +const util = @import("util.zig"); + +pub fn main() !void { + const response = try part_one(false); + print("{}\n", .{response}); +} + +const Location = struct { + x: usize, + y: usize, + // The amount of casting in this is astonishing. Surely there must be a better way to do this? + fn newLocation(start: Location, x_move: i32, y_move: i32, width: usize, height: usize) ?Location { + const start_x_as_i32: i32 = @intCast(start.x); + const new_x = start_x_as_i32 + x_move; + const start_y_as_i32: i32 = @intCast(start.y); + const new_y = start_y_as_i32 + y_move; + + if (new_x < 0 or new_x >= width or new_y < 0 or new_y >= height) { + return null; + } + return Location{ .x = @intCast(new_x), .y = @intCast(new_y) }; + } +}; + +fn part_one(is_test_case: bool) anyerror!u64 { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + const input_file = try util.getInputFile("10", is_test_case); + const data = try util.readAllInputWithAllocator(input_file, allocator); + defer allocator.free(data); + + const grid = try buildGrid(data, allocator); + defer allocator.free(grid); + print("{any}\n", .{grid}); + + // If we wanted, we could find trailheads during the `buildGrid` iteration, but given the small data sizes I'd much + // rather keep small focused functions at the cost of some constant-factor performance. + var total: u32 = 0; + var i: usize = 0; + while (i < grid.len) : (i += 1) { + var j: usize = 0; + while (j < grid[0].len) : (j += 1) { + if (grid[i][j] == 0) { + var so_far = std.AutoHashMap(Location, bool).init(allocator); + const err = getReachablePeaks(grid, Location{ .x = j, .y = i }, 0, &so_far, allocator); + if (err != null) { + return err.?; + } + const score = so_far.count(); + print("DEBUG - for the trailhead at {}/{}, found a trailscore of {}\n", .{ j, i, score }); + total += score; + so_far.deinit(); + } + } + } + + // Wow I do _not_ like memory management + for (grid) |line| { + allocator.free(line); + } + + return total; +} + +fn buildGrid(data: []const u8, alloc: std.mem.Allocator) ![][]u32 { + var lines = std.ArrayList([]u32).init(alloc); + defer lines.deinit(); + + var current_line = std.ArrayList(u32).init(alloc); + defer current_line.deinit(); + + for (data) |c| { + if (c == '\n') { + const slice = try current_line.toOwnedSlice(); + try lines.append(slice); + } else { + try current_line.append(c - 48); + } + } + return try lines.toOwnedSlice(); +} + +fn buildNeighbours(grid: [][]u32, location: Location, alloc: std.mem.Allocator) ![]Location { + var neighbours = std.ArrayList(Location).init(alloc); + defer neighbours.deinit(); + + const up = Location.newLocation(location, 0, -1, grid[0].len, grid.len); + if (up != null) { + try neighbours.append(up.?); + } + + const right = Location.newLocation(location, 1, 0, grid[0].len, grid.len); + if (right != null) { + try neighbours.append(right.?); + } + + const down = Location.newLocation(location, 0, 1, grid[0].len, grid.len); + if (down != null) { + try neighbours.append(down.?); + } + + const left = Location.newLocation(location, -1, 0, grid[0].len, grid.len); + if (left != null) { + try neighbours.append(left.?); + } + + return neighbours.toOwnedSlice(); +} + +fn getReachablePeaks(grid: [][]u32, start: Location, start_value: u32, so_far: *std.AutoHashMap(Location, bool), alloc: std.mem.Allocator) ?anyerror { + if (start_value == 9) { + try so_far.put(start, true); + return null; + } + + // Check up, down, left, right - if they have the right next value, iterate from there + const neighbours = try buildNeighbours(grid, start, alloc); + for (neighbours) |neighbour| { + if (grid[neighbour.y][neighbour.x] == start_value + 1) { + const err = getReachablePeaks(grid, neighbour, start_value + 1, so_far, alloc); + if (err != null) { + return err; + } + } + } + alloc.free(neighbours); + return null; +} + +const expect = std.testing.expect; + +test "part_one" { + const part_one_response = try part_one(true); + print("DEBUG - part_one_response is {}\n", .{part_one_response}); + try expect(part_one_response == 36); +}