day 2 and revised day 1 with advantage of DoubleEndedIterator

This commit is contained in:
RingOfStorms (Joshua Bell) 2023-12-02 01:00:41 -06:00
parent b2f78d33e7
commit 69b53f37a6
11 changed files with 2392 additions and 28 deletions

View file

@ -1,14 +1,24 @@
use aoc23::prelude::*;
fn d1_0(input: String) -> Result<usize> {
fn part1(input: String) -> Result<usize> {
let mut sum = 0;
for line in input.lines().into_iter() {
let chars = line.chars();
let first_num: usize = chars.clone().into_iter().skip_while(|c| !c.is_digit(10)).take(1)
let first_num: usize = chars
.clone()
.into_iter()
.skip_while(|c| !c.is_digit(10))
.take(1)
// This would get continuous digits: .take_while(|c| c.is_digit(10))
.collect::<String>().parse()?;
let last_num: usize =
chars.into_iter().rev().skip_while(|c| !c.is_digit(10)).take(1).collect::<String>().parse()?;
.collect::<String>()
.parse()?;
let last_num: usize = chars
.into_iter()
.rev()
.skip_while(|c| !c.is_digit(10))
.take(1)
.collect::<String>()
.parse()?;
let value = first_num * 10 + last_num;
sum += value;
}
@ -36,10 +46,11 @@ fn first_occurrence(str: &str, reversed: bool) -> Option<u32> {
if reversed {
for (i, char) in str.chars().rev().enumerate() {
let digit = char.to_digit(10).or_else(|| {
for num in 1 ..= 9 {
for num in 1..=9 {
if let Some(num_word) = num_to_word(num) {
if i + 1 >= num_word.len() &&
&str[str.len() - i - 1 .. str.len()][..num_word.len()] == num_word {
if i + 1 >= num_word.len()
&& &str[str.len() - i - 1..str.len()][..num_word.len()] == num_word
{
return Some(num);
}
}
@ -53,9 +64,9 @@ fn first_occurrence(str: &str, reversed: bool) -> Option<u32> {
} else {
for (i, char) in str.chars().enumerate() {
let digit = char.to_digit(10).or_else(|| {
for num in 1 ..= 9 {
for num in 1..=9 {
if let Some(num_word) = num_to_word(num) {
if i + 1 >= num_word.len() && &str[i + 1 - num_word.len() ..= i] == num_word {
if i + 1 >= num_word.len() && &str[i + 1 - num_word.len()..=i] == num_word {
return Some(num);
}
}
@ -70,15 +81,15 @@ fn first_occurrence(str: &str, reversed: bool) -> Option<u32> {
None
}
fn d1_1(input: String) -> Result<u32> {
fn part_2(input: String) -> Result<u32> {
let mut sum = 0;
for line in input.lines().into_iter() {
let first_num = first_occurrence(line, false).ok_or("No number found in line")?;
let last_num = first_occurrence(line, true).ok_or("No number found in line reversed")?;
let value = first_num * 10 + last_num;
sum += value;
println!("Line {line}: a:{first_num}|b:{last_num} = {value} ++ {sum}");
}
Ok(sum)
}
@ -87,8 +98,7 @@ async fn main() -> Result<()> {
println!("Day 1");
println!("=====");
let input = utils::aoc::get_puzzle_input(1).await?;
println!("Day 1, 0: {}", d1_0(input.clone())?);
println!("Day 1, 1: {}", d1_1(input.clone())?);
println!("part 1: {}", part1(input.clone())?);
println!("part 2: {}", part_2(input.clone())?);
Ok(())
}

91
src/bin/day1_revised.rs Normal file
View file

@ -0,0 +1,91 @@
use aoc23::prelude::*;
fn part1(input: String) -> Result<usize> {
let mut sum = 0;
for line in input.lines().into_iter() {
let chars = line.chars();
let first_num: usize = chars
.clone()
.into_iter()
.skip_while(|c| !c.is_digit(10))
.take(1)
// This would get continuous digits: .take_while(|c| c.is_digit(10))
.collect::<String>()
.parse()?;
let last_num: usize = chars
.into_iter()
.rev()
.skip_while(|c| !c.is_digit(10))
.take(1)
.collect::<String>()
.parse()?;
let value = first_num * 10 + last_num;
sum += value;
}
// println!("Answer: {}", sum);
Ok(sum)
}
fn part_2(input: String) -> Result<u32> {
let numbers: [(&'static str, u32); 9] = [
("one", 1),
("two", 2),
("three", 3),
("four", 4),
("five", 5),
("six", 6),
("seven", 7),
("eight", 8),
("nine", 9),
];
let mut sum = 0;
for line in input.lines().into_iter() {
let mut chars = line.chars();
let mut peek_index = 0;
let first_num = loop {
// I wanted this to work but peek advances the iterator anyways despite saying it
// doesn't. I don't understand
//
// ```
// if let Some(Some(peek_digit)) = chars.by_ref().peekable().peek().map(|c| c.to_digit(10))
// ```
if let Some(Some(peek_digit)) = line.chars().nth(peek_index).map(|c| c.to_digit(10)) {
break peek_digit;
}
if let Some((_, digit)) = numbers.iter().find(|num| chars.as_str().starts_with(num.0)) {
break *digit;
}
if chars.next().is_none() {
break 0;
}
peek_index += 1;
};
let last_num = loop {
if let Some((_, digit)) = numbers.iter().find(|num| chars.as_str().ends_with(num.0)) {
break *digit;
}
if let Some(last_char) = chars.next_back() {
if let Some(digit) = last_char.to_digit(10) {
break digit;
}
} else {
break first_num;
}
};
let value = first_num * 10 + last_num;
sum += value;
println!("Line {line}: a:{first_num}|b:{last_num} = {value} ++ {sum}");
}
Ok(sum)
}
#[tokio::main]
async fn main() -> Result<()> {
println!("Day 1");
println!("=====");
let input = utils::aoc::get_puzzle_input(1).await?;
println!("part 1: {}", part1(input.clone())?);
println!("part 2: {}", part_2(input.clone())?);
Ok(())
}

144
src/bin/day2.rs Normal file
View file

@ -0,0 +1,144 @@
use aoc23::prelude::*;
use std::{collections::HashMap, error::Error, fmt::Display, str::FromStr, string::ParseError};
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
enum Cube {
Blue,
Red,
Green,
}
impl FromStr for Cube {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"blue" => Ok(Cube::Blue),
"red" => Ok(Cube::Red),
"green" => Ok(Cube::Green),
_ => Err("asd".into()),
}
}
}
impl Cube {
pub fn as_str(&self) -> &str {
match self {
Cube::Blue => "blue",
Cube::Red => "red",
Cube::Green => "green",
}
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
struct Game {
id: u32,
sets: Vec<HashMap<Cube, u32>>,
}
impl FromStr for Game {
type Err = String;
// Game 1: 7 green, 4 blue, 3 red; 4 blue, 10 red, 1 green; 1 blue, 9 red Game 2:
// 2 red, 4 blue, 3 green; 5 green, 3 red, 1 blue; 3 green, 5 blue, 3 red Game 3:
// 12 red, 1 blue; 6 red, 2 green, 3 blue; 2 blue, 5 red, 3 green
fn from_str(line: &str) -> std::result::Result<Self, Self::Err> {
let parts: Vec<&str> = line.split(':').collect();
let id = parts[0]
.trim()
.split_whitespace()
.last()
.ok_or("no id")?
.parse::<u32>()
.ok()
.ok_or("not a number")?;
let sets = parts[1]
.split(';')
.map(|set| {
set.split(',')
.filter_map(|part| {
let mut iter = part.trim().split_whitespace();
let count = iter.next().unwrap().parse::<u32>().unwrap();
let color = iter.next().unwrap().parse::<Cube>().ok()?;
Some((color, count))
})
.collect()
})
.collect();
Ok(Game { id, sets })
}
}
fn part1(input: String) -> Result<impl Display> {
let games: Vec<Game> = input
.lines()
.into_iter()
.map(|l| l.parse().expect("failed to parse line"))
.collect();
let mut possible_game_id_sum = 0;
let limit_red = 12;
let limit_green = 13;
let limit_blue = 14;
for game in games {
let out_of_bounds = game.sets.iter().any(|set| match set.get(&Cube::Red) {
Some(red) => red > &limit_red,
None => false,
} || match set.get(&Cube::Green) {
Some(green) => green > &limit_green,
None => false,
} || match set.get(&Cube::Blue) {
Some(blue) => blue > &limit_blue,
None => false,
});
if !out_of_bounds {
// println!("Valid game id, {:?}", game);
possible_game_id_sum += game.id;
} else {
// println!("INVALID game {:?}", game);
}
}
Ok(possible_game_id_sum)
}
fn part2(input: String) -> Result<impl Display> {
let games: Vec<Game> = input
.lines()
.into_iter()
.map(|l| l.parse().expect("failed to parse line"))
.collect();
let mut power_sum = 0;
for game in games {
let mut maxes: HashMap<Cube, u32> = [(Cube::Red, 1), (Cube::Green, 1), (Cube::Blue, 1)]
.into_iter()
.collect();
for set in game.sets.iter() {
for (cube, &count) in set {
let max_count = maxes.get_mut(cube).ok_or("default missing")?;
*max_count = u32::max(*max_count, count);
}
}
let power = maxes.values().into_iter().fold(1u32, |acc, v| acc * v);
power_sum += power;
}
Ok(power_sum)
}
#[tokio::main]
async fn main() -> Result<()> {
println!("Day 1");
println!("=====");
let input = utils::aoc::get_puzzle_input(2).await?;
// ```
// let input = "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
// Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
// Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
// Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
// Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green"
// .to_owned();
// ```
println!("part 1: {}", part1(input.clone())?);
println!("part 2: {}", part2(input.clone())?);
Ok(())
}

View file

@ -3,12 +3,7 @@ pub mod utils;
pub mod prelude {
pub use super::utils::{
self,
common::{
BoxE,
Result,
SError,
SResult,
},
common::{BoxE, Result, SError, SResult},
config::get_config,
};
}

View file

@ -1,7 +1,4 @@
use std::{
env::var,
sync::OnceLock,
};
use std::{env::var, sync::OnceLock};
static CONFIG: OnceLock<Config> = OnceLock::new();
@ -9,13 +6,15 @@ pub struct Config {
pub aoc_session: String,
}
impl Config { }
impl Config {}
fn get_var(var_name: &str) -> String {
var(var_name).unwrap_or("".to_owned())
}
pub fn get_config() -> &'static Config {
let config = CONFIG.get_or_init(|| Config { aoc_session: get_var("AOC_SESSION") });
let config = CONFIG.get_or_init(|| Config {
aoc_session: get_var("AOC_SESSION"),
});
config
}

View file

@ -1,3 +1,3 @@
pub mod aoc;
pub mod common;
pub mod config;
pub mod aoc;