day 2 and revised day 1 with advantage of DoubleEndedIterator
This commit is contained in:
parent
b2f78d33e7
commit
69b53f37a6
11 changed files with 2392 additions and 28 deletions
|
@ -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
91
src/bin/day1_revised.rs
Normal 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
144
src/bin/day2.rs
Normal 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(())
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
pub mod aoc;
|
||||
pub mod common;
|
||||
pub mod config;
|
||||
pub mod aoc;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue