Compare commits
2 Commits
8f9891cdde
...
c5bf80a9b5
Author | SHA1 | Date | |
---|---|---|---|
c5bf80a9b5 | |||
a15d429279 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
zig-cache
|
zig-cache
|
||||||
zig-out
|
zig-out
|
||||||
|
input.txt
|
||||||
|
@ -18,3 +18,10 @@ pub const Day = struct {
|
|||||||
part1: ?Solver,
|
part1: ?Solver,
|
||||||
part2: ?Solver
|
part2: ?Solver
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn expectAnswerUInt(solver: Solver, answer: u32, lines: []const []const u8) !void {
|
||||||
|
var input = Input{ .lines = lines, .allocator = std.testing.allocator };
|
||||||
|
var actual = try solver(&input);
|
||||||
|
var expected: u32 = answer;
|
||||||
|
try std.testing.expectEqual(expected, actual.uint);
|
||||||
|
}
|
||||||
|
14
src/day1.zig
14
src/day1.zig
@ -94,20 +94,17 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "part 1 example" {
|
test "part 1 example" {
|
||||||
var lines = [_][]const u8{
|
var example_input = [_][]const u8{
|
||||||
"1abc2",
|
"1abc2",
|
||||||
"pqr3stu8vwx",
|
"pqr3stu8vwx",
|
||||||
"a1b2c3d4e5f",
|
"a1b2c3d4e5f",
|
||||||
"treb7uchet",
|
"treb7uchet",
|
||||||
};
|
};
|
||||||
var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part1, 142, &example_input);
|
||||||
var actual = try part1(&input);
|
|
||||||
var expected: u32 = 142;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "part 2 example" {
|
test "part 2 example" {
|
||||||
var lines = [_][]const u8{
|
var example_input = [_][]const u8{
|
||||||
"two1nine",
|
"two1nine",
|
||||||
"eightwothree",
|
"eightwothree",
|
||||||
"abcone2threexyz",
|
"abcone2threexyz",
|
||||||
@ -116,8 +113,5 @@ test "part 2 example" {
|
|||||||
"zoneight234",
|
"zoneight234",
|
||||||
"7pqrstsixteen",
|
"7pqrstsixteen",
|
||||||
};
|
};
|
||||||
var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part2, 281, &example_input);
|
||||||
var actual = try part2(&input);
|
|
||||||
var expected: u32 = 281;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
10
src/day2.zig
10
src/day2.zig
@ -111,15 +111,9 @@ const example_input = [_][]const u8{
|
|||||||
};
|
};
|
||||||
|
|
||||||
test "part 1 example" {
|
test "part 1 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part1, 8, &example_input);
|
||||||
var actual = try part1(&input);
|
|
||||||
var expected: u32 = 8;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "part 2 example" {
|
test "part 2 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part2, 2286, &example_input);
|
||||||
var actual = try part2(&input);
|
|
||||||
var expected: u32 = 2286;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
10
src/day3.zig
10
src/day3.zig
@ -121,15 +121,9 @@ const example_input = [_][]const u8{
|
|||||||
};
|
};
|
||||||
|
|
||||||
test "part 1 example" {
|
test "part 1 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part1, 4361, &example_input);
|
||||||
var actual = try part1(&input);
|
|
||||||
var expected: u32 = 4361;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "part 2 example" {
|
test "part 2 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part2, 467835, &example_input);
|
||||||
var actual = try part2(&input);
|
|
||||||
var expected: u32 = 467835;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
10
src/day4.zig
10
src/day4.zig
@ -105,15 +105,9 @@ const example_input = [_][]const u8{
|
|||||||
};
|
};
|
||||||
|
|
||||||
test "part 1 example" {
|
test "part 1 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part1, 13, &example_input);
|
||||||
var actual = try part1(&input);
|
|
||||||
var expected: u32 = 13;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "part 2 example" {
|
test "part 2 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part2, 30, &example_input);
|
||||||
var actual = try part2(&input);
|
|
||||||
var expected: u32 = 30;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
10
src/day5.zig
10
src/day5.zig
@ -249,16 +249,10 @@ const example_input = [_][]const u8{
|
|||||||
};
|
};
|
||||||
|
|
||||||
test "part 1 example" {
|
test "part 1 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part1, 35, &example_input);
|
||||||
var actual = try part1(&input);
|
|
||||||
var expected: u32 = 35;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "part 2 example" {
|
test "part 2 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part2, 46, &example_input);
|
||||||
var actual = try part2(&input);
|
|
||||||
var expected: u32 = 46;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/day6.zig
10
src/day6.zig
@ -101,15 +101,9 @@ const example_input = [_][]const u8{
|
|||||||
};
|
};
|
||||||
|
|
||||||
test "part 1 example" {
|
test "part 1 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part1, 288, &example_input);
|
||||||
var actual = try part1(&input);
|
|
||||||
var expected: u32 = 288;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "part 2 example" {
|
test "part 2 example" {
|
||||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
try aoc.expectAnswerUInt(part2, 71503, &example_input);
|
||||||
var actual = try part2(&input);
|
|
||||||
var expected: u32 = 71503;
|
|
||||||
try std.testing.expectEqual(expected, actual.uint);
|
|
||||||
}
|
}
|
||||||
|
325
src/day7.zig
Normal file
325
src/day7.zig
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
const aoc = @import("./aoc.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const HandStrength = enum {
|
||||||
|
Nothing,
|
||||||
|
HighCard,
|
||||||
|
OnePair,
|
||||||
|
TwoPair,
|
||||||
|
ThreeOfAKind,
|
||||||
|
FullHouse,
|
||||||
|
FourOfAKind,
|
||||||
|
FiveOfAKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CARD_VARIANTS = 13;
|
||||||
|
const CardIndex = u4;
|
||||||
|
|
||||||
|
const Hand = [5]CardIndex;
|
||||||
|
|
||||||
|
const HandBid = struct {
|
||||||
|
hand: Hand = undefined,
|
||||||
|
bid: u32
|
||||||
|
};
|
||||||
|
|
||||||
|
const CardCounts = [CARD_VARIANTS]CardIndex;
|
||||||
|
const IndexArray = std.ArrayList(usize);
|
||||||
|
const HandBids = std.ArrayList(HandBid);
|
||||||
|
|
||||||
|
fn hand_to_str(hand: Hand) [5]u8 {
|
||||||
|
var str = [1]u8{0} ** 5;
|
||||||
|
for (0..hand.len) |i| {
|
||||||
|
str[i] = switch (hand[i]) {
|
||||||
|
12 => 'A',
|
||||||
|
11 => 'K',
|
||||||
|
10 => 'Q',
|
||||||
|
9 => 'J',
|
||||||
|
8 => 'T',
|
||||||
|
7 => '9',
|
||||||
|
6 => '8',
|
||||||
|
5 => '7',
|
||||||
|
4 => '6',
|
||||||
|
3 => '5',
|
||||||
|
2 => '4',
|
||||||
|
1 => '3',
|
||||||
|
0 => '2',
|
||||||
|
else => unreachable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_card_index(char: u8) CardIndex {
|
||||||
|
return switch (char) {
|
||||||
|
'A' => 12,
|
||||||
|
'K' => 11,
|
||||||
|
'Q' => 10,
|
||||||
|
'J' => 9,
|
||||||
|
'T' => 8,
|
||||||
|
'9' => 7,
|
||||||
|
'8' => 6,
|
||||||
|
'7' => 5,
|
||||||
|
'6' => 4,
|
||||||
|
'5' => 3,
|
||||||
|
'4' => 2,
|
||||||
|
'3' => 1,
|
||||||
|
'2' => 0,
|
||||||
|
else => unreachable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(allocator: Allocator, lines: []const []const u8) !HandBids {
|
||||||
|
var hand_bids = HandBids.init(allocator);
|
||||||
|
|
||||||
|
for (lines) |line| {
|
||||||
|
var tokens = std.mem.tokenizeScalar(u8, line, ' ');
|
||||||
|
const hand_str = tokens.next().?;
|
||||||
|
const bid_str = tokens.next().?;
|
||||||
|
const bid = try std.fmt.parseInt(u32, bid_str, 10);
|
||||||
|
assert(hand_str.len == 5);
|
||||||
|
|
||||||
|
var hand_bid = HandBid{ .bid = bid };
|
||||||
|
for (0..5) |i| {
|
||||||
|
hand_bid.hand[i] = get_card_index(hand_str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try hand_bids.append(hand_bid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hand_bids;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_cards(hand: Hand) CardCounts {
|
||||||
|
var counts = [1]CardIndex{0} ** CARD_VARIANTS;
|
||||||
|
for (hand) |card| {
|
||||||
|
counts[card] += 1;
|
||||||
|
}
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn determine_card_strength(hand: Hand) HandStrength {
|
||||||
|
var counts = count_cards(hand);
|
||||||
|
|
||||||
|
var ones_count: u4 = 0;
|
||||||
|
var two_count: u4 = 0;
|
||||||
|
var three_count: u4 = 0;
|
||||||
|
for (counts) |count| {
|
||||||
|
switch (count) {
|
||||||
|
5 => return .FiveOfAKind,
|
||||||
|
4 => return .FourOfAKind,
|
||||||
|
3 => three_count += 1,
|
||||||
|
2 => two_count += 1,
|
||||||
|
1 => ones_count += 1,
|
||||||
|
else => continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ones_count == 5) {
|
||||||
|
return .HighCard;
|
||||||
|
}
|
||||||
|
if (two_count == 2) {
|
||||||
|
return .TwoPair;
|
||||||
|
}
|
||||||
|
if (two_count == 1 and three_count == 1) {
|
||||||
|
return .FullHouse;
|
||||||
|
}
|
||||||
|
if (ones_count == 2 and three_count == 1) {
|
||||||
|
return .ThreeOfAKind;
|
||||||
|
}
|
||||||
|
if (two_count == 1 and ones_count == 3) {
|
||||||
|
return .OnePair;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn determine_card_strength_joker(hand: Hand) HandStrength {
|
||||||
|
const joker_card = comptime get_card_index('J');
|
||||||
|
|
||||||
|
var jokers = std.BoundedArray(usize, 5).init(0) catch unreachable;
|
||||||
|
var card_types = std.BoundedArray(CardIndex, 5).init(0) catch unreachable;
|
||||||
|
for (0.., hand) |i, card| {
|
||||||
|
if (card == joker_card) {
|
||||||
|
jokers.append(i) catch unreachable;
|
||||||
|
}
|
||||||
|
if (std.mem.indexOfScalar(CardIndex, card_types.slice(), card) == null) {
|
||||||
|
card_types.append(card) catch unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jokers.len == 0) {
|
||||||
|
return determine_card_strength(hand);
|
||||||
|
}
|
||||||
|
|
||||||
|
var joker_choices_container = [1]usize{0} ** 5;
|
||||||
|
var joker_choices = joker_choices_container[0..jokers.len];
|
||||||
|
|
||||||
|
var modified_hand: Hand = undefined;
|
||||||
|
@memcpy(&modified_hand, &hand);
|
||||||
|
|
||||||
|
var best_strength = HandStrength.Nothing;
|
||||||
|
const combination_count = std.math.powi(usize, card_types.len, jokers.len) catch unreachable;
|
||||||
|
for (0..combination_count) |_| {
|
||||||
|
for (0..joker_choices.len) |i| {
|
||||||
|
modified_hand[jokers.get(i)] = card_types.get(joker_choices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const strength = determine_card_strength(modified_hand);
|
||||||
|
|
||||||
|
best_strength = @enumFromInt(@max(@intFromEnum(best_strength), @intFromEnum(strength)));
|
||||||
|
|
||||||
|
joker_choices[0] += 1;
|
||||||
|
for (0..(joker_choices.len-1)) |i| {
|
||||||
|
if (joker_choices[i] == card_types.len) {
|
||||||
|
joker_choices[i] = 0;
|
||||||
|
joker_choices[i+1] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_strength;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_hands(a: *Hand, b: *Hand) bool {
|
||||||
|
inline for (0..a.len) |i| {
|
||||||
|
if (a[i] != b[i]) {
|
||||||
|
return a[i] > b[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_hands_joker(a: *Hand, b: *Hand) bool {
|
||||||
|
const joker_card = comptime get_card_index('J');
|
||||||
|
|
||||||
|
inline for (0..a.len) |i| {
|
||||||
|
if (a[i] != b[i]) {
|
||||||
|
const card_a = if (a[i] == joker_card) 0 else a[i]+1;
|
||||||
|
const card_b = if (b[i] == joker_card) 0 else b[i]+1;
|
||||||
|
return card_a > card_b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_same_strength(hands: []Hand, indexes: []usize, cmp: *const fn(*Hand, *Hand) bool) void {
|
||||||
|
for (0..(indexes.len-1)) |i| {
|
||||||
|
for ((i+1)..indexes.len) |j| {
|
||||||
|
const hand_a = &hands[indexes[i]];
|
||||||
|
const hand_b = &hands[indexes[j]];
|
||||||
|
if (cmp(hand_a, hand_b)) {
|
||||||
|
std.mem.swap(usize, &indexes[i], &indexes[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
|
const allocator = input.allocator;
|
||||||
|
const hand_bids = try parse_input(allocator, input.lines);
|
||||||
|
defer hand_bids.deinit();
|
||||||
|
|
||||||
|
var hand_strengths = try allocator.alloc(HandStrength, hand_bids.items.len);
|
||||||
|
defer allocator.free(hand_strengths);
|
||||||
|
|
||||||
|
var hands = try allocator.alloc(Hand, hand_bids.items.len);
|
||||||
|
defer allocator.free(hands);
|
||||||
|
|
||||||
|
for (0..hand_bids.items.len) |i| {
|
||||||
|
hands[i] = hand_bids.items[i].hand;
|
||||||
|
hand_strengths[i] = determine_card_strength(hand_bids.items[i].hand);
|
||||||
|
}
|
||||||
|
|
||||||
|
var order = try IndexArray.initCapacity(allocator, hand_bids.items.len);
|
||||||
|
defer order.deinit();
|
||||||
|
|
||||||
|
var sub_order = try IndexArray.initCapacity(allocator, hand_bids.items.len);
|
||||||
|
defer sub_order.deinit();
|
||||||
|
|
||||||
|
inline for (@typeInfo(HandStrength).Enum.fields) |field| {
|
||||||
|
sub_order.clearRetainingCapacity();
|
||||||
|
|
||||||
|
const hand_strength: HandStrength = @enumFromInt(field.value);
|
||||||
|
for (0..hand_bids.items.len) |idx| {
|
||||||
|
if (hand_strengths[idx] == hand_strength) {
|
||||||
|
sub_order.appendAssumeCapacity(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub_order.items.len != 0) {
|
||||||
|
sort_same_strength(hands, sub_order.items, compare_hands);
|
||||||
|
order.appendSliceAssumeCapacity(sub_order.items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var answer: u32 = 0;
|
||||||
|
for (0.., order.items) |i, idx| {
|
||||||
|
answer += @as(u32, @intCast(i+1)) * hand_bids.items[idx].bid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .uint = answer };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
|
const allocator = input.allocator;
|
||||||
|
const hand_bids = try parse_input(allocator, input.lines);
|
||||||
|
defer hand_bids.deinit();
|
||||||
|
|
||||||
|
var hand_strengths = try allocator.alloc(HandStrength, hand_bids.items.len);
|
||||||
|
defer allocator.free(hand_strengths);
|
||||||
|
|
||||||
|
var hands = try allocator.alloc(Hand, hand_bids.items.len);
|
||||||
|
defer allocator.free(hands);
|
||||||
|
|
||||||
|
for (0..hand_bids.items.len) |i| {
|
||||||
|
hands[i] = hand_bids.items[i].hand;
|
||||||
|
hand_strengths[i] = determine_card_strength_joker(hand_bids.items[i].hand);
|
||||||
|
}
|
||||||
|
|
||||||
|
var order = try IndexArray.initCapacity(allocator, hand_bids.items.len);
|
||||||
|
defer order.deinit();
|
||||||
|
|
||||||
|
var sub_order = try IndexArray.initCapacity(allocator, hand_bids.items.len);
|
||||||
|
defer sub_order.deinit();
|
||||||
|
|
||||||
|
inline for (@typeInfo(HandStrength).Enum.fields) |field| {
|
||||||
|
sub_order.clearRetainingCapacity();
|
||||||
|
|
||||||
|
const hand_strength: HandStrength = @enumFromInt(field.value);
|
||||||
|
for (0..hand_bids.items.len) |idx| {
|
||||||
|
if (hand_strengths[idx] == hand_strength) {
|
||||||
|
sub_order.appendAssumeCapacity(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub_order.items.len != 0) {
|
||||||
|
sort_same_strength(hands, sub_order.items, compare_hands_joker);
|
||||||
|
order.appendSliceAssumeCapacity(sub_order.items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var answer: u32 = 0;
|
||||||
|
for (0.., order.items) |i, idx| {
|
||||||
|
answer += @as(u32, @intCast(i+1)) * hand_bids.items[idx].bid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .uint = answer };
|
||||||
|
}
|
||||||
|
|
||||||
|
const example_input = [_][]const u8{
|
||||||
|
"32T3K 765",
|
||||||
|
"T55J5 684",
|
||||||
|
"KK677 28",
|
||||||
|
"KTJJT 220",
|
||||||
|
"QQQJA 483"
|
||||||
|
};
|
||||||
|
|
||||||
|
test "part 1 example" {
|
||||||
|
try aoc.expectAnswerUInt(part1, 6440, &example_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "part 2 example" {
|
||||||
|
try aoc.expectAnswerUInt(part2, 5905, &example_input);
|
||||||
|
}
|
@ -27,6 +27,7 @@ const Days = [_]aoc.Day{
|
|||||||
create_day(@import("day4.zig")),
|
create_day(@import("day4.zig")),
|
||||||
create_day(@import("day5.zig")),
|
create_day(@import("day5.zig")),
|
||||||
create_day(@import("day6.zig")),
|
create_day(@import("day6.zig")),
|
||||||
|
create_day(@import("day7.zig")),
|
||||||
};
|
};
|
||||||
|
|
||||||
fn kilobytes(count: u32) u32 {
|
fn kilobytes(count: u32) u32 {
|
||||||
@ -179,4 +180,5 @@ test {
|
|||||||
_ = @import("day4.zig");
|
_ = @import("day4.zig");
|
||||||
_ = @import("day5.zig");
|
_ = @import("day5.zig");
|
||||||
_ = @import("day6.zig");
|
_ = @import("day6.zig");
|
||||||
|
_ = @import("day7.zig");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user