day 1
This commit is contained in:
commit
b2f78d33e7
12 changed files with 3245 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.env.local
|
4
.rtx.toml
Normal file
4
.rtx.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
env_file = ".env.local"
|
||||
|
||||
[env]
|
||||
# AOC_SESSION = "set in .env.local"
|
2010
Cargo.lock
generated
Normal file
2010
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
26
Cargo.toml
Normal file
26
Cargo.toml
Normal file
|
@ -0,0 +1,26 @@
|
|||
[package]
|
||||
name = "aoc23"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-recursion = "1.0.5"
|
||||
expanduser = "1.2.2"
|
||||
futures = "0.3"
|
||||
regex = "1.9"
|
||||
reqwest = { version = "0.11", features = [
|
||||
"json",
|
||||
"gzip",
|
||||
"brotli",
|
||||
"deflate",
|
||||
"stream",
|
||||
"cookies",
|
||||
] }
|
||||
reqwest-middleware = "0.2"
|
||||
reqwest-retry = "0.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tiktoken-rs = "0.5"
|
||||
tokio = { version = "1", features = ["full"] }
|
1000
aoc_puzzle_cache/day_1
Normal file
1000
aoc_puzzle_cache/day_1
Normal file
File diff suppressed because it is too large
Load diff
6
readme.md
Normal file
6
readme.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# AOC 23
|
||||
|
||||
# Day 1 Trebuchet?!
|
||||
|
||||
`cr --bin d1_0`
|
||||
|
94
src/bin/day1.rs
Normal file
94
src/bin/day1.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use aoc23::prelude::*;
|
||||
|
||||
fn d1_0(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 num_to_word(num: u32) -> Option<&'static str> {
|
||||
match num {
|
||||
1 => Some("one"),
|
||||
2 => Some("two"),
|
||||
3 => Some("three"),
|
||||
4 => Some("four"),
|
||||
5 => Some("five"),
|
||||
6 => Some("six"),
|
||||
7 => Some("seven"),
|
||||
8 => Some("eight"),
|
||||
9 => Some("nine"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
return Some(num);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
if digit.is_some() {
|
||||
return digit;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i, char) in str.chars().enumerate() {
|
||||
let digit = char.to_digit(10).or_else(|| {
|
||||
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 {
|
||||
return Some(num);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
if digit.is_some() {
|
||||
return digit;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn d1_1(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;
|
||||
}
|
||||
|
||||
Ok(sum)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
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())?);
|
||||
Ok(())
|
||||
}
|
14
src/lib.rs
Normal file
14
src/lib.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
pub mod utils;
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::utils::{
|
||||
self,
|
||||
common::{
|
||||
BoxE,
|
||||
Result,
|
||||
SError,
|
||||
SResult,
|
||||
},
|
||||
config::get_config,
|
||||
};
|
||||
}
|
61
src/utils/aoc.rs
Normal file
61
src/utils/aoc.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use crate::prelude::*;
|
||||
use reqwest::{
|
||||
header::{self, COOKIE},
|
||||
Client,
|
||||
};
|
||||
use reqwest_middleware::{ClientBuilder as MiddlewareClientBuilder, ClientWithMiddleware};
|
||||
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
static AOC_PUZZLE_INPUT_CACHE: &str = "aoc_puzzle_cache";
|
||||
|
||||
pub async fn get_puzzle_input(day: i8) -> Result<String> {
|
||||
let file_name = format!("day_{}", day);
|
||||
let cache_path = PathBuf::from(AOC_PUZZLE_INPUT_CACHE).join(file_name);
|
||||
if cache_path.exists() {
|
||||
// Read from the cache file
|
||||
let mut cache_file = File::open(cache_path)?;
|
||||
let mut contents = String::new();
|
||||
cache_file.read_to_string(&mut contents)?;
|
||||
Ok(contents)
|
||||
} else {
|
||||
// Fetch the content from the URL
|
||||
let response = aoc_client()
|
||||
.await?
|
||||
.get(format!("https://adventofcode.com/2023/day/{}/input", day))
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
// Cache the content to a file Ensure the cache directory exists
|
||||
fs::create_dir_all(AOC_PUZZLE_INPUT_CACHE)?;
|
||||
let mut cache_file = File::create(cache_path)?;
|
||||
cache_file.write_all(response.as_bytes())?;
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn aoc_client() -> Result<ClientWithMiddleware> {
|
||||
let session = &get_config().aoc_session;
|
||||
let mut session_cookie = header::HeaderValue::from_str(&format!("session={}", session))
|
||||
.expect("failed to create header with api_key.");
|
||||
session_cookie.set_sensitive(true);
|
||||
let mut headers = header::HeaderMap::new();
|
||||
headers.insert(COOKIE, session_cookie);
|
||||
let client = Client::builder()
|
||||
.default_headers(headers)
|
||||
.gzip(true)
|
||||
.brotli(true)
|
||||
.deflate(true)
|
||||
.https_only(false)
|
||||
.build()?;
|
||||
let client = MiddlewareClientBuilder::new(client)
|
||||
.with(RetryTransientMiddleware::new_with_policy(
|
||||
ExponentialBackoff::builder().build_with_max_retries(2),
|
||||
))
|
||||
.build();
|
||||
Ok(client)
|
||||
}
|
4
src/utils/common.rs
Normal file
4
src/utils/common.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub type SResult<T, R> = std::result::Result<T, R>;
|
||||
pub type SError = dyn std::error::Error;
|
||||
pub type BoxE = Box<SError>;
|
||||
pub type Result<T> = SResult<T, BoxE>;
|
21
src/utils/config.rs
Normal file
21
src/utils/config.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use std::{
|
||||
env::var,
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
static CONFIG: OnceLock<Config> = OnceLock::new();
|
||||
|
||||
pub struct Config {
|
||||
pub aoc_session: String,
|
||||
}
|
||||
|
||||
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") });
|
||||
config
|
||||
}
|
3
src/utils/mod.rs
Normal file
3
src/utils/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod common;
|
||||
pub mod config;
|
||||
pub mod aoc;
|
Loading…
Add table
Add a link
Reference in a new issue