Compare commits
2 Commits
8f9891cdde
...
c5bf80a9b5
Author | SHA1 | Date | |
---|---|---|---|
c5bf80a9b5 | |||
a15d429279 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
zig-cache
|
||||
zig-out
|
||||
input.txt
|
||||
|
@ -18,3 +18,10 @@ pub const Day = struct {
|
||||
part1: ?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" {
|
||||
var lines = [_][]const u8{
|
||||
var example_input = [_][]const u8{
|
||||
"1abc2",
|
||||
"pqr3stu8vwx",
|
||||
"a1b2c3d4e5f",
|
||||
"treb7uchet",
|
||||
};
|
||||
var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator };
|
||||
var actual = try part1(&input);
|
||||
var expected: u32 = 142;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part1, 142, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
var lines = [_][]const u8{
|
||||
var example_input = [_][]const u8{
|
||||
"two1nine",
|
||||
"eightwothree",
|
||||
"abcone2threexyz",
|
||||
@ -116,8 +113,5 @@ test "part 2 example" {
|
||||
"zoneight234",
|
||||
"7pqrstsixteen",
|
||||
};
|
||||
var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator };
|
||||
var actual = try part2(&input);
|
||||
var expected: u32 = 281;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part2, 281, &example_input);
|
||||
}
|
||||
|
10
src/day2.zig
10
src/day2.zig
@ -111,15 +111,9 @@ const example_input = [_][]const u8{
|
||||
};
|
||||
|
||||
test "part 1 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part1(&input);
|
||||
var expected: u32 = 8;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part1, 8, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part2(&input);
|
||||
var expected: u32 = 2286;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part2, 2286, &example_input);
|
||||
}
|
||||
|
10
src/day3.zig
10
src/day3.zig
@ -121,15 +121,9 @@ const example_input = [_][]const u8{
|
||||
};
|
||||
|
||||
test "part 1 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part1(&input);
|
||||
var expected: u32 = 4361;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part1, 4361, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part2(&input);
|
||||
var expected: u32 = 467835;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part2, 467835, &example_input);
|
||||
}
|
||||
|
10
src/day4.zig
10
src/day4.zig
@ -105,15 +105,9 @@ const example_input = [_][]const u8{
|
||||
};
|
||||
|
||||
test "part 1 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part1(&input);
|
||||
var expected: u32 = 13;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part1, 13, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part2(&input);
|
||||
var expected: u32 = 30;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part2, 30, &example_input);
|
||||
}
|
||||
|
10
src/day5.zig
10
src/day5.zig
@ -249,16 +249,10 @@ const example_input = [_][]const u8{
|
||||
};
|
||||
|
||||
test "part 1 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part1(&input);
|
||||
var expected: u32 = 35;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part1, 35, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part2(&input);
|
||||
var expected: u32 = 46;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part2, 46, &example_input);
|
||||
}
|
||||
|
||||
|
10
src/day6.zig
10
src/day6.zig
@ -101,15 +101,9 @@ const example_input = [_][]const u8{
|
||||
};
|
||||
|
||||
test "part 1 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part1(&input);
|
||||
var expected: u32 = 288;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part1, 288, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
|
||||
var actual = try part2(&input);
|
||||
var expected: u32 = 71503;
|
||||
try std.testing.expectEqual(expected, actual.uint);
|
||||
try aoc.expectAnswerUInt(part2, 71503, &example_input);
|
||||
}
|
||||
|
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("day5.zig")),
|
||||
create_day(@import("day6.zig")),
|
||||
create_day(@import("day7.zig")),
|
||||
};
|
||||
|
||||
fn kilobytes(count: u32) u32 {
|
||||
@ -179,4 +180,5 @@ test {
|
||||
_ = @import("day4.zig");
|
||||
_ = @import("day5.zig");
|
||||
_ = @import("day6.zig");
|
||||
_ = @import("day7.zig");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user