Skip to content

Commit

Permalink
docs for all days & refactored 07 to use templates and a trait
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
  • Loading branch information
henryiii committed Dec 15, 2023
1 parent 1bddc61 commit b16c7bf
Show file tree
Hide file tree
Showing 20 changed files with 395 additions and 265 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ year to learn Rust. I'm not trying to be fast and place on the leaderboards
(which also require working at midnight, which I'm mostly not interested in
doing), I'm trying to be somewhat elegant and learn new things in Rust.

I highly recommend loading this up in a good editor, like Visual Studio Code or
VIM with the ALE plugin. It will add type information to all inferred types,
autocomplete, show documentation, etc.

## Formatting and linting

Use:
Expand Down Expand Up @@ -39,7 +43,7 @@ cargo run -r --bin 01

## Docs

Some problems have docs, build with:
You can build with:

```bash
cargo docs --no-deps
Expand Down Expand Up @@ -72,4 +76,4 @@ A few of the crates I'm using or have used:
- `strum`: Powerful enum tools like conversion with strings & iteration over enums
- `indexmap`: Ordered map

I added some documentation to 13 to try `cargo doc`.
I added fairly extensive docs to `13` to try `cargo doc`.
17 changes: 17 additions & 0 deletions src/bin/01.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
#!/usr/bin/env cargo -Zscript
/*!
# 2023 Day 1 - Number line
<https://adventofcode.com/2023/day/1>
This is a simple introduction to Rust. I'm doing some functional style
processing to find the first and last digits. I originally used a line iterator
(see history), but simplified it to reading the whole file for most examples, as
Advent of Code input will always fit in memory. I left the original line
iterator for Day 1b (also in Day 3, which is a bit more complex).
To keep this simple as possible, I've put 1b in a separate file. Most days
compute everything in one.
Day 1 is also the only day that supports Rust nightlies' experimental script
mode (`cargo script`).
*/

fn number_line(line: &str) -> u32 {
let mut chars = line.chars().filter_map(|c| c.to_digit(10));
Expand Down
8 changes: 8 additions & 0 deletions src/bin/01b.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#!/usr/bin/env cargo -Zscript

/*!
# 2023 Day 1 - Number line
<https://adventofcode.com/2023/day/1>
This is part 2. This could probably use `strum`, but I went with simple and performant.
*/

use std::io::prelude::*;

const NUMS: &[&str] = &[
Expand Down
62 changes: 40 additions & 22 deletions src/bin/02.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
/*!
# 2023 Day 2 - Colored balls
<https://adventofcode.com/2023/day/2>
This is a nice introduction to custom structs. I originally used pure Rust (see
history), but added some nice utilities from `derive_more` and `itertools` to
simplify (pretty much fully replace) the impl blocks. I'm also implementing the
`FromStr` trait to convert from a string to my Meas struct.
I'm also doing some error handling here (see history for different version, also
with error hanlding). It was rather forced on me by the `FromStr` trait, but I
think it's a good idea to get used to it instead of simply unwrapping all the
time. Which I'm going to most of the time, since these are controlled inputs.
I'm currently not using `anyhow` and `thiserror`, but might in the future. This
would make error handling and messages better, and avoid the
`.or(Err(FailedReadError))`.
*/

use derive_more::{Add, Constructor};
use itertools::Itertools;
use std::str::FromStr;

/// This is an error for my `FromStr` implementation.
#[derive(Debug, Clone)]
struct FailedReadError;

/// This is a measurement of balls, with the number of each color present.
#[derive(Default, Add, Constructor)]
struct Meas {
red: u32,
Expand All @@ -15,30 +37,33 @@ struct Meas {
impl FromStr for Meas {
type Err = FailedReadError;

/// This accepts strings of the form `"1 red, 2 green, 3 blue"` and adds
/// them together.
fn from_str(meas: &str) -> Result<Self, Self::Err> {
match meas.split_ascii_whitespace().collect_tuple() {
Some((val, "red")) => Ok(Self::new(val.parse().unwrap(), 0, 0)),
Some((val, "green")) => Ok(Self::new(0, val.parse().unwrap(), 0)),
Some((val, "blue")) => Ok(Self::new(0, 0, val.parse().unwrap())),
_ => Err(FailedReadError),
}
meas.trim().split(',').try_fold(Self::default(), |acc, x| {
let (val, color) = x
.split_ascii_whitespace()
.collect_tuple()
.ok_or(FailedReadError)?;
let val: u32 = val.parse().or(Err(FailedReadError))?;
let current = match color {
"red" => Self::new(val, 0, 0),
"green" => Self::new(0, val, 0),
"blue" => Self::new(0, 0, val),
_ => Err(FailedReadError)?,
};
Ok(acc + current)
})
}
}

fn make_meas(meas_str: &str) -> Meas {
meas_str
.trim()
.split(", ")
.fold(Meas::default(), |acc, x| acc + x.parse().unwrap())
}

fn measurements(line: &str) -> (u32, Vec<Meas>) {
let split: Vec<&str> = line.split(':').collect();
assert_eq!(split.len(), 2);
let game_str = split[0];
let game_number: u32 = game_str.strip_prefix("Game ").unwrap().parse().unwrap();
let results = split[1].split(';');
let meas = results.map(make_meas).collect();
let meas = results.map(|x| x.parse().unwrap()).collect();
(game_number, meas)
}

Expand All @@ -50,14 +75,7 @@ fn valid_measurements(max: &Meas, all_meas: &[Meas]) -> bool {

fn accumulator(acc: u32, line: &str) -> u32 {
let (game_number, all_meas) = measurements(line);
if valid_measurements(
&Meas {
red: 12,
green: 13,
blue: 14,
},
&all_meas[..],
) {
if valid_measurements(&Meas::new(12, 13, 14), &all_meas) {
acc + game_number
} else {
acc
Expand Down
20 changes: 16 additions & 4 deletions src/bin/03.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
/*!
# 2023 Day 3 - Number grid
<https://adventofcode.com/2023/day/3>
This creates a grid of numbers and gears, stored as pairs. It uses a buffered
file reader, which reads the file a line at a time (not required, as the file
isn't that large, but interesting). The implentation is mostly in the struct's
impl block. The struct is mostly just for passing values around together,
though.
*/

use std::io::prelude::*;

const fn adjacent(x: usize, y: usize, cx: usize, cy: usize, sz: usize) -> bool {
(y == cy || y == cy + 1 || y + 1 == cy) && x <= cx + 1 && cx < x + 1 + sz
}

struct NumberGrid {
chars: Vec<(usize, usize)>,
gears: Vec<(usize, usize)>,
numbers: Vec<(usize, usize, usize, u32)>,
}

const fn adjacent(x: usize, y: usize, cx: usize, cy: usize, sz: usize) -> bool {
(y == cy || y == cy + 1 || y + 1 == cy) && x <= cx + 1 && cx < x + 1 + sz
}

impl NumberGrid {
fn from_lines(lines: impl Iterator<Item = String>) -> Self {
let mut numbers = Vec::new();
Expand Down
18 changes: 12 additions & 6 deletions src/bin/04.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
use std::io::prelude::*;
/*!
# 2023 Day 4 - Winning cards
<https://adventofcode.com/2023/day/4>
This is a simple counting problem with cards holding numbers. The solution
covers most of what 3 did, custom struct and error, `FromStr`, etc.
*/

use std::str::FromStr;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -60,9 +68,8 @@ fn card_count(cards: &[Card]) -> Vec<usize> {
}

fn main() {
let file = std::fs::File::open("input/04.txt").unwrap();
let lines = std::io::BufReader::new(file).lines();
let cards: Vec<Card> = lines.map(|x| x.unwrap().parse().unwrap()).collect();
let text = std::fs::read_to_string("input/04.txt").unwrap();
let cards: Vec<Card> = text.lines().map(|x| x.parse().unwrap()).collect();
let score: u32 = cards.iter().map(Card::score).sum();
println!("Score: {score}");
let count: usize = card_count(&cards).iter().sum();
Expand All @@ -83,8 +90,7 @@ Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11";

#[test]
fn test_03() {
let lines = INPUT.lines().map(|x| x.to_string());
let cards: Vec<Card> = lines.map(|x| x.parse().unwrap()).collect();
let cards: Vec<Card> = INPUT.lines().map(|x| x.parse().unwrap()).collect();
assert_eq!(cards.len(), 6);
assert_eq!(cards[0].winning.len(), 5);
assert_eq!(cards[0].numbers.len(), 8);
Expand Down
46 changes: 38 additions & 8 deletions src/bin/05.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
/*!
# 2023 Day 5 - Mapping ranges
<https://adventofcode.com/2023/day/5>
This computes the answer the brute-force way. It's a bit slow, so I've added a
progress bar (using the `progressbar` feature, enabled by default). I've also
used the built-in `Range` instead of a custom one (see history). I'm also using
the `derive_new` crate to add new functions with default values to structs.
I'm also using `itertools` to get a nice tuple conversion.
*/

#[cfg(feature = "progressbar")]
use indicatif::ProgressIterator;

use derive_new::new;
use itertools::Itertools;

use std::str::FromStr;

type Range = std::ops::Range<u64>;

#[derive(Debug, new)]
struct Mapper {
range: Range,
to: u64,
Expand All @@ -20,6 +38,25 @@ impl Mapper {
}
}

impl FromStr for Mapper {
type Err = std::io::Error;

fn from_str(line: &str) -> Result<Self, Self::Err> {
if let Some((to, from, size)) = line
.split_whitespace()
.filter_map(|x| x.parse::<u64>().ok())
.collect_tuple()
{
Ok(Self::new(from..from + size, to))
} else {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Invalid line: {line}"),
))
}
}
}

#[derive(new)]
struct Mappers {
#[new(default)]
Expand Down Expand Up @@ -74,14 +111,7 @@ fn read<'a>(lines: impl Iterator<Item = &'a str>) -> (Vec<u64>, AllMappers) {
if line.is_empty() {
break;
}
let mut parts = line.split_whitespace();
let to = parts.next().unwrap().parse::<u64>().unwrap();
let from = parts.next().unwrap().parse::<u64>().unwrap();
let size = parts.next().unwrap().parse::<u64>().unwrap();
mappers.mappers.push(Mapper {
range: (from..from + size),
to,
});
mappers.mappers.push(line.parse().unwrap());
}
all_mappers.mappers.push(mappers);
}
Expand Down
10 changes: 10 additions & 0 deletions src/bin/06.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/*!
# 2023 Day 6 - Races
<https://adventofcode.com/2023/day/6>
This continus to use a few helpers (as always with the first 10 days or so,
check history for pure implementations). I'm not implenting `FromStr` since
races are recorded vertically.
*/

use derive_more::Constructor;
use itertools::Itertools;

Expand Down
Loading

0 comments on commit b16c7bf

Please sign in to comment.