parent
b8e82c25be
commit
164127412d
@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day7"
|
||||||
|
version = "0.1.0"
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "day7"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483
|
||||||
|
J2883 666
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,59 @@
|
|||||||
|
use std::io::{Lines, StdinLock};
|
||||||
|
|
||||||
|
/// A simple, but panic-y reader for stdin
|
||||||
|
pub fn simple_stdin() -> SimpleStdinLines {
|
||||||
|
SimpleStdinLines::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SimpleStdinLines {
|
||||||
|
pub inner: Lines<StdinLock<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleStdinLines {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { inner: std::io::stdin().lines() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for SimpleStdinLines {
|
||||||
|
type Item = String;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.inner.next().transpose().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple, but panic-y parser of arguments
|
||||||
|
pub fn simple_args() -> SimpleArgs {
|
||||||
|
SimpleArgs::init()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct SimpleArgs {
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleArgs {
|
||||||
|
pub fn init() -> Self {
|
||||||
|
Self { args: std::env::args().collect() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_flag(&self, name: &str) -> bool {
|
||||||
|
self.args.iter().any(|x| x == name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self, name: &str, default: &str) -> String {
|
||||||
|
let pos = self.args.iter().position(|x| x == name);
|
||||||
|
if let Some(idx) = pos {
|
||||||
|
if let Some(s) = self.args.iter().nth(idx + 1) {
|
||||||
|
s.clone()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String::from(default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String::from(default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Token {
|
||||||
|
Part(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tokenize_line(input: &str) -> Vec<Token> {
|
||||||
|
let mut vals = vec![];
|
||||||
|
let mut idx = 0;
|
||||||
|
|
||||||
|
while idx < input.len() {
|
||||||
|
let c = input.chars().nth(idx).unwrap();
|
||||||
|
|
||||||
|
if is_keyword_char(c) {
|
||||||
|
let end = match input.chars().skip(idx)
|
||||||
|
.position(|x| !is_keyword_char(x)) {
|
||||||
|
|
||||||
|
Some(pos) => idx + pos,
|
||||||
|
None => input.len(),
|
||||||
|
};
|
||||||
|
vals.push(Token::Part(String::from(&input[idx..end])));
|
||||||
|
idx = end;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
idx = idx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vals
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_keyword_char(c: char) -> bool {
|
||||||
|
match c {
|
||||||
|
'A'|'K'|'Q'|'J'|'T'|'0'..='9' => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_part<'a>(iter: &mut impl Iterator<Item = &'a Token>) -> Option<String> {
|
||||||
|
if let Some(Token::Part(k)) = iter.next() {
|
||||||
|
Some(k.clone())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,274 @@
|
|||||||
|
mod input_tools;
|
||||||
|
mod lexer;
|
||||||
|
|
||||||
|
use lexer::*;
|
||||||
|
use input_tools::*;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
|
||||||
|
enum Handtype {
|
||||||
|
HighCard,
|
||||||
|
Pair,
|
||||||
|
TwoPair,
|
||||||
|
ThreeKind,
|
||||||
|
FullHouse,
|
||||||
|
FourKind,
|
||||||
|
FiveKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handtype {
|
||||||
|
pub fn new(cards: &[u32; 5]) -> Self {
|
||||||
|
let mut counts = [0u32; 14];
|
||||||
|
for card in cards {
|
||||||
|
counts[card.saturating_sub(1) as usize] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let max = *counts.iter().max().unwrap();
|
||||||
|
if max == 5 {
|
||||||
|
Handtype::FiveKind
|
||||||
|
}
|
||||||
|
else if max == 4 {
|
||||||
|
Handtype::FourKind
|
||||||
|
}
|
||||||
|
else if max == 3 {
|
||||||
|
if counts.iter().any(|x| *x == 2 )
|
||||||
|
{
|
||||||
|
Handtype::FullHouse
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Handtype::ThreeKind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if max == 2 {
|
||||||
|
if counts.iter().filter(|x| **x == 2).count() == 2 {
|
||||||
|
Handtype::TwoPair
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Handtype::Pair
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Handtype::HighCard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_jokers(cards: &[u32; 5]) -> Self {
|
||||||
|
let mut counts = [0u32; 15];
|
||||||
|
for card in cards {
|
||||||
|
counts[*card as usize] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let jokers = counts[1];
|
||||||
|
let max = *counts.iter().skip(2).max().unwrap() + jokers;
|
||||||
|
if max == 5 {
|
||||||
|
Handtype::FiveKind
|
||||||
|
}
|
||||||
|
else if max == 4 {
|
||||||
|
Handtype::FourKind
|
||||||
|
}
|
||||||
|
else if max == 3 {
|
||||||
|
let pair_count = counts.iter().skip(2).filter(|x| **x == 2 ).count();
|
||||||
|
if pair_count == 2
|
||||||
|
{
|
||||||
|
// Joker full house
|
||||||
|
Handtype::FullHouse
|
||||||
|
}
|
||||||
|
else if pair_count == 1 && jokers == 0
|
||||||
|
{
|
||||||
|
// True full house
|
||||||
|
Handtype::FullHouse
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Handtype::ThreeKind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if max == 2 {
|
||||||
|
if counts.iter().skip(2).filter(|x| **x == 2).count() == 2 {
|
||||||
|
Handtype::TwoPair
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Handtype::Pair
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Handtype::HighCard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd)]
|
||||||
|
struct Hand {
|
||||||
|
kind: Handtype,
|
||||||
|
cards: [u32; 5],
|
||||||
|
bid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hand {
|
||||||
|
pub fn from_tokens<'a>(toks: &mut impl Iterator<Item = &'a Token>) -> Self {
|
||||||
|
|
||||||
|
let hand_desc = expect_part(toks).unwrap();
|
||||||
|
let bid_desc = expect_part(toks).unwrap();
|
||||||
|
let mut cards = [0; 5];
|
||||||
|
|
||||||
|
for (idx, c) in Iterator::enumerate(hand_desc.chars()) {
|
||||||
|
let val = match c {
|
||||||
|
'A' => 14,
|
||||||
|
'K' => 13,
|
||||||
|
'Q' => 12,
|
||||||
|
'J' => 11,
|
||||||
|
'T' => 10,
|
||||||
|
'2'..='9' => u32::from_str_radix(&c.to_string(), 10).unwrap(),
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
cards[idx] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind = Handtype::new(&cards);
|
||||||
|
|
||||||
|
let bid = u32::from_str_radix(&bid_desc, 10).unwrap();
|
||||||
|
|
||||||
|
Self { cards, bid, kind }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Ord for Hand {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
match self.kind.cmp(&other.kind) {
|
||||||
|
Ordering::Less => Ordering::Less,
|
||||||
|
Ordering::Greater => Ordering::Greater,
|
||||||
|
Ordering::Equal => {
|
||||||
|
self.cards.iter().cmp(other.cards.iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd)]
|
||||||
|
struct HandWithJokers {
|
||||||
|
kind: Handtype,
|
||||||
|
cards: [u32; 5],
|
||||||
|
bid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HandWithJokers {
|
||||||
|
pub fn from_tokens<'a>(toks: &mut impl Iterator<Item = &'a Token>) -> Self {
|
||||||
|
|
||||||
|
let hand_desc = expect_part(toks).unwrap();
|
||||||
|
let bid_desc = expect_part(toks).unwrap();
|
||||||
|
let mut cards = [0; 5];
|
||||||
|
|
||||||
|
for (idx, c) in Iterator::enumerate(hand_desc.chars()) {
|
||||||
|
let val = match c {
|
||||||
|
'A' => 14,
|
||||||
|
'K' => 13,
|
||||||
|
'Q' => 12,
|
||||||
|
'J' => 1,
|
||||||
|
'T' => 10,
|
||||||
|
'2'..='9' => u32::from_str_radix(&c.to_string(), 10).unwrap(),
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
cards[idx] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind = Handtype::new_with_jokers(&cards);
|
||||||
|
|
||||||
|
let bid = u32::from_str_radix(&bid_desc, 10).unwrap();
|
||||||
|
|
||||||
|
Self { cards, bid, kind }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Ord for HandWithJokers {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
match self.kind.cmp(&other.kind) {
|
||||||
|
Ordering::Less => Ordering::Less,
|
||||||
|
Ordering::Greater => Ordering::Greater,
|
||||||
|
Ordering::Equal => {
|
||||||
|
self.cards.iter().cmp(other.cards.iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn puzzle1(verbose: bool) {
|
||||||
|
println!("Part 1");
|
||||||
|
|
||||||
|
let mut sum = 0;
|
||||||
|
let mut hands = vec![];
|
||||||
|
for line in simple_stdin() {
|
||||||
|
let tokens = tokenize_line(&line);
|
||||||
|
let hand = Hand::from_tokens(&mut tokens.iter());
|
||||||
|
|
||||||
|
let pos = hands.binary_search(&hand).unwrap_or_else(|e| e);
|
||||||
|
hands.insert(pos, hand);
|
||||||
|
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println!("{} -> {:?}", line, hand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx, h) in Iterator::enumerate(hands.iter()) {
|
||||||
|
let rank = idx + 1;
|
||||||
|
let score = rank as u32 * h.bid;
|
||||||
|
sum += score;
|
||||||
|
if verbose {
|
||||||
|
println!("Score {} * {} = {} => {:?}", h.bid, rank, score, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Sum: {}", sum);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn puzzle2(verbose: bool) {
|
||||||
|
println!("Part 2");
|
||||||
|
|
||||||
|
let mut sum = 0u64;
|
||||||
|
let mut hands = vec![];
|
||||||
|
for line in simple_stdin() {
|
||||||
|
let tokens = tokenize_line(&line);
|
||||||
|
let hand = HandWithJokers::from_tokens(&mut tokens.iter());
|
||||||
|
|
||||||
|
let pos = hands.binary_search(&hand).unwrap_or_else(|e| e);
|
||||||
|
hands.insert(pos, hand);
|
||||||
|
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println!("{} -> {:?}", line, hand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx, h) in Iterator::enumerate(hands.iter()) {
|
||||||
|
let rank = idx + 1;
|
||||||
|
let score = rank as u64 * h.bid as u64;
|
||||||
|
sum += score;
|
||||||
|
if verbose {
|
||||||
|
println!("Score {} * {} = {} => {:?}", h.bid, rank, score, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Sum: {}", sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = simple_args();
|
||||||
|
let verbose = args.get_flag("-v");
|
||||||
|
let part = args.get_value("-p", "1");
|
||||||
|
|
||||||
|
match part.as_str() {
|
||||||
|
"1" => puzzle1(verbose),
|
||||||
|
"2" => puzzle2(verbose),
|
||||||
|
_ => println!("Nothing to do")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in new issue