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

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay