Trying to optimize heap allocations
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -48,3 +48,4 @@ tags
|
|||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
|
||||||
input.txt
|
input.txt
|
||||||
|
dhat-heap.json
|
||||||
|
|||||||
6
2023/Cargo.lock
generated
6
2023/Cargo.lock
generated
@@ -259,10 +259,15 @@ dependencies = [
|
|||||||
name = "day-20"
|
name = "day-20"
|
||||||
version = "2023.0.0"
|
version = "2023.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dhat",
|
||||||
|
"error-stack",
|
||||||
"itertools 0.12.0",
|
"itertools 0.12.0",
|
||||||
|
"log",
|
||||||
"nom",
|
"nom",
|
||||||
"num",
|
"num",
|
||||||
"rstest",
|
"rstest",
|
||||||
|
"test-log",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -359,6 +364,7 @@ dependencies = [
|
|||||||
"nom",
|
"nom",
|
||||||
"num",
|
"num",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ repository.workspace = true
|
|||||||
nom = { workspace = true }
|
nom = { workspace = true }
|
||||||
itertools = {workspace = true }
|
itertools = {workspace = true }
|
||||||
num.workspace = true
|
num.workspace = true
|
||||||
|
dhat = { workspace = true }
|
||||||
|
thiserror.workspace = true
|
||||||
|
error-stack.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest.workspace = true
|
rstest.workspace = true
|
||||||
|
test-log.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dhat-heap = []
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||||
|
|
||||||
use day_20::part1;
|
use day_20::part1;
|
||||||
use day_20::part2;
|
use day_20::part2;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
let _profiler = dhat::Profiler::new_heap();
|
||||||
|
|
||||||
let input = include_str!("./input.txt");
|
let input = include_str!("./input.txt");
|
||||||
let part1_result = part1(input);
|
let part1_result = part1(input);
|
||||||
println!("part 1: {part1_result}");
|
println!("part 1: {part1_result}");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap, VecDeque};
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
@@ -27,21 +27,18 @@ struct Module<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Module<'a> {
|
impl<'a> Module<'a> {
|
||||||
fn handle_pulse(&self, from: &'a str, is_high_pulse: bool) -> (Self, Option<bool>) {
|
fn handle_pulse(&mut self, from: &'a str, is_high_pulse: bool) -> Option<bool> {
|
||||||
let mut m = self.clone();
|
//let mut m = self.clone();
|
||||||
match (&m.mod_type, is_high_pulse) {
|
match (&mut self.mod_type, is_high_pulse) {
|
||||||
(ModuleType::Broadcast, _) => (m, Some(is_high_pulse)),
|
(ModuleType::Broadcast, _) => Some(is_high_pulse),
|
||||||
(ModuleType::FlipFlop(_), true) => (m, None),
|
(ModuleType::FlipFlop(_), true) => None,
|
||||||
(ModuleType::FlipFlop(is_on), false) => {
|
(ModuleType::FlipFlop(ref mut is_on), false) => {
|
||||||
let was_on = *is_on;
|
*is_on = !*is_on;
|
||||||
m.mod_type = ModuleType::FlipFlop(!is_on);
|
Some(*is_on)
|
||||||
(m, Some(!was_on))
|
|
||||||
}
|
}
|
||||||
(ModuleType::Conjunction(map), is_high_pulse) => {
|
(ModuleType::Conjunction(memory), is_high_pulse) => {
|
||||||
let mut map = map.clone();
|
*memory.get_mut(from).unwrap() = is_high_pulse;
|
||||||
map.insert(from, is_high_pulse);
|
Some(!memory.values().all(|x| *x))
|
||||||
m.mod_type = ModuleType::Conjunction(map.clone());
|
|
||||||
(m, Some(!map.values().all(|x| *x)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,8 +73,8 @@ impl<'a> Module<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn push_button<'a>(
|
fn push_button<'a>(
|
||||||
mut setup: BTreeMap<&'a str, Module<'a>>,
|
setup: &mut BTreeMap<&'a str, Module<'a>>,
|
||||||
) -> (BTreeMap<&'a str, Module<'a>>, (usize, usize)) {
|
) -> (usize, usize) {
|
||||||
let mut queue = VecDeque::from(vec![("broadcaster", None, false)]);
|
let mut queue = VecDeque::from(vec![("broadcaster", None, false)]);
|
||||||
let mut low_signals = 1;
|
let mut low_signals = 1;
|
||||||
let mut high_signals = 0;
|
let mut high_signals = 0;
|
||||||
@@ -88,14 +85,14 @@ fn push_button<'a>(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let (current_label, from, signal) = queue.pop_front().unwrap();
|
let (current_label, from, signal) = queue.pop_front().unwrap();
|
||||||
let Some(current) = setup.get(current_label) else {
|
let Some(current) = setup.get_mut(current_label) else {
|
||||||
// if not found then in a sink
|
// if not found then in a sink
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let (new_current, signal_to_send) = current.handle_pulse(from.unwrap_or("button"), signal);
|
let signal_to_send = current.handle_pulse(from.unwrap_or("button"), signal);
|
||||||
if let Some(signal_to_send) = signal_to_send {
|
if let Some(signal_to_send) = signal_to_send {
|
||||||
new_current
|
current
|
||||||
.connections
|
.connections
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| (*x, Some(current_label), signal_to_send))
|
.map(|x| (*x, Some(current_label), signal_to_send))
|
||||||
@@ -111,11 +108,12 @@ fn push_button<'a>(
|
|||||||
high_signals += usize::from(signal_to_send);
|
high_signals += usize::from(signal_to_send);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setup.insert(current_label, new_current);
|
//setup.insert(current_label, new_current);
|
||||||
}
|
}
|
||||||
(setup, (low_signals, high_signals))
|
(low_signals, high_signals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn setup_to_key(setup: &BTreeMap<&str, Module>) -> String {
|
fn setup_to_key(setup: &BTreeMap<&str, Module>) -> String {
|
||||||
setup.values().map(Module::state_hash).collect::<String>()
|
setup.values().map(Module::state_hash).collect::<String>()
|
||||||
}
|
}
|
||||||
@@ -130,55 +128,13 @@ fn setup_to_key(setup: &BTreeMap<&str, Module>) -> String {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn part1(input: &str) -> String {
|
pub fn part1(input: &str) -> String {
|
||||||
let (_, mut setup) = parse_input(input).expect("aoc input always valid");
|
let (_, mut setup) = parse_input(input).expect("aoc input always valid");
|
||||||
let mut cache = HashMap::new();
|
|
||||||
let mut high;
|
|
||||||
let mut low;
|
|
||||||
let mut high_count = 0;
|
let mut high_count = 0;
|
||||||
let mut low_count = 0;
|
let mut low_count = 0;
|
||||||
let mut key = setup_to_key(&setup);
|
for _ in 0..1000 {
|
||||||
let mut next_key;
|
let (low, high) = push_button(&mut setup);
|
||||||
let mut iteration = 0;
|
|
||||||
let cycle_start = loop {
|
|
||||||
if iteration >= 1000 {
|
|
||||||
break iteration;
|
|
||||||
}
|
|
||||||
(setup, (low, high)) = push_button(setup);
|
|
||||||
next_key = setup_to_key(&setup);
|
|
||||||
if let Some(x) = cache.insert(key, (high, low, next_key.clone(), iteration)) {
|
|
||||||
break x.3;
|
|
||||||
}
|
|
||||||
high_count += high;
|
high_count += high;
|
||||||
low_count += low;
|
low_count += low;
|
||||||
key = next_key;
|
|
||||||
iteration += 1;
|
|
||||||
};
|
};
|
||||||
if cycle_start < 1000 {
|
|
||||||
let cycle_len = iteration - cycle_start;
|
|
||||||
let nnn = 1000 - iteration;
|
|
||||||
let number_of_cycles = nnn / cycle_len;
|
|
||||||
let left_over_pushes = nnn % cycle_len;
|
|
||||||
let (cycle_high, cycle_low) = cache
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(_, (high, low, _, iter))| {
|
|
||||||
(*iter >= cycle_start && *iter <= iteration).then_some((high, low))
|
|
||||||
})
|
|
||||||
.fold((0, 0), |(high_total, low_total), (high, low)| {
|
|
||||||
(high_total + high, low_total + low)
|
|
||||||
});
|
|
||||||
high_count += number_of_cycles * cycle_high;
|
|
||||||
low_count += number_of_cycles * cycle_low;
|
|
||||||
let (left_high, left_low) = cache
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(_, (high, low, _, iter))| {
|
|
||||||
(*iter >= cycle_start && *iter <= iteration).then_some((high, low))
|
|
||||||
})
|
|
||||||
.take(left_over_pushes)
|
|
||||||
.fold((0, 0), |(high_total, low_total), (high, low)| {
|
|
||||||
(high_total + high, low_total + low)
|
|
||||||
});
|
|
||||||
high_count += left_high;
|
|
||||||
low_count += left_low;
|
|
||||||
}
|
|
||||||
|
|
||||||
(high_count * low_count).to_string()
|
(high_count * low_count).to_string()
|
||||||
}
|
}
|
||||||
@@ -252,7 +208,7 @@ mod test {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[rstest]
|
#[test_log::test(rstest)]
|
||||||
#[case(
|
#[case(
|
||||||
"broadcaster -> a, b, c
|
"broadcaster -> a, b, c
|
||||||
%a -> b
|
%a -> b
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ struct Module<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Module<'a> {
|
impl<'a> Module<'a> {
|
||||||
fn handle_pulse(&mut self, from: &'a str, pulse: Signal) -> Vec<(&'a str, &'a str, Signal)> {
|
fn handle_pulse(&mut self, from: &'a str, pulse: Signal) -> Option<Signal>{
|
||||||
/*println!(
|
/*println!(
|
||||||
"{from} -{}-> {}",
|
"{from} -{}-> {}",
|
||||||
if pulse == Signal::Low { "low" } else { "high" },
|
if pulse == Signal::Low { "low" } else { "high" },
|
||||||
self.label
|
self.label
|
||||||
);*/
|
);*/
|
||||||
let signal_to_send = match (&mut self.mod_type, pulse) {
|
match (&mut self.mod_type, pulse) {
|
||||||
(ModuleType::Broadcast, _) => Some(pulse),
|
(ModuleType::Broadcast, _) => Some(pulse),
|
||||||
(ModuleType::FlipFlop(_), Signal::High) => None,
|
(ModuleType::FlipFlop(_), Signal::High) => None,
|
||||||
(ModuleType::FlipFlop(ref mut state), Signal::Low) => {
|
(ModuleType::FlipFlop(ref mut state), Signal::Low) => {
|
||||||
@@ -67,15 +67,6 @@ impl<'a> Module<'a> {
|
|||||||
Signal::High
|
Signal::High
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
|
||||||
//println!("{self:#?}");
|
|
||||||
if let Some(signal_to_send) = signal_to_send {
|
|
||||||
self.connections
|
|
||||||
.iter()
|
|
||||||
.map(|x| (*x, self.label, signal_to_send))
|
|
||||||
.collect()
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +105,6 @@ impl<'a> Module<'a> {
|
|||||||
|
|
||||||
fn push_button<'a>(
|
fn push_button<'a>(
|
||||||
setup: &mut BTreeMap<&'a str, Module<'a>>,
|
setup: &mut BTreeMap<&'a str, Module<'a>>,
|
||||||
cache: &mut BTreeMap<&'a str, Vec<String>>,
|
|
||||||
) -> (bool, Vec<(&'a str, Signal)>) {
|
) -> (bool, Vec<(&'a str, Signal)>) {
|
||||||
let mut queue = VecDeque::from(vec![("broadcaster", "button", Signal::Low)]);
|
let mut queue = VecDeque::from(vec![("broadcaster", "button", Signal::Low)]);
|
||||||
let mut triggered = Vec::new();
|
let mut triggered = Vec::new();
|
||||||
@@ -127,14 +117,13 @@ fn push_button<'a>(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
cache
|
if let Some(signal_to_send) = current.handle_pulse(from, signal){
|
||||||
.entry(current_label)
|
triggered.push((current_label, signal));
|
||||||
.and_modify(|list| list.push(current.state_hash()))
|
current.connections.iter().for_each(|con| {
|
||||||
.or_insert(vec![current.state_hash()]);
|
queue.push_back((con, current_label, signal_to_send));
|
||||||
|
});
|
||||||
let signal_to_send = current.handle_pulse(from, signal);
|
//queue.extend(signal_to_send);
|
||||||
triggered.push((current_label, signal));
|
}
|
||||||
queue.extend(signal_to_send);
|
|
||||||
}
|
}
|
||||||
(false, triggered)
|
(false, triggered)
|
||||||
}
|
}
|
||||||
@@ -177,7 +166,6 @@ pub fn part2(input: &str) -> String {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut cache = BTreeMap::new();
|
|
||||||
//loop through pushing the button till we found all the connecting nodes cycles
|
//loop through pushing the button till we found all the connecting nodes cycles
|
||||||
for i in 0_u64.. {
|
for i in 0_u64.. {
|
||||||
if penultimate_modules.is_empty() {
|
if penultimate_modules.is_empty() {
|
||||||
@@ -186,7 +174,7 @@ pub fn part2(input: &str) -> String {
|
|||||||
|
|
||||||
//push the button
|
//push the button
|
||||||
//println!("push the button {i}");
|
//println!("push the button {i}");
|
||||||
let (triggered_early, triggered) = push_button(&mut setup, &mut cache);
|
let (triggered_early, triggered) = push_button(&mut setup);
|
||||||
if triggered_early {
|
if triggered_early {
|
||||||
return (i + 1).to_string();
|
return (i + 1).to_string();
|
||||||
}
|
}
|
||||||
@@ -209,7 +197,6 @@ pub fn part2(input: &str) -> String {
|
|||||||
//get the LCM of all the last nodes connections
|
//get the LCM of all the last nodes connections
|
||||||
// TODO this is ugly cause it assumes lineailty from the end of the input
|
// TODO this is ugly cause it assumes lineailty from the end of the input
|
||||||
|
|
||||||
//println!("{cache:#?}");
|
|
||||||
lcm(&penulttimate_lengths).to_string()
|
lcm(&penulttimate_lengths).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +287,7 @@ mod test {
|
|||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(
|
#[case(
|
||||||
"broadcaster -> a, inv
|
"broadcaster -> a
|
||||||
%c -> d
|
%c -> d
|
||||||
%a -> b
|
%a -> b
|
||||||
&inv -> con
|
&inv -> con
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ itertools.workspace = true
|
|||||||
num.workspace = true
|
num.workspace = true
|
||||||
num-traits.workspace = true
|
num-traits.workspace = true
|
||||||
dhat = { workspace = true }
|
dhat = { workspace = true }
|
||||||
|
thiserror.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dhat-heap = []
|
dhat-heap = []
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ use nom::{character::complete, multi::separated_list1, sequence::separated_pair,
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::{Ord, Ordering, PartialOrd},
|
cmp::{Ord, Ordering, PartialOrd},
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
str::FromStr,
|
|
||||||
};
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
struct Day1Part1Error;
|
#[error("Day7 part 1 error")]
|
||||||
|
struct Day7Part1Error;
|
||||||
|
|
||||||
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd)]
|
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd, Copy, Clone)]
|
||||||
enum Card {
|
enum Card {
|
||||||
Two,
|
Two,
|
||||||
Three,
|
Three,
|
||||||
@@ -26,24 +27,24 @@ enum Card {
|
|||||||
King,
|
King,
|
||||||
Ace,
|
Ace,
|
||||||
}
|
}
|
||||||
impl FromStr for Card {
|
impl TryFrom<char> for Card {
|
||||||
type Err = Day1Part1Error;
|
type Error = Day7Part1Error;
|
||||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
fn try_from(input: char) -> Result<Self, Self::Error> {
|
||||||
match input {
|
match input {
|
||||||
"2" => Ok(Self::Two),
|
'2' => Ok(Self::Two),
|
||||||
"3" => Ok(Self::Three),
|
'3' => Ok(Self::Three),
|
||||||
"4" => Ok(Self::Four),
|
'4' => Ok(Self::Four),
|
||||||
"5" => Ok(Self::Five),
|
'5' => Ok(Self::Five),
|
||||||
"6" => Ok(Self::Six),
|
'6' => Ok(Self::Six),
|
||||||
"7" => Ok(Self::Seven),
|
'7' => Ok(Self::Seven),
|
||||||
"8" => Ok(Self::Eight),
|
'8' => Ok(Self::Eight),
|
||||||
"9" => Ok(Self::Nine),
|
'9' => Ok(Self::Nine),
|
||||||
"T" => Ok(Self::Ten),
|
'T' => Ok(Self::Ten),
|
||||||
"J" => Ok(Self::Jack),
|
'J' => Ok(Self::Jack),
|
||||||
"Q" => Ok(Self::Queen),
|
'Q' => Ok(Self::Queen),
|
||||||
"K" => Ok(Self::King),
|
'K' => Ok(Self::King),
|
||||||
"A" => Ok(Self::Ace),
|
'A' => Ok(Self::Ace),
|
||||||
_ => Err(Day1Part1Error),
|
_ => Err(Day7Part1Error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,30 +83,26 @@ enum HandType {
|
|||||||
impl From<&Hand> for HandType {
|
impl From<&Hand> for HandType {
|
||||||
fn from(value: &Hand) -> Self {
|
fn from(value: &Hand) -> Self {
|
||||||
let map = value.cards.iter().fold(BTreeMap::new(), |mut acc, card| {
|
let map = value.cards.iter().fold(BTreeMap::new(), |mut acc, card| {
|
||||||
if let Some(c) = acc.get_mut(card) {
|
acc.entry(card).and_modify(|c| *c += 1).or_insert(1);
|
||||||
*c += 1;
|
|
||||||
} else {
|
|
||||||
acc.insert(card, 1);
|
|
||||||
}
|
|
||||||
acc
|
acc
|
||||||
});
|
});
|
||||||
match map
|
match map
|
||||||
.iter()
|
.into_values()
|
||||||
.sorted_by(|a, b| b.1.cmp(a.1))
|
.sorted_by(|a, &b| b.cmp(a))
|
||||||
.collect::<Vec<_>>()[..]
|
.as_slice()
|
||||||
{
|
{
|
||||||
[(_, 5), ..] => Self::FiveOfAKind,
|
[5, ..] => Self::FiveOfAKind,
|
||||||
[(_, 4), ..] => Self::FourOfAKind,
|
[4, ..] => Self::FourOfAKind,
|
||||||
[(_, 3), (_, 2), ..] => Self::FullHouse,
|
[3, 2, ..] => Self::FullHouse,
|
||||||
[(_, 3), ..] => Self::ThreeOfAKind,
|
[3, ..] => Self::ThreeOfAKind,
|
||||||
[(_, 2), (_, 2), ..] => Self::TwoPair,
|
[2, 2, ..] => Self::TwoPair,
|
||||||
[(_, 2), ..] => Self::OnePair,
|
[2, ..] => Self::OnePair,
|
||||||
_ => Self::HighCard,
|
_ => Self::HighCard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
struct Hand {
|
struct Hand {
|
||||||
pub cards: [Card; 5],
|
pub cards: [Card; 5],
|
||||||
pub bet: u32,
|
pub bet: u32,
|
||||||
@@ -123,10 +120,10 @@ impl Ord for Hand {
|
|||||||
match c {
|
match c {
|
||||||
Ordering::Equal => self
|
Ordering::Equal => self
|
||||||
.cards
|
.cards
|
||||||
.iter()
|
.into_iter()
|
||||||
.interleave(other.cards.iter())
|
.interleave(other.cards)
|
||||||
.tuples::<(_, _)>()
|
.tuples::<(_, _)>()
|
||||||
.find_map(|(a, b)| match a.cmp(b) {
|
.find_map(|(a, b)| match a.cmp(&b) {
|
||||||
Ordering::Equal => None,
|
Ordering::Equal => None,
|
||||||
x => Some(x),
|
x => Some(x),
|
||||||
})
|
})
|
||||||
@@ -149,7 +146,7 @@ pub fn part1(input: &str) -> String {
|
|||||||
hands.sort();
|
hands.sort();
|
||||||
|
|
||||||
hands
|
hands
|
||||||
.iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, hand)| (i + 1) * hand.bet as usize)
|
.map(|(i, hand)| (i + 1) * hand.bet as usize)
|
||||||
.sum::<usize>()
|
.sum::<usize>()
|
||||||
@@ -161,7 +158,7 @@ fn parse_hand(input: &str) -> IResult<&str, Hand> {
|
|||||||
separated_pair(complete::alphanumeric1, complete::space1, complete::u32)(input)?;
|
separated_pair(complete::alphanumeric1, complete::space1, complete::u32)(input)?;
|
||||||
let cards = cards
|
let cards = cards
|
||||||
.chars()
|
.chars()
|
||||||
.filter_map(|c| c.to_string().parse().ok())
|
.filter_map(|c| c.try_into().ok())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("should work");
|
.expect("should work");
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use std::fmt;
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::{Ord, Ordering, PartialOrd},
|
cmp::{Ord, Ordering, PartialOrd},
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
str::FromStr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -27,23 +26,23 @@ enum Card {
|
|||||||
King,
|
King,
|
||||||
Ace,
|
Ace,
|
||||||
}
|
}
|
||||||
impl FromStr for Card {
|
impl TryFrom<char> for Card {
|
||||||
type Err = Day1Part2Error;
|
type Error = Day1Part2Error;
|
||||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
fn try_from(input: char) -> Result<Self, Self::Error> {
|
||||||
match input {
|
match input {
|
||||||
"2" => Ok(Self::Two),
|
'2' => Ok(Self::Two),
|
||||||
"3" => Ok(Self::Three),
|
'3' => Ok(Self::Three),
|
||||||
"4" => Ok(Self::Four),
|
'4' => Ok(Self::Four),
|
||||||
"5" => Ok(Self::Five),
|
'5' => Ok(Self::Five),
|
||||||
"6" => Ok(Self::Six),
|
'6' => Ok(Self::Six),
|
||||||
"7" => Ok(Self::Seven),
|
'7' => Ok(Self::Seven),
|
||||||
"8" => Ok(Self::Eight),
|
'8' => Ok(Self::Eight),
|
||||||
"9" => Ok(Self::Nine),
|
'9' => Ok(Self::Nine),
|
||||||
"T" => Ok(Self::Ten),
|
'T' => Ok(Self::Ten),
|
||||||
"J" => Ok(Self::Joker),
|
'J' => Ok(Self::Joker),
|
||||||
"Q" => Ok(Self::Queen),
|
'Q' => Ok(Self::Queen),
|
||||||
"K" => Ok(Self::King),
|
'K' => Ok(Self::King),
|
||||||
"A" => Ok(Self::Ace),
|
'A' => Ok(Self::Ace),
|
||||||
_ => Err(Day1Part2Error),
|
_ => Err(Day1Part2Error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,28 +101,24 @@ enum HandType {
|
|||||||
|
|
||||||
impl From<&Hand> for HandType {
|
impl From<&Hand> for HandType {
|
||||||
fn from(value: &Hand) -> Self {
|
fn from(value: &Hand) -> Self {
|
||||||
let mut map = value.cards.iter().fold(BTreeMap::new(), |mut acc, card| {
|
let mut map = value.cards.into_iter().fold(BTreeMap::new(), |mut acc, card| {
|
||||||
if let Some(c) = acc.get_mut(card) {
|
acc.entry(card).and_modify(|c| *c += 1).or_insert(1);
|
||||||
*c += 1;
|
|
||||||
} else {
|
|
||||||
acc.insert(card, 1);
|
|
||||||
}
|
|
||||||
acc
|
acc
|
||||||
});
|
});
|
||||||
let jokers = map.remove(&Card::Joker).unwrap_or(0);
|
let jokers = map.remove(&Card::Joker).unwrap_or(0);
|
||||||
match map
|
match map
|
||||||
.iter()
|
.into_values()
|
||||||
.sorted_by(|a, b| b.1.cmp(a.1))
|
.sorted_by(|a, &b| b.cmp(a))
|
||||||
.collect::<Vec<_>>()[..]
|
.as_slice()
|
||||||
{
|
{
|
||||||
[(_, x), ..] if jokers + x == 5 => Self::FiveOfAKind,
|
[x, ..] if jokers + x == 5 => Self::FiveOfAKind,
|
||||||
[] if jokers == 5 => Self::FiveOfAKind,
|
[] if jokers == 5 => Self::FiveOfAKind,
|
||||||
[(_, x), ..] if jokers + x == 4 => Self::FourOfAKind,
|
[x, ..] if jokers + x == 4 => Self::FourOfAKind,
|
||||||
[(_, 3), (_, 2)] => Self::FullHouse,
|
[3, 2] => Self::FullHouse,
|
||||||
[(_, 2), (_, 2)] if jokers == 1 => Self::FullHouse,
|
[2, 2] if jokers == 1 => Self::FullHouse,
|
||||||
[(_, x), ..] if jokers + x == 3 => Self::ThreeOfAKind,
|
[x, ..] if jokers + x == 3 => Self::ThreeOfAKind,
|
||||||
[(_, 2), (_, 2), ..] => Self::TwoPair,
|
[2, 2, ..] => Self::TwoPair,
|
||||||
[(_, x), ..] if jokers + x == 2 => Self::OnePair,
|
[x, ..] if jokers + x == 2 => Self::OnePair,
|
||||||
_ => Self::HighCard,
|
_ => Self::HighCard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,10 +142,10 @@ impl Ord for Hand {
|
|||||||
match c {
|
match c {
|
||||||
Ordering::Equal => self
|
Ordering::Equal => self
|
||||||
.cards
|
.cards
|
||||||
.iter()
|
.into_iter()
|
||||||
.interleave(other.cards.iter())
|
.interleave(other.cards)
|
||||||
.tuples::<(_, _)>()
|
.tuples::<(_, _)>()
|
||||||
.find_map(|(a, b)| match a.cmp(b) {
|
.find_map(|(a, b)| match a.cmp(&b) {
|
||||||
Ordering::Equal => None,
|
Ordering::Equal => None,
|
||||||
x => Some(x),
|
x => Some(x),
|
||||||
})
|
})
|
||||||
@@ -185,7 +180,7 @@ fn parse_hand(input: &str) -> IResult<&str, Hand> {
|
|||||||
separated_pair(complete::alphanumeric1, complete::space1, complete::u32)(input)?;
|
separated_pair(complete::alphanumeric1, complete::space1, complete::u32)(input)?;
|
||||||
let cards = cards
|
let cards = cards
|
||||||
.chars()
|
.chars()
|
||||||
.filter_map(|c| c.to_string().parse().ok())
|
.filter_map(|c| c.try_into().ok())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("should work");
|
.expect("should work");
|
||||||
|
|||||||
Reference in New Issue
Block a user