~sgeisenh/aoc2023

49a9bcf2b69af74e6c0ca3053a70ebcb737f5844 — Samuel Eisenhandler 6 months ago c3f1cd1
cargo clippy
M rust/day01/src/main.rs => rust/day01/src/main.rs +3 -3
@@ 20,7 20,7 @@ fn p2_calibration(line: &str) -> u32 {
        P2_PATTERNS.iter().enumerate().find_map(|(pidx, pattern)| {
            bytes[idx..]
                .starts_with(pattern.as_bytes())
                .then(|| pidx as u32)
                .then_some(pidx as u32)
        })
    };
    let first = rng.clone().find_map(find_pattern).unwrap() % 10;


@@ 34,11 34,11 @@ fn solve(input: &str, calibration: impl Fn(&str) -> u32) -> u32 {

fn main() {
    let p1_start_time = Instant::now();
    println!("Part 1: {}", solve(&INPUT, p1_calibration));
    println!("Part 1: {}", solve(INPUT, p1_calibration));
    let p1_elapsed = p1_start_time.elapsed();

    let p2_start_time = Instant::now();
    println!("Part 2: {}", solve(&INPUT, p2_calibration));
    println!("Part 2: {}", solve(INPUT, p2_calibration));
    let p2_elapsed = p2_start_time.elapsed();

    println!("Part 1 took {} ns", p1_elapsed.as_nanos());

A rust/day02/Cargo.lock => rust/day02/Cargo.lock +7 -0
@@ 0,0 1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "day02"
version = "0.1.0"

A rust/day02/Cargo.toml => rust/day02/Cargo.toml +4 -0
@@ 0,0 1,4 @@
[package]
name = "day02"
version = "0.1.0"
edition = "2021"

A rust/day02/src/main.rs => rust/day02/src/main.rs +118 -0
@@ 0,0 1,118 @@
use std::collections::HashMap;
use std::time::Instant;

const INPUT: &str = include_str!("../../../inputs/02.txt");

struct Cubes {
    quantity: usize,
    color: &'static str,
}

impl Cubes {
    fn from_str(s: &'static str) -> Self {
        let mut split = s.split(' ');
        let quantity = split.next().unwrap().parse().unwrap();
        let color = split.next().unwrap();
        Self { quantity, color }
    }
}

struct Game {
    idx: usize,
    subsets: Box<[Box<[Cubes]>]>,
}

impl Game {
    fn from_str(s: &'static str) -> Self {
        let mut splits = s.split(": ");
        let game_and_idx = splits.next().unwrap();
        let idx = game_and_idx
            .strip_prefix("Game ")
            .unwrap()
            .parse::<usize>()
            .unwrap();
        let subsets = splits
            .next()
            .unwrap()
            .split("; ")
            .map(|s| s.split(", ").map(Cubes::from_str).collect())
            .collect();
        Self { idx, subsets }
    }

    fn within_limits(&self, limits: &[Cubes]) -> bool {
        self.subsets.iter().all(|subset| {
            subset.iter().all(|cube| {
                limits
                    .iter()
                    .any(|limit| cube.color == limit.color && cube.quantity <= limit.quantity)
            })
        })
    }

    fn power(&self) -> usize {
        // Find the maximum quantity of cubes of each color in a subset across all subsets.
        // Then multiply the quantities of all colors together.
        self.subsets
            .iter()
            .fold(HashMap::new(), |mut acc, subset| {
                for cube in subset.iter() {
                    acc.entry(cube.color)
                        .and_modify(|e| {
                            if cube.quantity > *e {
                                *e = cube.quantity;
                            }
                        })
                        .or_insert(cube.quantity);
                }
                acc
            })
            .values()
            .product()
    }
}

fn p1(input: &'static str) -> usize {
    let games: Box<[Game]> = input.lines().map(str::trim).map(Game::from_str).collect();
    let limits = [
        Cubes {
            quantity: 12,
            color: "red",
        },
        Cubes {
            quantity: 13,
            color: "green",
        },
        Cubes {
            quantity: 14,
            color: "blue",
        },
    ];
    games
        .iter()
        .filter_map(|game| game.within_limits(&limits).then_some(game.idx))
        .sum()
}

fn p2(input: &'static str) -> usize {
    input
        .lines()
        .map(str::trim)
        .map(Game::from_str)
        .map(|game| game.power())
        .sum()
}

fn main() {
    let p1_start_time = Instant::now();
    println!("Part 1: {}", p1(INPUT));
    let p1_elapsed = p1_start_time.elapsed();

    let p2_start_time = Instant::now();
    println!("Part 2: {}", p2(INPUT));
    let p2_elapsed = p2_start_time.elapsed();

    println!("Part 1 took {} ns", p1_elapsed.as_nanos());
    println!("Part 2 took {} ns", p2_elapsed.as_nanos());
    println!("Total time: {} ns", (p1_elapsed + p2_elapsed).as_nanos());
}

A rust/day04/Cargo.lock => rust/day04/Cargo.lock +7 -0
@@ 0,0 1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "day04"
version = "0.1.0"

A rust/day04/Cargo.toml => rust/day04/Cargo.toml +4 -0
@@ 0,0 1,4 @@
[package]
name = "day04"
version = "0.1.0"
edition = "2021"

A rust/day04/src/main.rs => rust/day04/src/main.rs +94 -0
@@ 0,0 1,94 @@
use std::time::Instant;

const INPUT: &str = include_str!("../../../inputs/04.txt");

#[derive(Debug)]
struct Cards {
    winning: Box<[usize]>,
    have: Box<[usize]>,
}

impl Cards {
    fn matches(&self) -> usize {
        self.winning
            .iter()
            .filter(|&c| self.have.contains(c))
            .count()
    }

    fn score(&self) -> usize {
        match self.matches() {
            0 => 0,
            n => 2_usize.pow((n - 1) as u32),
        }
    }
}

fn parse_cards(input: &'static str) -> Box<[Cards]> {
    input
        .lines()
        .map(str::trim)
        .map(|line| {
            let mut splits = line.split(": ");
            splits.next().unwrap();
            let rest = splits.next().unwrap();
            let mut winning_and_have = rest.split(" | ");
            let winning = winning_and_have
                .next()
                .unwrap()
                .split_whitespace()
                .map(|s| s.parse().unwrap())
                .collect();
            let have = winning_and_have
                .next()
                .unwrap()
                .split_whitespace()
                .map(|s| s.parse().unwrap())
                .collect();
            Cards { winning, have }
        })
        .collect()
}

fn p1(input: &'static str) -> usize {
    parse_cards(input).iter().map(Cards::score).sum()
}

fn p2(input: &'static str) -> usize {
    let cards = parse_cards(input);
    let mut memo: Box<[Option<usize>]> = vec![None; cards.len()].into_boxed_slice();
    fn score(cards: &Box<[Cards]>, idx: usize, memo: &mut [Option<usize>]) -> usize {
        if idx > cards.len() {
            return 0;
        }
        if let Some(s) = memo[idx] {
            return s;
        }
        let matches = cards[idx].matches();
        let result = 1
            + (idx + 1..idx + matches + 1)
                .map(|i| score(cards, i, memo))
                .sum::<usize>();
        memo[idx] = Some(result);
        result
    }
    cards
        .iter()
        .enumerate()
        .map(|(i, _)| score(&cards, i, &mut memo))
        .sum()
}

fn main() {
    let p1_start_time = Instant::now();
    println!("Part 1: {}", p1(INPUT));
    let p1_elapsed = p1_start_time.elapsed();

    let p2_start_time = Instant::now();
    println!("Part 2: {}", p2(INPUT));
    let p2_elapsed = p2_start_time.elapsed();

    println!("Part 1 took {} ns", p1_elapsed.as_nanos());
    println!("Part 2 took {} ns", p2_elapsed.as_nanos());
    println!("Total time: {} ns", (p1_elapsed + p2_elapsed).as_nanos());
}