Advent of Code #7 (in Crystal)
Caleb Weeks
Posted on December 7, 2023
Today's problem was primarily an exercise in sorting, with a little bit of math/logic for part 2. Instead of assigning a new order for each card, I kept the numbers the same and assigned new values to the face cards (and the number 10). Then I used Array.group_by
and some patter matching to determine the order of the type of hand.
I don't know of any language that features composition of comparisons. Comparisons are usually represented as -1/0/1
for less than, equal, or greater, but are sometimes represented as an enum or tagged union such as LT/EQ/GT
. I would imagine such a feature to take two comparisons and return the result of the second if the first is equal. It is surprising how often I run into this situation. Maybe when I finally get around to writing my own language, I'll add this feature.
The last thing I wanted to discuss is code readability (or whatever word you want to use for the quality of code). Functional programming is often labeled as a paradigm that leans into math (perhaps too much). While I think that is a misrepresentation, it is often true that a functional style of code lacks descriptiveness. The OOP approach of representing everything as an object has the advantage of forcing you to use descriptive names.
It is certainly possible to write FP style code that uses more intermediate variables and even objects to convey intent and meaning more clearly. But coming up with those descriptive names takes careful consideration, and can slow down the 'real' code.
Sometime, I would like to discuss this more and show examples of improved readability of FP code. But today is not that day, so here is the mess of code I came up with:
input = File.read("input").strip
cards = input.split("\n").map do |line|
card, bid = line.split(' ')
bid = bid.to_i
card = card.gsub({T: ':', J: ';', Q: '<', K: '=', A: '>'})
{card, bid}
end
def type(a)
cases = {
[] of Int32 => 7,
[1] => 7,
[2] => 7,
[1, 1] => 6,
[3] => 7,
[1, 2] => 6,
[1, 1, 1] => 4,
[4] => 7,
[1, 3] => 6,
[2, 2] => 5,
[1, 1, 2] => 4,
[1, 1, 1, 1] => 2,
[5] => 7,
[1, 4] => 6,
[2, 3] => 5,
[1, 1, 3] => 4,
[1, 2, 2] => 3,
[1, 1, 1, 2] => 2,
[1, 1, 1, 1, 1] => 1
}
hand = a
.gsub('1', "")
.chars
.group_by { |x| x }
.values
.map(&.size)
.sort
cases[hand]? || 1
end
def stronger_hand(a, b)
compare_type = type(a) <=> type(b)
compare_type == 0 ? a <=> b : compare_type
end
part1 = cards
.sort { |(a, _), (b, _)| stronger_hand(a, b) }
.map_with_index(1) { |(_, bid), i| bid * i }
.sum
puts part1
part2 = cards
.map { |card, bid| {card.gsub(';', '1'), bid} }
.sort { |(a, _), (b, _)| stronger_hand(a, b) }
.map_with_index(1) { |(_, bid), i| bid * i }
.sum
puts part2
Posted on December 7, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.