Skip to content

Commit

Permalink
minor day 3 optimizations: split parts 1 and 2 out so we don't do wor…
Browse files Browse the repository at this point in the history
…k for both in both parts
  • Loading branch information
kcaffrey committed Dec 8, 2023
1 parent 212aeac commit 649a069
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 99 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.

| Day | Part 1 | Part 2 |
| :---: | :---: | :---: |
| [Day 1](./src/bin/01.rs) | `33.9µs` | `36.0µs` |
| [Day 2](./src/bin/02.rs) | `41.5µs` | `40.8µs` |
| [Day 3](./src/bin/03.rs) | `116.5µs` | `115.9µs` |
| [Day 4](./src/bin/04.rs) | `49.4µs` | `49.7µs` |
| [Day 5](./src/bin/05.rs) | `20.9µs` | `24.1µs` |
| [Day 6](./src/bin/06.rs) | `202.0ns` | `100.0ns` |
| [Day 7](./src/bin/07.rs) | `93.6µs` | `92.6µs` |

**Total: 0.72ms**
| [Day 1](./src/bin/01.rs) | `28.7µs` | `34.6µs` |
| [Day 2](./src/bin/02.rs) | `41.7µs` | `40.8µs` |
| [Day 3](./src/bin/03.rs) | `84.3µs` | `99.7µs` |
| [Day 4](./src/bin/04.rs) | `49.0µs` | `50.7µs` |
| [Day 5](./src/bin/05.rs) | `20.7µs` | `24.3µs` |
| [Day 6](./src/bin/06.rs) | `203.0ns` | `102.0ns` |
| [Day 7](./src/bin/07.rs) | `97.6µs` | `92.8µs` |

**Total: 0.67ms**
<!--- benchmarking table --->

---
Expand Down
176 changes: 86 additions & 90 deletions src/bin/03.rs
Original file line number Diff line number Diff line change
@@ -1,109 +1,105 @@
use std::{collections::HashMap, str::FromStr};
use std::collections::HashMap;

advent_of_code::solution!(3);

pub fn part_one(input: &str) -> Option<u32> {
Some(
input
.parse::<Engine>()
.ok()?
.parts
.into_iter()
.map(|p| p.number)
.sum(),
)
let mut part_sum = 0;
let mut cur_num: Option<u32> = None;
let mut cur_start: Option<usize> = None;
let chars: Vec<Vec<char>> = input
.lines()
.map(|line| line.trim().chars().collect())
.collect();
// TODO: scan for gears instead, and only parse numbers when gears are found?
for (row, line) in chars.iter().enumerate() {
for (col, ch) in line.iter().copied().enumerate() {
let mut numeric = false;
if let Some(digit) = ch.to_digit(10) {
numeric = true;
cur_num = Some(cur_num.unwrap_or(0) * 10 + digit);
if cur_start.is_none() {
cur_start = Some(col);
}
}
if !numeric || col == line.len() - 1 {
if let Some((start, number)) = cur_start.zip(cur_num) {
let end = if numeric { col } else { col - 1 };
let mut symbol: Option<char> = None;
for i in row.saturating_sub(1)..=row.saturating_add(1) {
for j in start.saturating_sub(1)..=end.saturating_add(1) {
if let Some(&adj_ch) = chars.get(i).and_then(|l| l.get(j)) {
if !adj_ch.is_numeric() && adj_ch != '.' {
symbol = Some(adj_ch);
}
}
}
}
if symbol.is_some() {
part_sum += number;
}
}
cur_start = None;
cur_num = None;
}
}
}
Some(part_sum)
}

pub fn part_two(input: &str) -> Option<u32> {
Some(
input
.parse::<Engine>()
.ok()?
.gears
.into_iter()
.map(|gear| gear.ratio)
.sum(),
)
}

struct Part {
number: u32,
}

struct Gear {
ratio: u32,
adjacent_parts: usize,
}

struct Engine {
parts: Vec<Part>,
gears: Vec<Gear>,
}

struct ParseEngineErr;

impl FromStr for Engine {
type Err = ParseEngineErr;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts: Vec<Part> = Vec::new();
let mut gears: HashMap<(usize, usize), Gear> = HashMap::new();
let mut cur_num: Option<u32> = None;
let mut cur_start: Option<usize> = None;
let chars: Vec<Vec<char>> = s
.lines()
.map(|line| line.trim().chars().collect())
.collect();
for (row, line) in chars.iter().enumerate() {
for (col, ch) in line.iter().copied().enumerate() {
let mut numeric = false;
if let Some(digit) = ch.to_digit(10) {
numeric = true;
cur_num = Some(cur_num.unwrap_or(0) * 10 + digit);
if cur_start.is_none() {
cur_start = Some(col);
}
let mut gears: HashMap<(usize, usize), Gear> = HashMap::new();
let mut cur_num: Option<u32> = None;
let mut cur_start: Option<usize> = None;
let chars: Vec<Vec<char>> = input
.lines()
.map(|line| line.trim().chars().collect())
.collect();
for (row, line) in chars.iter().enumerate() {
for (col, ch) in line.iter().copied().enumerate() {
let mut numeric = false;
if let Some(digit) = ch.to_digit(10) {
numeric = true;
cur_num = Some(cur_num.unwrap_or(0) * 10 + digit);
if cur_start.is_none() {
cur_start = Some(col);
}
if !numeric || col == line.len() - 1 {
if let Some((start, number)) = cur_start.zip(cur_num) {
let end = if numeric { col } else { col - 1 };
let mut symbol: Option<char> = None;
let mut counted_gear_part = false;
for i in row.saturating_sub(1)..=row.saturating_add(1) {
for j in start.saturating_sub(1)..=end.saturating_add(1) {
if let Some(&adj_ch) = chars.get(i).and_then(|l| l.get(j)) {
if !adj_ch.is_numeric() && adj_ch != '.' {
symbol = Some(adj_ch);
}
if adj_ch == '*' && !counted_gear_part {
let gear = gears.entry((i, j)).or_insert(Gear {
ratio: 1,
adjacent_parts: 0,
});
gear.ratio *= number;
gear.adjacent_parts += 1;
counted_gear_part = true;
}
}
if !numeric || col == line.len() - 1 {
if let Some((start, number)) = cur_start.zip(cur_num) {
let end = if numeric { col } else { col - 1 };
let mut counted_gear_part = false;
for i in row.saturating_sub(1)..=row.saturating_add(1) {
for j in start.saturating_sub(1)..=end.saturating_add(1) {
if let Some(&adj_ch) = chars.get(i).and_then(|l| l.get(j)) {
if adj_ch == '*' && !counted_gear_part {
let gear = gears.entry((i, j)).or_insert(Gear {
ratio: 1,
adjacent_parts: 0,
});
gear.ratio *= number;
gear.adjacent_parts += 1;
counted_gear_part = true;
}
}
}
if symbol.is_some() {
parts.push(Part { number });
}
}
cur_start = None;
cur_num = None;
}
cur_start = None;
cur_num = None;
}
}
Ok(Engine {
parts,
gears: gears
.into_values()
.filter(|gear| gear.adjacent_parts >= 2)
.collect(),
})
}
Some(
gears
.into_values()
.filter_map(|gear| (gear.adjacent_parts == 2).then_some(gear.ratio))
.sum(),
)
}

struct Gear {
ratio: u32,
adjacent_parts: usize,
}

#[cfg(test)]
Expand Down

0 comments on commit 649a069

Please sign in to comment.