Compare commits
19 commits
Author | SHA1 | Date | |
---|---|---|---|
Daniel Flanagan | f70f537f36 | ||
Daniel Flanagan | 3bd6fc2600 | ||
Daniel Flanagan | b17ad64e2f | ||
Daniel Flanagan | 772478e4fb | ||
Daniel Flanagan | 6f86208041 | ||
Daniel Flanagan | c595088352 | ||
Daniel Flanagan | f954cdad24 | ||
Daniel Flanagan | 81337c405b | ||
Daniel Flanagan | e5669fac3b | ||
Daniel Flanagan | 3d78aaee63 | ||
Daniel Flanagan | dc5e3b450f | ||
Daniel Flanagan | 80315133c4 | ||
Daniel Flanagan | 337508f4e8 | ||
Daniel Flanagan | c529a96f30 | ||
Daniel Flanagan | 07b32d8ff8 | ||
Daniel Flanagan | 0c0524cf7b | ||
Daniel Flanagan | ab55683f55 | ||
Daniel Flanagan | f5e79284a1 | ||
Daniel Flanagan | 95d88256e0 |
115
2024/rust/src/day10.rs
Normal file
115
2024/rust/src/day10.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
mod prelude;
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(10);
|
||||||
|
show_answers(part1(&input), part2(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> usize {
|
||||||
|
println!("{input}");
|
||||||
|
let mut sum = 0;
|
||||||
|
let map: Vec<Vec<u8>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|l| l.bytes().map(|b| b - b'0').collect())
|
||||||
|
.collect();
|
||||||
|
for (y, row) in map.iter().enumerate() {
|
||||||
|
for (x, height) in row.iter().enumerate() {
|
||||||
|
let mut scores = HashSet::new();
|
||||||
|
if *height == 0 {
|
||||||
|
capture_scores(&map, *height, x, y, &mut scores);
|
||||||
|
sum += scores.len();
|
||||||
|
}
|
||||||
|
println!("{x} {y} {height}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capture_scores(
|
||||||
|
map: &Vec<Vec<u8>>,
|
||||||
|
height: u8,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
scores: &mut HashSet<(usize, usize)>,
|
||||||
|
) {
|
||||||
|
if height == 9 {
|
||||||
|
scores.insert((x, y));
|
||||||
|
println!("found a 9");
|
||||||
|
}
|
||||||
|
for (nx, ny) in [
|
||||||
|
(x + 1, y),
|
||||||
|
(x.saturating_sub(1), y),
|
||||||
|
(x, y + 1),
|
||||||
|
(x, y.saturating_sub(1)),
|
||||||
|
] {
|
||||||
|
if nx >= map[0].len() || ny >= map.len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let next_height = map[ny][nx];
|
||||||
|
if next_height.saturating_sub(height) == 1 {
|
||||||
|
println!("{height} -> {next_height} at {nx},{ny}");
|
||||||
|
capture_scores(map, next_height, nx, ny, scores)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> usize {
|
||||||
|
println!("{input}");
|
||||||
|
let mut sum = 0;
|
||||||
|
let map: Vec<Vec<u8>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|l| l.bytes().map(|b| b - b'0').collect())
|
||||||
|
.collect();
|
||||||
|
for (y, row) in map.iter().enumerate() {
|
||||||
|
for (x, height) in row.iter().enumerate() {
|
||||||
|
if *height == 0 {
|
||||||
|
sum += find_paths_to_nines(&map, *height, x, y);
|
||||||
|
}
|
||||||
|
println!("{x} {y} {height}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_paths_to_nines(map: &Vec<Vec<u8>>, height: u8, x: usize, y: usize) -> usize {
|
||||||
|
let mut sum = 0;
|
||||||
|
if height == 9 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for (nx, ny) in [
|
||||||
|
(x + 1, y),
|
||||||
|
(x.saturating_sub(1), y),
|
||||||
|
(x, y + 1),
|
||||||
|
(x, y.saturating_sub(1)),
|
||||||
|
] {
|
||||||
|
if nx >= map[0].len() || ny >= map.len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let next_height = map[ny][nx];
|
||||||
|
if next_height.saturating_sub(height) == 1 {
|
||||||
|
println!("{height} -> {next_height} at {nx},{ny}");
|
||||||
|
sum += find_paths_to_nines(map, next_height, nx, ny)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let input = r#"89010123
|
||||||
|
78121874
|
||||||
|
87430965
|
||||||
|
96549874
|
||||||
|
45678903
|
||||||
|
32019012
|
||||||
|
01329801
|
||||||
|
10456732"#;
|
||||||
|
assert_eq!(part1(input), 36);
|
||||||
|
assert_eq!(part2(input), 81);
|
||||||
|
}
|
||||||
|
}
|
56
2024/rust/src/day11.rs
Normal file
56
2024/rust/src/day11.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
mod prelude;
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(11);
|
||||||
|
show_answers(part1(&input), part2(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> usize {
|
||||||
|
stone_blinks(input, 25)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stone_blinks(input: &str, count: usize) -> usize {
|
||||||
|
let mut stones: Vec<usize> = input
|
||||||
|
.trim()
|
||||||
|
.split(" ")
|
||||||
|
.map(|s| s.parse().unwrap())
|
||||||
|
.collect();
|
||||||
|
let mut next_stones = vec![];
|
||||||
|
for blink in 0..count {
|
||||||
|
println!("{blink} {}", stones.len());
|
||||||
|
for (_i, s) in stones.iter().enumerate() {
|
||||||
|
// next_stones.push(*s)
|
||||||
|
if *s == 0 {
|
||||||
|
next_stones.push(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let ss = s.to_string();
|
||||||
|
if ss.len() % 2 == 0 {
|
||||||
|
let mid = ss.len() / 2;
|
||||||
|
let (s1, s2) = (ss[0..mid].parse().unwrap(), ss[mid..].parse().unwrap());
|
||||||
|
next_stones.push(s1);
|
||||||
|
next_stones.push(s2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
next_stones.push(s * 2024)
|
||||||
|
}
|
||||||
|
stones = next_stones;
|
||||||
|
next_stones = vec![];
|
||||||
|
}
|
||||||
|
stones.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> usize {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let input = r#"125 17"#;
|
||||||
|
assert_eq!(part1(input), 55312);
|
||||||
|
assert_eq!(part2(input), 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,44 +24,55 @@ impl FromStr for Report {
|
||||||
|
|
||||||
impl Report {
|
impl Report {
|
||||||
fn is_safe(&self, skip_index: Option<usize>) -> bool {
|
fn is_safe(&self, skip_index: Option<usize>) -> bool {
|
||||||
let mut index_sequence: Box<dyn Iterator<Item = &i64>> = if let Some(i) = skip_index {
|
let mut ordering: Option<Ordering> = None;
|
||||||
Box::new(
|
let skip_index = skip_index.unwrap_or(self.levels.len() + 1);
|
||||||
self.levels[0..i]
|
let max = if skip_index < self.levels.len() {
|
||||||
.iter()
|
self.levels.len() - 2
|
||||||
.chain(self.levels[i + 1..self.levels.len()].iter())
|
|
||||||
.into_iter(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Box::new(self.levels.iter())
|
self.levels.len() - 1
|
||||||
};
|
};
|
||||||
let a = index_sequence.next().unwrap();
|
|
||||||
let b = index_sequence.next().unwrap();
|
for i in 0..max {
|
||||||
let ordering = a.cmp(b);
|
let mut ai = i;
|
||||||
if ordering == Ordering::Equal {
|
let mut bi = ai + 1;
|
||||||
return false;
|
if ai >= skip_index {
|
||||||
}
|
ai += 1
|
||||||
if !Self::safe_level_pair(*a, *b, ordering) {
|
};
|
||||||
return false;
|
if bi >= skip_index {
|
||||||
}
|
bi += 1
|
||||||
println!("{a} {b} {ordering:?} {skip_index:?}");
|
};
|
||||||
while let (a, Some(b)) = (b, index_sequence.next()) {
|
let (a, b) = (self.levels[ai], self.levels[bi]);
|
||||||
println!("{a} {b} {ordering:?}");
|
if !Self::safe_level_pair(a, b, &ordering) {
|
||||||
if !Self::safe_level_pair(*a, *b, ordering) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if ordering == None {
|
||||||
|
ordering = Some(a.cmp(&b))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_safe_with_any_single_level_removed(&self) -> bool {
|
fn is_safe_with_any_single_level_removed(&self) -> bool {
|
||||||
(0..self.levels.len())
|
for i in 0..self.levels.len() {
|
||||||
.into_iter()
|
if self.is_safe(Some(i)) {
|
||||||
.any(|i| self.is_safe(Some(i)))
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn safe_level_pair(a: i64, b: i64, kind: Ordering) -> bool {
|
fn safe_level_pair(a: i64, b: i64, kind: &Option<Ordering>) -> bool {
|
||||||
kind == a.cmp(&b) && (a - b).abs() <= 3
|
if a == b {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (a - b).abs() > 3 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if kind.map(|o| o == a.cmp(&b)) == Some(false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
130
2024/rust/src/day3.rs
Normal file
130
2024/rust/src/day3.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
mod prelude;
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(3);
|
||||||
|
show_answers(sum_muls(&input), sum_muls_with_do_and_dont(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum_muls(input: &str) -> usize {
|
||||||
|
let mut sum = 0;
|
||||||
|
|
||||||
|
// this string slice will be how we keep track of what is "left" to parse
|
||||||
|
// though it would likely be easier to use an index and slice as needed :shrug:
|
||||||
|
let mut s = &input[..];
|
||||||
|
|
||||||
|
// look for the next valid mul operation
|
||||||
|
while let Some(i) = s.find("mul(") {
|
||||||
|
// re-slice to the remaining string
|
||||||
|
s = &s[i + 4..];
|
||||||
|
|
||||||
|
// variables to contain our operands
|
||||||
|
let mut left: Option<usize> = None;
|
||||||
|
let mut right: Option<usize> = None;
|
||||||
|
|
||||||
|
// variable to contain our currently-parsing token, as we will continue
|
||||||
|
// from here one character at a time
|
||||||
|
let mut token = &s[0..0];
|
||||||
|
|
||||||
|
for c in s.chars() {
|
||||||
|
match c {
|
||||||
|
// any digit simply gets "appended" to our token (in reality, we are just increasing the length of the token slice)
|
||||||
|
'0'..='9' => {
|
||||||
|
token = &s[0..token.len() + 1];
|
||||||
|
// the problem indicates that numbers may not be longer than
|
||||||
|
// 3 digits, so if we encounter one, we break out of this
|
||||||
|
// loop and head to the next mul operation
|
||||||
|
if token.len() > 3 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// once we hit a comma, we check that we don't already have
|
||||||
|
// a left operand then parse the number (which we can unwrap
|
||||||
|
// since we are assuring we are dealing with digits above,
|
||||||
|
// though we could easily optimize that out with bit of
|
||||||
|
// arithmetic), advance through our string slice, and reset our
|
||||||
|
// currently-parsing token
|
||||||
|
',' => {
|
||||||
|
if left.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
left = Some(token.parse().unwrap());
|
||||||
|
s = &s[token.len() + 1..];
|
||||||
|
token = &s[0..0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// pretty much the same logic for finding a comma, but for the
|
||||||
|
// right operand
|
||||||
|
')' => {
|
||||||
|
if right.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
right = Some(token.parse().unwrap());
|
||||||
|
s = &s[token.len() + 1..];
|
||||||
|
token = &s[0..0];
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let (Some(left), Some(right)) = (left, right) {
|
||||||
|
sum += left * right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum_muls_with_do_and_dont(input: &str) -> usize {
|
||||||
|
// the gist of this function is that we look for string segments that are
|
||||||
|
// between do() and don't() operations and only sum_muls for those NOT
|
||||||
|
// between a don't() and do()
|
||||||
|
let mut sum = 0;
|
||||||
|
let mut s = &input[..];
|
||||||
|
|
||||||
|
// since there may be a case where the last do() or don't() is a do(),
|
||||||
|
// we need to know whether or not to parse the "remainder" after we have
|
||||||
|
// processed the last don't() segment which may have ended with a do()
|
||||||
|
let mut mul_rest = true;
|
||||||
|
while let Some(i) = s.find("don't()") {
|
||||||
|
// once we find a don't, we know we want to sum_muls for everything up
|
||||||
|
// to the current position
|
||||||
|
sum += sum_muls(&s[0..i]);
|
||||||
|
|
||||||
|
// then we can advance our slice
|
||||||
|
s = &s[i..];
|
||||||
|
mul_rest = false;
|
||||||
|
if let Some(i) = s.find("do()") {
|
||||||
|
// if we find a do(), set our mul_rest flag appropriately, advance
|
||||||
|
// along the slice, and head to the next iteration (or be done in
|
||||||
|
// the loop)
|
||||||
|
s = &s[i..];
|
||||||
|
mul_rest = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we "ended" with a d(), we want to sum_muls for the rest of the slice
|
||||||
|
if mul_rest {
|
||||||
|
sum += sum_muls(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
assert_eq!(
|
||||||
|
sum_muls("xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))"),
|
||||||
|
161
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sum_muls_with_do_and_dont(
|
||||||
|
"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))"
|
||||||
|
),
|
||||||
|
48
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
126
2024/rust/src/day4.rs
Normal file
126
2024/rust/src/day4.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
mod prelude;
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(4);
|
||||||
|
let grid: Vec<Vec<u8>> = input
|
||||||
|
.trim()
|
||||||
|
.lines()
|
||||||
|
.map(|l| l.trim().bytes().collect())
|
||||||
|
.collect();
|
||||||
|
show_answers(count_xmas(&grid), count_mas_x(&grid));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_xmas(grid: &Vec<Vec<u8>>) -> usize {
|
||||||
|
let height = grid.len();
|
||||||
|
let width = grid[0].len();
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
const XMAS: (u8, u8, u8, u8) = (b'X', b'M', b'A', b'S');
|
||||||
|
const SAMX: (u8, u8, u8, u8) = (b'S', b'A', b'M', b'X');
|
||||||
|
|
||||||
|
for (y, line) in grid.iter().enumerate() {
|
||||||
|
for (x, letter) in line.iter().enumerate() {
|
||||||
|
// let near_left_edge = x < 3;
|
||||||
|
let near_top_edge = y < 3;
|
||||||
|
let near_right_edge = x >= width - 3;
|
||||||
|
let near_bottom_edge = y >= height - 3;
|
||||||
|
|
||||||
|
if !near_right_edge && !near_top_edge {
|
||||||
|
// north-west
|
||||||
|
let ray = (
|
||||||
|
*letter,
|
||||||
|
grid[y - 1][x + 1],
|
||||||
|
grid[y - 2][x + 2],
|
||||||
|
grid[y - 3][x + 3],
|
||||||
|
);
|
||||||
|
if ray == XMAS || ray == SAMX {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !near_right_edge {
|
||||||
|
// west
|
||||||
|
let ray = (*letter, grid[y][x + 1], grid[y][x + 2], grid[y][x + 3]);
|
||||||
|
if ray == XMAS || ray == SAMX {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !near_right_edge && !near_bottom_edge {
|
||||||
|
// south-west
|
||||||
|
let ray = (
|
||||||
|
*letter,
|
||||||
|
grid[y + 1][x + 1],
|
||||||
|
grid[y + 2][x + 2],
|
||||||
|
grid[y + 3][x + 3],
|
||||||
|
);
|
||||||
|
if ray == XMAS || ray == SAMX {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !near_bottom_edge {
|
||||||
|
// south
|
||||||
|
let ray = (*letter, grid[y + 1][x], grid[y + 2][x], grid[y + 3][x]);
|
||||||
|
if ray == XMAS || ray == SAMX {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counter
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_mas_x(grid: &Vec<Vec<u8>>) -> usize {
|
||||||
|
let mut counter = 0;
|
||||||
|
let (width, height) = (grid[0].len(), grid.len());
|
||||||
|
for y in 1..height - 1 {
|
||||||
|
for x in 1..width - 1 {
|
||||||
|
// 'A' will always be in the middle, so we can easily just look for
|
||||||
|
// middle 'A' occurences and then check for opposing 'S' and 'M'
|
||||||
|
// characters
|
||||||
|
if grid[y][x] != b'A' {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check opposing cross-characters
|
||||||
|
let ray1 = (grid[y - 1][x - 1], grid[y + 1][x + 1]);
|
||||||
|
let ray2 = (grid[y + 1][x - 1], grid[y - 1][x + 1]);
|
||||||
|
if (ray1 == (b'M', b'S') || ray1 == (b'S', b'M'))
|
||||||
|
&& (ray2 == (b'M', b'S') || ray2 == (b'S', b'M'))
|
||||||
|
{
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counter
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let input = r#"MMMSXXMASM
|
||||||
|
MSAMXMSMSA
|
||||||
|
AMXSXMAAMM
|
||||||
|
MSAMASMSMX
|
||||||
|
XMASAMXAMM
|
||||||
|
XXAMMXXAMA
|
||||||
|
SMSMSASXSS
|
||||||
|
SAXAMASAAA
|
||||||
|
MAMMMXMMMM
|
||||||
|
MXMXAXMASX"#;
|
||||||
|
|
||||||
|
let grid: Vec<Vec<u8>> = input
|
||||||
|
.trim()
|
||||||
|
.lines()
|
||||||
|
.map(|l| l.trim().bytes().collect())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_eq!(count_xmas(&grid), 18);
|
||||||
|
assert_eq!(count_mas_x(&grid), 9);
|
||||||
|
}
|
||||||
|
}
|
133
2024/rust/src/day5.rs
Normal file
133
2024/rust/src/day5.rs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
mod prelude;
|
||||||
|
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(5);
|
||||||
|
let update: ManualUpdate = input.parse().unwrap();
|
||||||
|
show_answers(update.part1(), update.part2());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ManualUpdate {
|
||||||
|
rules: HashMap<i64, HashSet<i64>>,
|
||||||
|
updates: Vec<Vec<i64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ManualUpdate {
|
||||||
|
type Err = ParseIntError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (rules_string, updates_string) = s.split_once("\n\n").unwrap();
|
||||||
|
let mut rules: HashMap<i64, HashSet<i64>> = HashMap::new();
|
||||||
|
let mut updates = vec![];
|
||||||
|
for rule in rules_string.lines() {
|
||||||
|
let (left, right) = rule.split_once("|").unwrap();
|
||||||
|
let (left, right): (i64, i64) = (left.parse()?, right.parse()?);
|
||||||
|
|
||||||
|
rules.entry(left).or_default().insert(right);
|
||||||
|
}
|
||||||
|
for update in updates_string.lines() {
|
||||||
|
updates.push(
|
||||||
|
update
|
||||||
|
.split(",")
|
||||||
|
.map(|page| page.parse())
|
||||||
|
.collect::<Result<Vec<i64>, Self::Err>>()?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { rules, updates })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManualUpdate {
|
||||||
|
fn part1(&self) -> i64 {
|
||||||
|
self.updates
|
||||||
|
.iter()
|
||||||
|
.filter(|update| self.is_update_valid(update))
|
||||||
|
.map(Self::middle_page_number)
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(&self) -> i64 {
|
||||||
|
self.updates
|
||||||
|
.iter()
|
||||||
|
.filter(|update| !self.is_update_valid(update))
|
||||||
|
.map(|update| self.sort_update(update))
|
||||||
|
.map(|sorted_update| Self::middle_page_number(&sorted_update))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn middle_page_number(update: &Vec<i64>) -> i64 {
|
||||||
|
update[update.len() / 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_update_valid(&self, update: &Vec<i64>) -> bool {
|
||||||
|
// work through pages in an update backwards
|
||||||
|
for (i, page) in update.iter().rev().enumerate() {
|
||||||
|
// so we can loop through each previous page
|
||||||
|
for previous_page in update[0..update.len() - i].iter().rev() {
|
||||||
|
if let Some(failure_pages) = self.rules.get(page) {
|
||||||
|
if failure_pages.contains(previous_page) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_update(&self, update: &Vec<i64>) -> Vec<i64> {
|
||||||
|
let mut sorted_update = update.clone();
|
||||||
|
sorted_update.sort_by(|a, b| {
|
||||||
|
if let Some(after_pages) = self.rules.get(a) {
|
||||||
|
if after_pages.contains(b) {
|
||||||
|
return Ordering::Less;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ordering::Greater;
|
||||||
|
});
|
||||||
|
sorted_update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let manual_update: ManualUpdate = "47|53
|
||||||
|
97|13
|
||||||
|
97|61
|
||||||
|
97|47
|
||||||
|
75|29
|
||||||
|
61|13
|
||||||
|
75|53
|
||||||
|
29|13
|
||||||
|
97|29
|
||||||
|
53|29
|
||||||
|
61|53
|
||||||
|
97|53
|
||||||
|
61|29
|
||||||
|
47|13
|
||||||
|
75|47
|
||||||
|
97|75
|
||||||
|
47|61
|
||||||
|
75|61
|
||||||
|
47|29
|
||||||
|
75|13
|
||||||
|
53|13
|
||||||
|
|
||||||
|
75,47,61,53,29
|
||||||
|
97,61,53,29,13
|
||||||
|
75,29,13
|
||||||
|
75,97,47,61,53
|
||||||
|
61,13,29
|
||||||
|
97,13,75,29,47"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(manual_update.part1(), 143);
|
||||||
|
assert_eq!(manual_update.part2(), 123);
|
||||||
|
}
|
||||||
|
}
|
193
2024/rust/src/day6.rs
Normal file
193
2024/rust/src/day6.rs
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
mod prelude;
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(6);
|
||||||
|
let mut lab: Lab = input.parse().unwrap();
|
||||||
|
let mut lab2: Lab = input.parse().unwrap();
|
||||||
|
show_answers(
|
||||||
|
lab.num_steps_until_guard_leaves_lab(),
|
||||||
|
lab2.num_positions_of_new_obstructions_that_cause_guard_loop(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
|
struct Vec2 {
|
||||||
|
x: i64,
|
||||||
|
y: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vec2 {
|
||||||
|
fn new(x: i64, y: i64) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(&self, v: &Vec2) -> Self {
|
||||||
|
Self::new(self.x + v.x, self.y + v.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Vec2 {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Vec2({}, {})", self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Obstruction {
|
||||||
|
pos: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
|
struct Guard {
|
||||||
|
pos: Vec2,
|
||||||
|
vel: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Guard {
|
||||||
|
fn turn(&mut self) {
|
||||||
|
// too lazy for trig today I guess
|
||||||
|
if self.vel.x > 0 && self.vel.y > 0 {
|
||||||
|
self.vel.x *= -1;
|
||||||
|
} else if self.vel.x < 0 && self.vel.y > 0 {
|
||||||
|
self.vel.y *= -1;
|
||||||
|
} else if self.vel.x < 0 && self.vel.y < 0 {
|
||||||
|
self.vel.x *= -1;
|
||||||
|
} else if self.vel.x > 0 && self.vel.y < 0 {
|
||||||
|
self.vel.y *= -1;
|
||||||
|
} else if self.vel.y != 0 {
|
||||||
|
self.vel.x = self.vel.y * -1;
|
||||||
|
self.vel.y = 0;
|
||||||
|
} else if self.vel.x != 0 {
|
||||||
|
self.vel.y = self.vel.x;
|
||||||
|
self.vel.x = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Lab {
|
||||||
|
obstructions: Vec<Obstruction>,
|
||||||
|
guard: Guard,
|
||||||
|
size: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Lab {
|
||||||
|
type Err = Infallible;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut size = Vec2::default();
|
||||||
|
let mut obstructions = vec![];
|
||||||
|
let mut guard: Option<Guard> = None;
|
||||||
|
for (y, line) in s.lines().enumerate() {
|
||||||
|
size.y += 1;
|
||||||
|
size.x = line.len() as i64; // whatever - still linear ;)
|
||||||
|
for (x, c) in line.bytes().enumerate() {
|
||||||
|
match c {
|
||||||
|
b'#' => obstructions.push(Obstruction {
|
||||||
|
pos: Vec2::new(x as i64, y as i64),
|
||||||
|
}),
|
||||||
|
b'^' => {
|
||||||
|
guard = Some(Guard {
|
||||||
|
pos: Vec2::new(x as i64, y as i64),
|
||||||
|
vel: Vec2::new(0, -1),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Lab {
|
||||||
|
size,
|
||||||
|
guard: guard.unwrap(),
|
||||||
|
obstructions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lab {
|
||||||
|
fn step(&mut self) {
|
||||||
|
// move guard
|
||||||
|
let next_pos = self.guard.pos.add(&self.guard.vel);
|
||||||
|
self.guard.pos = next_pos;
|
||||||
|
|
||||||
|
// after moving, check if we need to turn
|
||||||
|
// TODO: obstructions should lookup by position instead of this
|
||||||
|
// broad-phase "check each one each time" slow business
|
||||||
|
while self.has_obstacle_at(&self.guard.pos.add(&self.guard.vel)) {
|
||||||
|
self.guard.turn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, pos: &Vec2) -> bool {
|
||||||
|
(0..self.size.x).contains(&pos.x) && (0..self.size.y).contains(&pos.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_steps_until_guard_leaves_lab(&mut self) -> usize {
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
visited.insert(self.guard.pos.clone());
|
||||||
|
while self.contains(&self.guard.pos) {
|
||||||
|
self.step();
|
||||||
|
visited.insert(self.guard.pos.clone());
|
||||||
|
}
|
||||||
|
visited.len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_obstacle_at(&self, pos: &Vec2) -> bool {
|
||||||
|
self.obstructions.iter().any(|o| o.pos == *pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_positions_of_new_obstructions_that_cause_guard_loop(&mut self) -> usize {
|
||||||
|
let mut positions_with_cycles_detected = 0;
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
visited.insert(self.guard.clone());
|
||||||
|
// TODO: avoid super brute force
|
||||||
|
for x in 0..self.size.x {
|
||||||
|
for y in 0..self.size.y {
|
||||||
|
println!("Simulating at {x} {y}...");
|
||||||
|
let mut alternate_visited = visited.clone();
|
||||||
|
let mut alternate_lab = self.clone();
|
||||||
|
alternate_lab.obstructions.push(Obstruction {
|
||||||
|
pos: Vec2::new(x, y),
|
||||||
|
});
|
||||||
|
while alternate_lab.contains(&alternate_lab.guard.pos) {
|
||||||
|
alternate_lab.step();
|
||||||
|
if alternate_visited.contains(&alternate_lab.guard) {
|
||||||
|
positions_with_cycles_detected += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
alternate_visited.insert(alternate_lab.guard.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positions_with_cycles_detected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let input = "
|
||||||
|
....#.....
|
||||||
|
.........#
|
||||||
|
..........
|
||||||
|
..#.......
|
||||||
|
.......#..
|
||||||
|
..........
|
||||||
|
.#..^.....
|
||||||
|
........#.
|
||||||
|
#.........
|
||||||
|
......#..."
|
||||||
|
.trim();
|
||||||
|
let mut lab: Lab = input.parse().unwrap();
|
||||||
|
let mut lab2 = lab.clone();
|
||||||
|
assert_eq!(lab.num_steps_until_guard_leaves_lab(), 41);
|
||||||
|
assert_eq!(
|
||||||
|
lab2.num_positions_of_new_obstructions_that_cause_guard_loop(),
|
||||||
|
6
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
93
2024/rust/src/day7.rs
Normal file
93
2024/rust/src/day7.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
mod prelude;
|
||||||
|
use std::slice::Windows;
|
||||||
|
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(7);
|
||||||
|
show_answers(
|
||||||
|
sum_possibly_valid_tests(&input, &add_and_mul),
|
||||||
|
sum_possibly_valid_tests(&input, &add_and_mul_and_concat),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum_possibly_valid_tests(input: &str, f: &dyn Fn(Vec<usize>, &usize) -> Vec<usize>) -> usize {
|
||||||
|
let mut sum = 0;
|
||||||
|
for l in input.lines() {
|
||||||
|
let (target, operands) = l.split_once(':').unwrap();
|
||||||
|
let target: usize = target.parse().unwrap();
|
||||||
|
let operands: Vec<usize> = operands
|
||||||
|
.trim()
|
||||||
|
.split(" ")
|
||||||
|
.map(|s| s.parse().unwrap())
|
||||||
|
.collect();
|
||||||
|
if operands
|
||||||
|
.iter()
|
||||||
|
.skip(1)
|
||||||
|
.fold(vec![operands[0]], f)
|
||||||
|
.iter()
|
||||||
|
.any(|n| *n == target)
|
||||||
|
{
|
||||||
|
sum += target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_and_mul(lefts: Vec<usize>, right: &usize) -> Vec<usize> {
|
||||||
|
let mut result = vec![];
|
||||||
|
for left in lefts {
|
||||||
|
result.push(left + right);
|
||||||
|
result.push(left * right);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_and_mul_and_concat(lefts: Vec<usize>, right: &usize) -> Vec<usize> {
|
||||||
|
let mut result = vec![];
|
||||||
|
for left in lefts {
|
||||||
|
result.push(left + right);
|
||||||
|
result.push(left * right);
|
||||||
|
result.push(format!("{}{}", left, right).parse().unwrap());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
assert_eq!(
|
||||||
|
sum_possibly_valid_tests(
|
||||||
|
"190: 10 19
|
||||||
|
3267: 81 40 27
|
||||||
|
83: 17 5
|
||||||
|
156: 15 6
|
||||||
|
7290: 6 8 6 15
|
||||||
|
161011: 16 10 13
|
||||||
|
192: 17 8 14
|
||||||
|
21037: 9 7 18 13
|
||||||
|
292: 11 6 16 20",
|
||||||
|
&add_and_mul
|
||||||
|
),
|
||||||
|
3749
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sum_possibly_valid_tests(
|
||||||
|
"190: 10 19
|
||||||
|
3267: 81 40 27
|
||||||
|
83: 17 5
|
||||||
|
156: 15 6
|
||||||
|
7290: 6 8 6 15
|
||||||
|
161011: 16 10 13
|
||||||
|
192: 17 8 14
|
||||||
|
21037: 9 7 18 13
|
||||||
|
292: 11 6 16 20",
|
||||||
|
&add_and_mul_and_concat
|
||||||
|
),
|
||||||
|
11387
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
115
2024/rust/src/day8.rs
Normal file
115
2024/rust/src/day8.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
mod prelude;
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(8);
|
||||||
|
show_answers(part1(&input), part2(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> usize {
|
||||||
|
let mut map: HashMap<u8, HashSet<(i64, i64)>> = HashMap::new();
|
||||||
|
let mut antinodes: HashSet<(i64, i64)> = HashSet::new();
|
||||||
|
let (width, height) = (
|
||||||
|
input.lines().count() as i64,
|
||||||
|
input.lines().next().unwrap().len() as i64,
|
||||||
|
);
|
||||||
|
for (y, line) in input.lines().enumerate() {
|
||||||
|
for (x, c) in line.bytes().enumerate() {
|
||||||
|
if c == b'.' {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
map.entry(c)
|
||||||
|
.or_insert_with(|| HashSet::new())
|
||||||
|
.insert((x as i64, y as i64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for nodes in map.values() {
|
||||||
|
println!("{nodes:?}");
|
||||||
|
for (i, (x1, y1)) in nodes.iter().enumerate() {
|
||||||
|
for (j, (x2, y2)) in nodes.iter().enumerate() {
|
||||||
|
if i == j {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (dx, dy) = (x1 - x2, y1 - y2);
|
||||||
|
let (a1x, a1y) = (x1 + dx, y1 + dy);
|
||||||
|
let (a2x, a2y) = (x2 - dx, y2 - dy);
|
||||||
|
if a1x >= 0 && a1x < width && a1y >= 0 && a1y < height {
|
||||||
|
antinodes.insert((a1x, a1y));
|
||||||
|
}
|
||||||
|
if a2x >= 0 && a2x < width && a2y >= 0 && a2y < height {
|
||||||
|
antinodes.insert((a2x, a2y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
antinodes.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> usize {
|
||||||
|
let mut map: HashMap<u8, HashSet<(i64, i64)>> = HashMap::new();
|
||||||
|
let mut antinodes: HashSet<(i64, i64)> = HashSet::new();
|
||||||
|
let (width, height) = (
|
||||||
|
input.trim().lines().count() as i64,
|
||||||
|
input.lines().next().unwrap().trim().len() as i64,
|
||||||
|
);
|
||||||
|
for (y, line) in input.lines().enumerate() {
|
||||||
|
for (x, c) in line.bytes().enumerate() {
|
||||||
|
if c == b'.' {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
map.entry(c)
|
||||||
|
.or_insert_with(|| HashSet::new())
|
||||||
|
.insert((x as i64, y as i64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for nodes in map.values() {
|
||||||
|
for (i, (x1, y1)) in nodes.iter().enumerate() {
|
||||||
|
for (j, (x2, y2)) in nodes.iter().enumerate() {
|
||||||
|
antinodes.insert((*x1, *y1));
|
||||||
|
antinodes.insert((*x2, *y2));
|
||||||
|
if i == j {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (dx, dy) = (x1 - x2, y1 - y2);
|
||||||
|
let (mut a1x, mut a1y) = (x1 + dx, y1 + dy);
|
||||||
|
while a1x >= 0 && a1x < width && a1y >= 0 && a1y < height {
|
||||||
|
antinodes.insert((a1x, a1y));
|
||||||
|
println!("{a1x} {a1y} {}", antinodes.len());
|
||||||
|
a1x += dx;
|
||||||
|
a1y += dy;
|
||||||
|
}
|
||||||
|
let (mut a2x, mut a2y) = (x2 - dx, y2 - dy);
|
||||||
|
while a2x >= 0 && a2x < width && a2y >= 0 && a2y < height {
|
||||||
|
antinodes.insert((a2x, a2y));
|
||||||
|
println!("{a2x} {a2y} {}", antinodes.len());
|
||||||
|
a2x -= dx;
|
||||||
|
a2y -= dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
antinodes.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let input = "............
|
||||||
|
........0...
|
||||||
|
.....0......
|
||||||
|
.......0....
|
||||||
|
....0.......
|
||||||
|
......A.....
|
||||||
|
............
|
||||||
|
............
|
||||||
|
........A...
|
||||||
|
.........A..
|
||||||
|
............
|
||||||
|
............";
|
||||||
|
assert_eq!(part1(input), 14);
|
||||||
|
assert_eq!(part2(input), 34);
|
||||||
|
}
|
||||||
|
}
|
101
2024/rust/src/day9.rs
Normal file
101
2024/rust/src/day9.rs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
mod prelude;
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_input(9);
|
||||||
|
show_answers(part1(&input), part2(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Disk {
|
||||||
|
blocks: Vec<Option<usize>>,
|
||||||
|
files: Vec<(usize, u8)>,
|
||||||
|
free_blocks: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Disk {
|
||||||
|
type Err = Infallible;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut blocks: Vec<Option<usize>> = vec![];
|
||||||
|
let mut files = vec![];
|
||||||
|
let mut free_blocks: usize = 0;
|
||||||
|
for (i, len) in s.trim().bytes().map(|len| len - b'0').enumerate() {
|
||||||
|
// every other iteration is "free space"
|
||||||
|
let file_id = if i % 2 == 0 {
|
||||||
|
Some((i / 2) as usize)
|
||||||
|
} else {
|
||||||
|
free_blocks += len as usize;
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if file_id.is_some() {
|
||||||
|
files.push((blocks.len(), len));
|
||||||
|
}
|
||||||
|
for _ in 0..len {
|
||||||
|
blocks.push(file_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
files,
|
||||||
|
blocks,
|
||||||
|
free_blocks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disk {
|
||||||
|
fn checksum(&self) -> usize {
|
||||||
|
self.blocks
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, b)| i * b.unwrap_or_default())
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> usize {
|
||||||
|
let mut disk: Disk = input.parse().unwrap();
|
||||||
|
let mut right = disk.blocks.len() - 1;
|
||||||
|
for left in 0..(right.clone() - disk.free_blocks + 1) {
|
||||||
|
if disk.blocks[left].is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
while disk.blocks[right].is_none() {
|
||||||
|
right -= 1;
|
||||||
|
}
|
||||||
|
(disk.blocks[left], disk.blocks[right]) = (disk.blocks[right], disk.blocks[left]);
|
||||||
|
}
|
||||||
|
|
||||||
|
disk.checksum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> usize {
|
||||||
|
let mut disk: Disk = input.parse().unwrap();
|
||||||
|
for (cur_i, len) in disk.files.iter().rev() {
|
||||||
|
for left in 0..*cur_i {
|
||||||
|
if disk.blocks[left..(left + (*len as usize))]
|
||||||
|
.iter()
|
||||||
|
.all(|f| f.is_none())
|
||||||
|
{
|
||||||
|
for i in 0..*len {
|
||||||
|
let i = i as usize;
|
||||||
|
(disk.blocks[left + i], disk.blocks[cur_i + i]) =
|
||||||
|
(disk.blocks[cur_i + i], disk.blocks[left + i])
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disk.checksum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let input = r#"2333133121414131402"#;
|
||||||
|
assert_eq!(part1(input), 1928);
|
||||||
|
assert_eq!(part2(input), 2858);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,9 @@ use std::path::{Path, PathBuf};
|
||||||
pub use std::{
|
pub use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
collections::HashSet,
|
||||||
|
convert::Infallible,
|
||||||
|
fmt::Display,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::Read,
|
io::Read,
|
||||||
iter::zip,
|
iter::zip,
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
mod prelude;
|
mod prelude;
|
||||||
use crate::prelude::*;
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
Day5::show(day_input(5), day_input(5))
|
let input = day_input(DAY);
|
||||||
|
show_answers(part1(&input), part2(&input));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Day5 {}
|
fn part1(input: &str) -> i64 {
|
||||||
impl AoCSolution for Day5 {
|
0
|
||||||
type Input = String;
|
}
|
||||||
type Solution = usize;
|
|
||||||
|
|
||||||
fn part1(input: Self::Input) -> Self::Solution {
|
fn part2(input: &str) -> i64 {
|
||||||
0
|
0
|
||||||
}
|
|
||||||
|
|
||||||
fn part2(input: Self::Input) -> Self::Solution {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -26,7 +21,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
let input = r#""#;
|
let input = r#""#;
|
||||||
assert_eq!(Day2::part1(input.into()), 1);
|
assert_eq!(part1(input), 1);
|
||||||
assert_eq!(Day2::part2(input.into()), 0);
|
assert_eq!(part2(input), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue