Solution to 1-2

This commit is contained in:
Jack Jackson 2024-12-19 14:32:12 -08:00
parent 09ff76600a
commit aa408ad75b
5 changed files with 174 additions and 5 deletions

View File

@ -3,6 +3,8 @@ Notes, thoughts, or questions that arose as I implemented the solutions. Hopeful
# Things I like
* [Continue expressions](https://zig-by-example.com/while)
* Built in optionals (with `orelse`)
* Better error-handling than GoLang's (though that bar is set _real_ low). I have only just scratched the surface, though, it looks interestingly powerful - might well be even better than I've realized at this point!
# Things that I've found missing from this language
@ -10,6 +12,7 @@ I mean, Jesus Christ, right now it seems even worse than GoLang.
* [String concatenation](https://old.reddit.com/r/Zig/comments/bfcsul/concatenating_zig_strings/)
* [String equality](https://nofmal.github.io/zig-with-example/string-handling/#string-equal)
* Switching on strings
# Questions
@ -83,3 +86,41 @@ test {
I can fix it by changing the type signature to accept `[]const u8`, but (I think?) that then means that I can't call the function with non-const-length strings - including strings read from files.
[This](https://stackoverflow.com/questions/72736997/how-to-pass-a-c-string-into-a-zig-function-expecting-a-zig-string) link refers to `[]const u8` as a "Zig-style string-slice", but also refers to `[*c]const u8` as a "c_string", so...:shrug:?
## Why can't I iterate over a HashMap?
The following code:
```zig
const std = @import("std");
const print = std.debug.print;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var hashMap = std.AutoHashMap(u32, u32).init(allocator);
try hashMap.put(2, 5);
try hashMap.put(1, 35);
try hashMap.put(4, 20);
const iter = hashMap.keyIterator();
while (try iter.next()) |key| {
print("{}\n", .{key});
}
}
```
gives:
```
scratch.zig:15:20: error: expected type '*hash_map.HashMapUnmanaged(u32,u32,hash_map.AutoContext(u32),80).FieldIterator(u32)', found '*const hash_map.HashMapUnmanaged(u32,u32,hash_map.AutoContext(u32),80).FieldIterator(u32)'
while (try iter.next()) |key| {
~~~~^~~~~
scratch.zig:15:20: note: cast discards const qualifier
/Users/scubbo/zig/zig-macos-x86_64-0.14.0-dev.2362+a47aa9dd9/lib/std/hash_map.zig:894:35: note: parameter type declared here
pub fn next(self: *@This()) ?*T {
```
I _think_ this means that the pointer to the Iterator is a Const-pointer and `.next()` expects a mutable pointer. But, if so - how do we get a mutable pointer from a const? I tried `@ptrCast` but that gave a similar error.

View File

@ -3,3 +3,11 @@
If you - like me - are new to the Zig language, [Ziglings](https://codeberg.org/ziglings/exercises/) seems to be a well-respected entrypoint!
The authoritative source for this code is on [Gitea](https://gitea.scubbo.org/scubbo/advent-of-code-2024). The GitHub version is a [mirror](https://docs.gitea.com/usage/repo-mirror#setting-up-a-push-mirror-from-gitea-to-github). [Self-hosting](https://old.reddit.com/r/selfhosted/) is not only the best way to learn, but also to reduce dependency on untrustworthy corporations.
# Execution
I've tried (in `main.zig`) to make a general-purpose executable that can be passed arguments to determine the function to run (e.g. `zig run main.zig -- 1 2`), but so far no luck - lots of type errors 🙃
So for now, run directly with (e.g.) `zig run solutions/01.zig`, and do the following manual changes:
* Change `pub fn main() void {...}` in each solution-file to invoke the function you want run.
* Change `isTestCase` from `true` to `false` when ready to get the real solution.

48
main.zig Normal file
View File

@ -0,0 +1,48 @@
const std = @import("std");
// Unfortunately Zig doesn't appear to support dynamic imports - that is, non-literal arguments to `@import` -
// so we can't do `const module = @import(util.concatString(&.{"solutions/", problem_number, ".zig"}))`
const problem_one = @import("solutions/01.zig");
pub fn main() void {
// std.debug.print("There are {d} args:\n", .{std.os.argv.len});
// for (std.os.argv) |arg| {
// std.debug.print(" {s}\n", .{arg});
// }
const args = std.os.argv;
const problem_number = parse_input_as_number(args[1]);
const sub_problem_number = parse_input_as_number(args[2]);
const module = switch (problem_number) {
1 => {
return problem_one;
},
else => {
unreachable;
},
};
switch (sub_problem_number) {
1 => {
module.part_one();
},
2 => {
module.part_two();
},
else => {
unreachable;
},
}
}
fn parse_input_as_number(input: [*:0]u8) i32 {
// We _should_ be able to just use `try std.fmt.parseInt(i32, input)` - but that complains with
// `expected type '[]const u8', found '[*:0]u8'`
// and I CBA to figure out how to do that conversion when I have the time pressure of solving problems!
// That can be for later learning :P
var output_value: i32 = 0;
for (input) |char| {
output_value *= 10;
output_value + char;
}
return output_value;
}

18
scratch.zig Normal file
View File

@ -0,0 +1,18 @@
const std = @import("std");
const print = std.debug.print;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var hashMap = std.AutoHashMap(u32, u32).init(allocator);
try hashMap.put(2, 5);
try hashMap.put(1, 35);
try hashMap.put(4, 20);
const iter = hashMap.keyIterator();
while (try iter.next()) |key| {
print("{}\n", .{key});
}
}

View File

@ -2,9 +2,13 @@ const std = @import("std");
const print = std.debug.print;
const util = @import("util.zig");
pub fn main() !void {
const output = try part_two();
print("{}\n", .{output});
}
pub fn part_one() !u32 {
const arrays = try parseInputFileToArrays();
std.debug.print("{any}", .{arrays.first});
const arr1 = arrays.first;
const arr2 = arrays.second;
@ -30,7 +34,57 @@ pub fn part_one() !u32 {
}
pub fn part_two() !u32 {
return 0;
const arrays = try parseInputFileToArrays();
const arr1 = arrays.first;
const arr2 = arrays.second;
print("DEBUG - arr1 is {any}\narr2 is {any}\n", .{arr1, arr2});
// Logic:
// * Iterate over each array
// * Count the number of occurrences
// * For each number, increment the sum by (number * count_1 * count_2)
// (Would technically be parallelizable if you wanted to get fancy, but JFC I'm not ready for that in Zig yet)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var counts1 = std.AutoHashMap(u32, u32).init(allocator);
var counts2 = std.AutoHashMap(u32, u32).init(allocator);
// Later, we'll need to iterate over the keys of `counts1` to sum up the final value.
// You'd think you could do this with:
// ```
// const iter = counts1.keyIterator();
// while (try iter.next()) |key| {
// ...
// ```
// But that gives an error that the iterator is a const pointer but `while` requires a mutable pointer.
// So instead I preserve the encountered keys in this array so that we can then loop over them.
// Ridiculous language.
var preservedKeys = std.ArrayList(u32).init(allocator);
for (arr1) |elem| {
if (!counts1.contains(elem)) {
try preservedKeys.append(elem);
print("Added {} to the preservedKeys list\n", .{elem});
}
try counts1.put(elem, (counts1.get(elem) orelse 0) + 1);
}
for (arr2) |elem| {
try counts2.put(elem, (counts2.get(elem) orelse 0) + 1);
}
print("Beginning the calculation", .{});
var output: u32 = 0;
for (preservedKeys.items) |key| {
print("Operating on {} - at this point the output value is {}\n", .{key, output});
const count1 = (counts1.get(key) orelse 0);
const count2 = (counts2.get(key) orelse 0);
const increment = key * count1 * count2;
print("The increment for key {} is {} (calculated based on counts {} and {})\n", .{key, increment, count1, count2});
output += increment;
}
return output;
}
fn parseInputFileToArrays() !struct { first: []u32, second: []u32 } {
@ -118,6 +172,6 @@ test "part one" {
try expect(try part_one() == 11);
}
// test "part two" {
// try expect(try part_two() == 31);
// }
test "part two" {
try expect(try part_two() == 31);
}