/ Rust / 2023 / 07.rs
07.rs
  1  #![feature(test)]
  2  
  3  use counter::Counter;
  4  use itertools::Itertools;
  5  
  6  type Input = Vec<Hand>;
  7  
  8  #[derive(Debug)]
  9  struct Hand {
 10      cards: [Card; 5],
 11      bid: u64,
 12  }
 13  
 14  #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 15  struct Card(u8);
 16  
 17  impl Card {
 18      fn from_byte(b: u8) -> Self {
 19          Self(match b {
 20              b'2'..=b'9' => b - b'0',
 21              b'T' => 10,
 22              b'J' => 11,
 23              b'Q' => 12,
 24              b'K' => 13,
 25              b'A' => 14,
 26              _ => panic!(),
 27          })
 28      }
 29  
 30      fn is_joker(&self) -> bool {
 31          self.0 == 11
 32      }
 33  
 34      fn remap_joker(self) -> Self {
 35          if self.is_joker() {
 36              Self(0)
 37          } else {
 38              self
 39          }
 40      }
 41  }
 42  
 43  impl Hand {
 44      fn sort_key<const JOKERS: bool>(&self) -> impl Ord + std::fmt::Debug {
 45          let cnt = self
 46              .cards
 47              .into_iter()
 48              .filter(|card| !JOKERS || !card.is_joker())
 49              .collect::<Counter<_>>()
 50              .into_map()
 51              .into_values()
 52              .sorted_unstable()
 53              .collect_vec();
 54  
 55          let ty = match &cnt[..] {
 56              [] | [_] => 6,
 57              [1, _] => 5,
 58              [2, _] => 4,
 59              [1, 1, _] => 3,
 60              [1, 2, _] => 2,
 61              [1, 1, 1, _] => 1,
 62              _ => 0,
 63          };
 64  
 65          let cards = if JOKERS {
 66              self.cards.map(Card::remap_joker)
 67          } else {
 68              self.cards
 69          };
 70  
 71          (ty, cards)
 72      }
 73  }
 74  
 75  fn setup(input: &str) -> Input {
 76      input
 77          .lines()
 78          .map(|line| {
 79              let mut line = line.split_whitespace();
 80              let cards = line
 81                  .next()
 82                  .unwrap()
 83                  .bytes()
 84                  .map(Card::from_byte)
 85                  .collect_vec()
 86                  .try_into()
 87                  .unwrap();
 88              let bid = line.next().unwrap().parse().unwrap();
 89              Hand { cards, bid }
 90          })
 91          .collect()
 92  }
 93  
 94  fn solve<O: Ord>(input: &Input, sort_key: fn(&Hand) -> O) -> u64 {
 95      input
 96          .iter()
 97          .sorted_by_cached_key(|&hand| sort_key(hand))
 98          .enumerate()
 99          .map(|(i, hand)| (i as u64 + 1) * hand.bid)
100          .sum()
101  }
102  
103  fn part1(input: &Input) -> u64 {
104      solve(input, Hand::sort_key::<false>)
105  }
106  
107  fn part2(input: &Input) -> u64 {
108      solve(input, Hand::sort_key::<true>)
109  }
110  
111  aoc::main!(2023, 7, ex: 1);