Compare commits
10 commits
2023-day1-
...
master
Author | SHA1 | Date | |
---|---|---|---|
Daniel Flanagan | 6f1199f3d2 | ||
Daniel Flanagan | 397a95f2dc | ||
Daniel Flanagan | 99b0fd06be | ||
Daniel Flanagan | 02e78aea8f | ||
Daniel Flanagan | 907385eb70 | ||
Daniel Flanagan | e02605974a | ||
Daniel Flanagan | 6dc64c8477 | ||
Daniel Flanagan | 5631415039 | ||
Daniel Flanagan | c89d892343 | ||
Daniel Flanagan | 14a77cd34f |
25
2023/rust/Cargo.lock
generated
25
2023/rust/Cargo.lock
generated
|
@ -5,3 +5,28 @@ version = 3
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aoc2023"
|
name = "aoc2023"
|
||||||
version = "1.0.0"
|
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",
|
||||||
|
]
|
||||||
|
|
|
@ -103,3 +103,4 @@ name = "day25"
|
||||||
path = "src/day25.rs"
|
path = "src/day25.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
nom = "7.1.3"
|
||||||
|
|
|
@ -1,102 +1,115 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::cell::OnceCell;
|
|
||||||
|
|
||||||
mod prelude;
|
mod prelude;
|
||||||
|
|
||||||
const DIGITS = OnceCell::new();
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
Day1::show(day_input(1), day_input(1))
|
Day1::show(day_input(1), day_input(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
const ASCII_ZERO: u8 = 0x30;
|
|
||||||
const ASCII_NINE: u8 = 0x39;
|
|
||||||
|
|
||||||
let value: &String = digits.get_or_init(|| {
|
|
||||||
[
|
|
||||||
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
|
||||||
]
|
|
||||||
.map(str::as_bytes)});
|
|
||||||
assert_eq!(value, "Hello, World!");
|
|
||||||
assert!(cell.get().is_some());
|
|
||||||
|
|
||||||
const DIGITS: [&[u8]; 10] = ;
|
|
||||||
const DIGITS_REV: [&[u8]; 10] = [
|
|
||||||
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
|
||||||
]
|
|
||||||
.map(|s| s.chars().rev().collect::<String>().as_bytes());
|
|
||||||
|
|
||||||
type Solution = i64;
|
|
||||||
|
|
||||||
struct Day1 {}
|
struct Day1 {}
|
||||||
impl Day1 {
|
impl Day1 {
|
||||||
fn is_digit(b: &u8) -> bool {
|
fn calibration_value(line: &str) -> i128 {
|
||||||
(ASCII_ZERO..=ASCII_NINE).contains(b)
|
println!("{line}");
|
||||||
}
|
let bytes = line.as_bytes();
|
||||||
|
let mut first_digit: Option<u8> = None;
|
||||||
fn to_digit(b: &u8) -> Solution {
|
let mut last_digit: Option<u8> = None;
|
||||||
i64::from(b - ASCII_ZERO)
|
for i in 0..bytes.len() {
|
||||||
}
|
let n = bytes.len() - 1 - i;
|
||||||
|
println!("{n} {i}");
|
||||||
fn calibration_value(line_bytes: &[u8]) -> i64 {
|
if first_digit.is_none() && (0x30..=0x39).contains(&bytes[i]) {
|
||||||
let i1 = line_bytes.iter().position(Self::is_digit).unwrap();
|
first_digit = Some(bytes[i] - 0x30);
|
||||||
let i2 = line_bytes[i1..]
|
println!("found first {first_digit:?}");
|
||||||
.iter()
|
}
|
||||||
.rev()
|
if last_digit.is_none() && (0x30..=0x39).contains(&bytes[n]) {
|
||||||
.position(Self::is_digit)
|
last_digit = Some(bytes[n] - 0x30);
|
||||||
.unwrap_or(i1);
|
println!("found last {last_digit:?}");
|
||||||
|
}
|
||||||
Self::to_digit(&line_bytes[i1]) * 10 + Self::to_digit(&line_bytes[i2])
|
if first_digit.is_some() && last_digit.is_some() {
|
||||||
}
|
break;
|
||||||
|
|
||||||
// part 2
|
|
||||||
|
|
||||||
fn num_name(s: &[u8], digits: &[&[u8]]) -> Option<i64> {
|
|
||||||
digits
|
|
||||||
.iter()
|
|
||||||
.position(|d| s.starts_with(d))
|
|
||||||
.map(|n| n as i64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn num_or_digit(s: &[u8], digits: &[&[u8]]) -> Option<Solution> {
|
|
||||||
if Self::is_digit(&s[0]) {
|
|
||||||
Some(Self::to_digit(&s[0]))
|
|
||||||
} else {
|
|
||||||
Self::num_name(s, digits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calibration_value_words(line_bytes: &[u8]) -> i64 {
|
|
||||||
for i in 0..line_bytes.len() {
|
|
||||||
if let Some(first_digit) = Self::num_or_digit(&line_bytes[i..], &DIGITS) {
|
|
||||||
for j in (i..line_bytes.len()).rev() {
|
|
||||||
if let Some(last_digit) = Self::num_or_digit(&line_bytes[i..j], &DIGITS_REV) {
|
|
||||||
return first_digit * 10 + last_digit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return first_digit * 10 + first_digit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
println!("{:?} {:?}", first_digit, last_digit);
|
||||||
|
Into::<i128>::into(first_digit.or(last_digit).unwrap() * 10)
|
||||||
|
+ Into::<i128>::into(last_digit.or(first_digit).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_name(s: &[u8]) -> Option<u8> {
|
||||||
|
for (i, w) in [
|
||||||
|
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
|
]
|
||||||
|
.map(|s| s.as_bytes())
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if s.starts_with(w) {
|
||||||
|
return Some(i as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_name_end(s: &[u8]) -> Option<u8> {
|
||||||
|
for (i, w) in [
|
||||||
|
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
|
]
|
||||||
|
.map(|s| s.as_bytes())
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if s.ends_with(w) {
|
||||||
|
return Some(i as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calibration_value_words(line: &str) -> i128 {
|
||||||
|
println!("{line}");
|
||||||
|
let bytes = line.as_bytes();
|
||||||
|
let mut first_digit: Option<u8> = None;
|
||||||
|
let mut last_digit: Option<u8> = None;
|
||||||
|
for i in 0..bytes.len() {
|
||||||
|
let n = bytes.len() - 1 - i;
|
||||||
|
println!("NI: {n} {i}");
|
||||||
|
if first_digit.is_none() {
|
||||||
|
if (0x30..=0x39).contains(&bytes[i]) {
|
||||||
|
first_digit = Some(bytes[i] - 0x30);
|
||||||
|
println!("found first {first_digit:?}");
|
||||||
|
} else if let Some(n) = Self::num_name(&bytes[i..]) {
|
||||||
|
first_digit = Some(n);
|
||||||
|
println!("found first text {first_digit:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if last_digit.is_none() {
|
||||||
|
println!("{:?}", &bytes[..=n]);
|
||||||
|
if (0x30..=0x39).contains(&bytes[n]) {
|
||||||
|
last_digit = Some(bytes[n] - 0x30);
|
||||||
|
println!("found last {last_digit:?}");
|
||||||
|
} else if let Some(n) = Self::num_name_end(&bytes[..=n]) {
|
||||||
|
last_digit = Some(n);
|
||||||
|
println!("found last text {last_digit:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if first_digit.is_some() && last_digit.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("{:?} {:?}", first_digit, last_digit);
|
||||||
|
Into::<i128>::into(first_digit.or(last_digit).unwrap() * 10)
|
||||||
|
+ Into::<i128>::into(last_digit.or(first_digit).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AoCSolution for Day1 {
|
impl AoCSolution for Day1 {
|
||||||
type Input = String;
|
type Input = String;
|
||||||
type Solution = i64;
|
type Solution = i128;
|
||||||
|
|
||||||
fn part1(input: Self::Input) -> Self::Solution {
|
fn part1(input: Self::Input) -> Self::Solution {
|
||||||
input
|
input.lines().map(Self::calibration_value).sum()
|
||||||
.lines()
|
|
||||||
.map(|l| Self::calibration_value(l.as_bytes()))
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: Self::Input) -> Self::Solution {
|
fn part2(input: Self::Input) -> Self::Solution {
|
||||||
input
|
input.lines().map(Self::calibration_value_words).sum()
|
||||||
.lines()
|
|
||||||
.map(|l| Self::calibration_value_words(l.as_bytes()))
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
108
2023/rust/src/day2.rs
Normal file
108
2023/rust/src/day2.rs
Normal 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
123
2023/rust/src/day3.rs
Normal 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
109
2023/rust/src/day4.rs
Normal 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
155
2023/rust/src/day5.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,8 +4,6 @@ pub use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{env, io};
|
use std::{env, io};
|
||||||
|
|
||||||
pub type Reader = Box<dyn Read>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum InputFileError {
|
enum InputFileError {
|
||||||
VarError(env::VarError),
|
VarError(env::VarError),
|
||||||
|
|
32
2023/rust/src/template.rs
Normal file
32
2023/rust/src/template.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue