Files
adventofcode2024/day_03/src/main.rs

161 lines
4.5 KiB
Rust
Raw Normal View History

2025-05-26 11:59:58 -05:00
//https://adventofcode.com/2024/day/3
//
// Run command: cargo run ./input.txt
use core::ops::Mul;
use core::panic;
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::{BufReader, prelude::*};
use std::str::FromStr;
#[derive(Eq, PartialEq, Clone, Debug)]
enum Token {
Text(String),
Number(String),
Comma,
OpenParenthesis,
CloseParenthesis,
SpecialChar(char),
Whitespace,
NewLine,
}
fn is_mul_text_token(text_token: &str) -> bool {
if text_token.ends_with("mul") {
true
} else {
false
}
}
fn tokenize(input_str: String) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
let mut chars = input_str.chars().peekable();
while let Some(c) = chars.next() {
let token = match c {
// Text Numeric
_ if char::is_alphabetic(c) => {
let mut text = c.to_string();
while let Some(alphabetic_char) = chars.next_if(|p| p.is_alphabetic()) {
text.push(alphabetic_char)
}
Token::Text(text)
}
// Numbers
_ if char::is_numeric(c) => {
let mut number = c.to_string();
while let Some(numeric_char) = chars.next_if(|p| p.is_numeric()) {
number.push(numeric_char)
}
Token::Number(number)
}
// whitespace
_ if char::is_whitespace(c) => {
while chars.next_if(|p| p.is_whitespace()).is_some() {}
Token::Whitespace
}
_ if c == ',' => Token::Comma,
_ if c == '(' => Token::OpenParenthesis,
_ if c == ')' => Token::CloseParenthesis,
_ if c == '\n' => Token::NewLine,
_ => Token::SpecialChar(c),
};
tokens.push(token);
}
tokens
}
#[derive(Debug)]
struct MulOp {
lhs: String,
rhs: String,
}
impl MulOp {
pub fn multiple<T>(&self) -> Result<<T as Mul>::Output, <T as FromStr>::Err>
where
T: FromStr + Mul,
{
let lhs: T = self.lhs.parse()?;
let rhs: T = self.rhs.parse()?;
Ok(lhs * rhs)
}
}
fn parse_tokens(tokens: &Vec<Token>) -> Vec<MulOp> {
let mut tokens = tokens.iter().peekable();
let mut muls: Vec<MulOp> = Vec::new();
while let Some(token) = tokens.next() {
match token {
Token::Text(t) => {
if is_mul_text_token(t) {
// Take OpenParenthesis or continue
if !tokens.next_if_eq(&&Token::OpenParenthesis).is_some() {
continue;
}
// Take Number or continue
let lhs = if let Some(Token::Number(lhs)) =
tokens.next_if(|v| matches!(v, Token::Number(_)))
{
lhs.to_owned()
} else {
continue;
};
// Take Comma or continue
if !tokens.next_if_eq(&&Token::Comma).is_some() {
continue;
}
// Take Number or continue
let rhs = if let Some(Token::Number(rhs)) =
tokens.next_if(|v: &&Token| matches!(v, Token::Number(_)))
{
rhs.to_owned()
} else {
continue;
};
// Take CloseParenthesis or continue
if !tokens.next_if_eq(&&Token::CloseParenthesis).is_some() {
continue;
}
muls.push(MulOp { lhs, rhs });
}
}
_ => (),
}
}
muls
}
fn main() -> Result<(), Box<dyn Error>> {
// Handle command input
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
panic!(
"{} must be run with a single argument of files name!",
&args[0]
)
}
let input_file_string = &args[1];
let input_buffer = BufReader::new(File::open(input_file_string)?);
let mut tokens: Vec<Token> = Vec::new();
for line in input_buffer.lines() {
tokens.append(&mut tokenize(line? + "\n"));
}
//println!("{:?}", tokens);
let muls = parse_tokens(&tokens);
//println!("{:?}", muls);
let mut accumulator: i64 = 0;
for mul in muls {
accumulator += mul.multiple::<i64>()?;
}
println!("Answer: {}", accumulator);
Ok(())
}