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