DEV Community

Caleb Weeks
Caleb Weeks

Posted on

Advent of Code #7 (in Crystal)

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
Enter fullscreen mode Exit fullscreen mode

Image of AssemblyAI

Automatic Speech Recognition with AssemblyAI

Experience near-human accuracy, low-latency performance, and advanced Speech AI capabilities with AssemblyAI's Speech-to-Text API. Sign up today and get $50 in API credit. No credit card required.

Try the API

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay