Compare commits

...

10 commits

Author SHA1 Message Date
Daniel Flanagan 6f1199f3d2
Parsing successfully 2023-12-12 10:43:43 -06:00
Daniel Flanagan 397a95f2dc
Pause to go touch grass 2023-12-09 15:00:30 -06:00
Daniel Flanagan 99b0fd06be
Day 4 part 2 brute force 2023-12-09 11:34:19 -06:00
Daniel Flanagan 02e78aea8f
Day 4 part 1 2023-12-09 10:52:38 -06:00
Daniel Flanagan 907385eb70
Day 3 part 2 2023-12-09 09:43:46 -06:00
Daniel Flanagan e02605974a
Day 3 part 1 2023-12-08 14:46:36 -06:00
Daniel Flanagan 6dc64c8477
Dedupe test input 2023-12-02 15:52:00 -06:00
Daniel Flanagan 5631415039
Cleanup 2023-12-02 15:50:21 -06:00
Daniel Flanagan c89d892343
Part 2 2023-12-02 15:18:20 -06:00
Daniel Flanagan 14a77cd34f
Day 2 2023-12-02 15:07:19 -06:00
8 changed files with 553 additions and 2 deletions

25
2023/rust/Cargo.lock generated
View file

@ -5,3 +5,28 @@ version = 3
[[package]]
name = "aoc2023"
version = "1.0.0"
dependencies = [
"nom",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]

View file

@ -103,3 +103,4 @@ name = "day25"
path = "src/day25.rs"
[dependencies]
nom = "7.1.3"

108
2023/rust/src/day2.rs Normal file
View file

@ -0,0 +1,108 @@
mod prelude;
extern crate nom;
use crate::prelude::*;
use nom::{
branch::alt,
bytes::complete::{tag, take_while},
combinator::{map_res, value},
multi::separated_list0,
IResult,
};
use std::cmp::max;
fn main() {
Day2::show(day_input(2), day_input(2))
}
#[derive(Debug, PartialEq, Clone)]
enum Reveal {
Red(usize),
Green(usize),
Blue(usize),
}
#[derive(Debug, PartialEq)]
struct Game {
id: usize,
reveals: Vec<Reveal>,
}
// parser
fn int(input: &str) -> IResult<&str, usize> {
map_res(take_while(char::is_numeric), str::parse)(input)
}
fn reveal(input: &str) -> IResult<&str, Reveal> {
let (input, num) = int(input)?;
alt((
value(Reveal::Red(num), tag(" red")),
value(Reveal::Green(num), tag(" green")),
value(Reveal::Blue(num), tag(" blue")),
))(input)
}
fn game(input: &str) -> IResult<&str, Game> {
let (input, id) = int(tag("Game ")(input)?.0)?;
let (input, reveals) =
separated_list0(alt((tag(", "), tag("; "))), reveal)(tag(": ")(input)?.0)?;
Ok((input, Game { id, reveals }))
}
impl Game {
fn possible_power(&self) -> usize {
// TODO: probably the last place to cleanup nicely
let mut factors: [usize; 3] = [0, 0, 0];
for r in &self.reveals {
match r {
Reveal::Red(n) => factors[0] = max(*n, factors[0]),
Reveal::Green(n) => factors[1] = max(*n, factors[1]),
Reveal::Blue(n) => factors[2] = max(*n, factors[2]),
}
}
factors.iter().fold(1, |a, b| a * b)
}
}
struct Day2 {}
impl AoCSolution for Day2 {
type Input = String;
type Solution = usize;
fn part1(input: Self::Input) -> Self::Solution {
let valid = |r: &Reveal| match r {
Reveal::Red(n) => *n <= 12,
Reveal::Green(n) => *n <= 13,
Reveal::Blue(n) => *n <= 14,
};
input
.lines()
.map(|s| game(s).unwrap().1)
.filter(|g| g.reveals.iter().all(valid))
.map(|g| g.id)
.sum()
}
fn part2(input: Self::Input) -> Self::Solution {
input
.lines()
.map(|s| game(s).unwrap().1.possible_power())
.sum()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#"Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green"#;
assert_eq!(Day2::part1(input.into()), 8);
assert_eq!(Day2::part2(input.into()), 2286);
}
}

123
2023/rust/src/day3.rs Normal file
View file

@ -0,0 +1,123 @@
mod prelude;
use std::{collections::HashSet, iter::FromIterator};
use crate::prelude::*;
fn main() {
Day3::show(day_input(3), day_input(3))
}
struct Day3 {}
impl Day3 {
fn adjacent_nums(chargrid: &Vec<Vec<char>>, y: usize, x: usize) -> Vec<usize> {
let mut result = vec![];
let h = chargrid.len();
let w = chargrid[0].len();
let miny = (y - 1).clamp(0, h);
let minx = (x - 1).clamp(0, w);
let maxy = (y + 1).clamp(0, h);
let maxx = (x + 1).clamp(0, w);
let mut scanned: HashSet<(usize, usize)> = HashSet::new();
println!("symbol at {y} {x}: {}", chargrid[y][x]);
for y in miny..=maxy {
for x in minx..=maxx {
if scanned.contains(&(y, x)) {
continue;
}
if chargrid[y][x].is_digit(10) {
println!("{y} {x} {}", chargrid[y][x]);
scanned.insert((y, x));
let mut rx = x;
let mut lx = x;
for rrx in (x + 1)..w {
if !chargrid[y][rrx].is_digit(10) {
break;
}
rx = rrx;
scanned.insert((y, rx));
}
for llx in (0..=(x - 1)).rev() {
if !chargrid[y][llx].is_digit(10) {
break;
}
lx = llx;
scanned.insert((y, lx));
}
let ns = String::from_iter(&chargrid[y][lx..=rx]);
println!("ns: {}", ns);
if let Ok(n) = ns.parse() {
result.push(n);
println!("FOUND: {n}");
}
}
}
}
result
}
}
impl AoCSolution for Day3 {
type Input = String;
type Solution = usize;
fn part1(input: Self::Input) -> Self::Solution {
let mut result = 0;
let chargrid: Vec<Vec<char>> = input.lines().map(|s| s.chars().collect()).collect();
println!("{chargrid:?}");
for y in 0..chargrid.len() {
let line = &chargrid[y];
for x in 0..line.len() {
let c = line[x];
if !c.is_digit(10) && c != '.' {
result += Self::adjacent_nums(&chargrid, y, x).iter().sum::<usize>();
}
}
}
result
}
fn part2(input: Self::Input) -> Self::Solution {
let mut result = 0;
let chargrid: Vec<Vec<char>> = input.lines().map(|s| s.chars().collect()).collect();
println!("{chargrid:?}");
for y in 0..chargrid.len() {
let line = &chargrid[y];
for x in 0..line.len() {
let c = line[x];
if c == '*' {
let nums = Self::adjacent_nums(&chargrid, y, x);
println!("nums: {nums:?}");
if nums.len() == 2 {
result += nums[0] * nums[1];
}
}
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#"467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598.."#;
println!("input:\n{input}");
assert_eq!(Day3::part1(input.into()), 4361);
assert_eq!(Day3::part2(input.into()), 467835);
}
}

109
2023/rust/src/day4.rs Normal file
View file

@ -0,0 +1,109 @@
mod prelude;
extern crate nom;
use nom::{
branch::alt,
bytes::complete::{tag, take_while},
character::complete::space1,
combinator::{map_res, value},
multi::separated_list0,
sequence::tuple,
IResult,
};
use std::{
collections::{HashMap, HashSet},
iter::FromIterator,
};
use crate::prelude::*;
fn main() {
Day4::show(day_input(4), day_input(4))
}
#[derive(Debug)]
struct Card {
id: usize,
winning: HashSet<usize>,
has: Vec<usize>,
}
impl Card {
fn num_winning(&self) -> usize {
self.has.iter().filter(|n| self.winning.contains(n)).count() as usize
}
fn points(&self) -> usize {
let n = self.num_winning();
if n >= 1 {
2_u64.pow((n - 1) as u32) as usize
} else {
0
}
}
}
// parser
fn int(input: &str) -> IResult<&str, usize> {
map_res(take_while(char::is_numeric), str::parse)(input)
}
fn card(input: &str) -> IResult<&str, Card> {
let (input, id) = int(tuple((tag("Card"), space1))(input)?.0)?;
let (input, winning_list) = separated_list0(space1, int)(tuple((tag(":"), space1))(input)?.0)?;
let (input, has) = separated_list0(space1, int)(tuple((space1, tag("|"), space1))(input)?.0)?;
let winning = HashSet::from_iter(winning_list);
Ok((input, Card { id, winning, has }))
}
struct Day4 {}
impl Day4 {}
impl AoCSolution for Day4 {
type Input = String;
type Solution = usize;
fn part1(input: Self::Input) -> Self::Solution {
input.lines().map(|s| card(s).unwrap().1.points()).sum()
}
fn part2(input: Self::Input) -> Self::Solution {
let mut result = 0;
let mut copies: HashMap<usize, usize> = HashMap::new();
input.lines().for_each(|s| {
let card = card(s).unwrap().1;
println!("{}, cur result: {}", card.id, result);
let mut num_copies = 1;
if let Some(extra_copies) = copies.get(&card.id) {
num_copies += extra_copies;
}
for _ in 1..=num_copies {
for i in card.id..=(card.id + card.num_winning()) {
if let Some(n) = copies.get_mut(&i) {
*n += 1;
} else {
copies.insert(i, 1);
}
}
}
result += num_copies
});
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#"Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11"#;
println!("input:\n{input}");
assert_eq!(Day4::part1(input.into()), 13);
assert_eq!(Day4::part2(input.into()), 30);
}
}

155
2023/rust/src/day5.rs Normal file
View file

@ -0,0 +1,155 @@
mod prelude;
use std::collections::HashMap;
extern crate nom;
use nom::{
branch::alt,
bytes::complete::{tag, take_until, take_while},
character::complete::{line_ending, newline, space1},
combinator::{map_res, value},
multi::separated_list0,
sequence::tuple,
IResult,
};
use crate::prelude::*;
fn main() {
Day5::show(day_input(5), day_input(5))
}
#[derive(Hash, Debug, PartialEq, Eq, Clone)]
enum Layer {
Seed,
Soil,
Fertilizer,
Water,
Light,
Temperature,
Humidity,
Location,
}
#[derive(Hash, Eq, Debug, PartialEq, Clone)]
struct LayerDir {
from: Layer,
to: Layer,
}
#[derive(Debug)]
struct Mapping {
from_start: usize,
to_start: usize,
length: usize,
}
#[derive(Debug)]
struct Almanac {
seeds: Vec<usize>,
mappings: HashMap<LayerDir, Vec<Mapping>>,
}
fn layer(input: &str) -> IResult<&str, Layer> {
alt((
value(Layer::Seed, tag("seed")),
value(Layer::Soil, tag("soil")),
value(Layer::Fertilizer, tag("fertilizer")),
value(Layer::Water, tag("water")),
value(Layer::Light, tag("light")),
value(Layer::Temperature, tag("temperature")),
value(Layer::Humidity, tag("humidity")),
value(Layer::Location, tag("location")),
))(input)
}
fn int(input: &str) -> IResult<&str, usize> {
map_res(take_while(char::is_numeric), str::parse)(input)
}
fn mapping(input: &str) -> IResult<&str, Mapping> {
println!("mapping input: {input:?}");
let (input, nums) = separated_list0(space1, int)(input)?;
println!("nums: {nums:?}");
Ok((
input,
Mapping {
from_start: nums[0],
to_start: nums[1],
length: nums[2],
},
))
}
fn mapping_entry(input: &str) -> IResult<&str, (LayerDir, Vec<Mapping>)> {
let (input, (from, _, to, _)) = tuple((layer, tag("-to-"), layer, tag(" map:\n")))(input)?;
let (rest, input) = take_until("\n\n")(input)?;
let (input, mappings) = separated_list0(newline, mapping)(input)?;
Ok((rest, (LayerDir { from, to }, mappings)))
}
fn almanac(input: &str) -> IResult<&str, Almanac> {
let (input, seeds) = separated_list0(space1, int)(tag("seeds: ")(input)?.0)?;
let (input, mapping_tuples) =
separated_list0(tuple((line_ending, line_ending)), mapping_entry)(tag("\n\n")(input)?.0)?;
let mappings: HashMap<LayerDir, Vec<Mapping>> = mapping_tuples.into_iter().collect();
Ok((input, Almanac { seeds, mappings }))
}
struct Day5 {}
impl AoCSolution for Day5 {
type Input = String;
type Solution = usize;
fn part1(input: Self::Input) -> Self::Solution {
let almanac = almanac(&input);
println!("{almanac:?}");
0
}
fn part2(input: Self::Input) -> Self::Solution {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#"seeds: 79 14 55 13
seed-to-soil map:
50 98 2
52 50 48
soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15
fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4
water-to-light map:
88 18 7
18 25 70
light-to-temperature map:
45 77 23
81 45 19
68 64 13
temperature-to-humidity map:
0 69 1
1 0 69
humidity-to-location map:
60 56 37
56 93 4"#;
assert_eq!(Day5::part1(input.into()), 35);
assert_eq!(Day5::part2(input.into()), 0);
}
}

View file

@ -4,8 +4,6 @@ pub use std::io::Read;
use std::path::{Path, PathBuf};
use std::{env, io};
pub type Reader = Box<dyn Read>;
#[derive(Debug)]
enum InputFileError {
VarError(env::VarError),

32
2023/rust/src/template.rs Normal file
View file

@ -0,0 +1,32 @@
mod prelude;
use crate::prelude::*;
fn main() {
Day5::show(day_input(5), day_input(5))
}
struct Day5 {}
impl AoCSolution for Day5 {
type Input = String;
type Solution = usize;
fn part1(input: Self::Input) -> Self::Solution {
0
}
fn part2(input: Self::Input) -> Self::Solution {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#""#;
assert_eq!(Day2::part1(input.into()), 1);
assert_eq!(Day2::part2(input.into()), 0);
}
}