Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
Daniel Flanagan | e7cc1df936 |
|
@ -1,115 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
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,29 +24,30 @@ impl FromStr for Report {
|
|||
|
||||
impl Report {
|
||||
fn is_safe(&self, skip_index: Option<usize>) -> bool {
|
||||
let mut ordering: Option<Ordering> = None;
|
||||
let skip_index = skip_index.unwrap_or(self.levels.len() + 1);
|
||||
let max = if skip_index < self.levels.len() {
|
||||
self.levels.len() - 2
|
||||
let mut index_sequence: Box<dyn Iterator<Item = &i64>> = if let Some(i) = skip_index {
|
||||
Box::new(
|
||||
self.levels[0..i]
|
||||
.iter()
|
||||
.chain(self.levels[i + 1..self.levels.len()].iter())
|
||||
.into_iter(),
|
||||
)
|
||||
} else {
|
||||
self.levels.len() - 1
|
||||
Box::new(self.levels.iter())
|
||||
};
|
||||
|
||||
for i in 0..max {
|
||||
let mut ai = i;
|
||||
let mut bi = ai + 1;
|
||||
if ai >= skip_index {
|
||||
ai += 1
|
||||
};
|
||||
if bi >= skip_index {
|
||||
bi += 1
|
||||
};
|
||||
let (a, b) = (self.levels[ai], self.levels[bi]);
|
||||
if !Self::safe_level_pair(a, b, &ordering) {
|
||||
let a = index_sequence.next().unwrap();
|
||||
let b = index_sequence.next().unwrap();
|
||||
let ordering = a.cmp(b);
|
||||
if ordering == Ordering::Equal {
|
||||
return false;
|
||||
}
|
||||
if ordering == None {
|
||||
ordering = Some(a.cmp(&b))
|
||||
if !Self::safe_level_pair(*a, *b, ordering) {
|
||||
return false;
|
||||
}
|
||||
println!("{a} {b} {ordering:?} {skip_index:?}");
|
||||
while let (a, Some(b)) = (b, index_sequence.next()) {
|
||||
println!("{a} {b} {ordering:?}");
|
||||
if !Self::safe_level_pair(*a, *b, ordering) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,25 +55,13 @@ impl Report {
|
|||
}
|
||||
|
||||
fn is_safe_with_any_single_level_removed(&self) -> bool {
|
||||
for i in 0..self.levels.len() {
|
||||
if self.is_safe(Some(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
(0..self.levels.len())
|
||||
.into_iter()
|
||||
.any(|i| self.is_safe(Some(i)))
|
||||
}
|
||||
|
||||
fn safe_level_pair(a: i64, b: i64, kind: &Option<Ordering>) -> bool {
|
||||
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;
|
||||
fn safe_level_pair(a: i64, b: i64, kind: Ordering) -> bool {
|
||||
kind == a.cmp(&b) && (a - b).abs() <= 3
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
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,9 +2,6 @@ use std::path::{Path, PathBuf};
|
|||
pub use std::{
|
||||
cmp::Ordering,
|
||||
collections::HashMap,
|
||||
collections::HashSet,
|
||||
convert::Infallible,
|
||||
fmt::Display,
|
||||
fs::File,
|
||||
io::Read,
|
||||
iter::zip,
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
mod prelude;
|
||||
pub use crate::prelude::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let input = day_input(DAY);
|
||||
show_answers(part1(&input), part2(&input));
|
||||
Day5::show(day_input(5), day_input(5))
|
||||
}
|
||||
|
||||
fn part1(input: &str) -> i64 {
|
||||
struct Day5 {}
|
||||
impl AoCSolution for Day5 {
|
||||
type Input = String;
|
||||
type Solution = usize;
|
||||
|
||||
fn part1(input: Self::Input) -> Self::Solution {
|
||||
0
|
||||
}
|
||||
|
||||
fn part2(input: &str) -> i64 {
|
||||
fn part2(input: Self::Input) -> Self::Solution {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -21,7 +26,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test() {
|
||||
let input = r#""#;
|
||||
assert_eq!(part1(input), 1);
|
||||
assert_eq!(part2(input), 0);
|
||||
assert_eq!(Day2::part1(input.into()), 1);
|
||||
assert_eq!(Day2::part2(input.into()), 0);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue