Compare commits
No commits in common. "0dc10ff73c50241c6d35ad4d4e539c5e72fb7c8f" and "ebae29e209f8907539af674eb015a93d46379114" have entirely different histories.
0dc10ff73c
...
ebae29e209
|
@ -15,8 +15,6 @@ full = [
|
||||||
"day8",
|
"day8",
|
||||||
"day9",
|
"day9",
|
||||||
"day10",
|
"day10",
|
||||||
"day11",
|
|
||||||
"day12",
|
|
||||||
]
|
]
|
||||||
default = ["full"]
|
default = ["full"]
|
||||||
day1 = []
|
day1 = []
|
||||||
|
@ -29,8 +27,6 @@ day7 = []
|
||||||
day8 = []
|
day8 = []
|
||||||
day9 = []
|
day9 = []
|
||||||
day10 = []
|
day10 = []
|
||||||
day11 = []
|
|
||||||
day12 = []
|
|
||||||
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
125 17
|
|
|
@ -1,10 +0,0 @@
|
||||||
RRRRIICCFF
|
|
||||||
RRRRIICCCF
|
|
||||||
VVRRRCCFFF
|
|
||||||
VVRCCCJFFF
|
|
||||||
VVVVCJJCFE
|
|
||||||
VVIVCCJJEE
|
|
||||||
VVIIICJJEE
|
|
||||||
MIIIIIJJEE
|
|
||||||
MIIISIJEEE
|
|
||||||
MMMISSJEEE
|
|
|
@ -1,5 +0,0 @@
|
||||||
OOOOO
|
|
||||||
OXOXO
|
|
||||||
OOOOO
|
|
||||||
OXOXO
|
|
||||||
OOOOO
|
|
79
src/day11.rs
79
src/day11.rs
|
@ -1,79 +0,0 @@
|
||||||
#![allow(
|
|
||||||
clippy::cast_sign_loss,
|
|
||||||
clippy::cast_possible_wrap,
|
|
||||||
clippy::too_many_lines
|
|
||||||
)]
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
fn blink_v2(stones: HashMap<usize, usize>) -> HashMap<usize, usize> {
|
|
||||||
let mut res: HashMap<usize, usize> = HashMap::new();
|
|
||||||
for (stone, count) in stones {
|
|
||||||
match stone {
|
|
||||||
0 => {
|
|
||||||
res.insert(1, *(res.get(&1).unwrap_or(&0)) + count);
|
|
||||||
}
|
|
||||||
s if s.to_string().len() % 2 == 0 => {
|
|
||||||
let s_string = s.to_string();
|
|
||||||
let split = s_string.split_at(s_string.len() / 2);
|
|
||||||
let left_half = split.0.parse::<usize>().unwrap();
|
|
||||||
res.insert(left_half, *(res.get(&left_half).unwrap_or(&0)) + count);
|
|
||||||
let right_half = split.1.parse::<usize>().unwrap();
|
|
||||||
res.insert(right_half, *(res.get(&right_half).unwrap_or(&0)) + count);
|
|
||||||
}
|
|
||||||
v => {
|
|
||||||
let new_stone = v * 2024;
|
|
||||||
res.insert(new_stone, *(res.get(&new_stone).unwrap_or(&0)) + count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
pub fn pt1(input: &str) -> usize {
|
|
||||||
let mut stones: HashMap<usize, usize> = HashMap::new();
|
|
||||||
for stone in input
|
|
||||||
.split_whitespace()
|
|
||||||
.map(|s| s.parse::<usize>().unwrap())
|
|
||||||
{
|
|
||||||
stones.insert(stone, stones.get(&stone).unwrap_or(&0) + 1);
|
|
||||||
}
|
|
||||||
for step in 0..25 {
|
|
||||||
debug!("step {step}");
|
|
||||||
stones = blink_v2(stones);
|
|
||||||
}
|
|
||||||
|
|
||||||
stones.values().sum()
|
|
||||||
}
|
|
||||||
pub fn pt2(input: &str) -> usize {
|
|
||||||
let mut stones: HashMap<usize, usize> = HashMap::new();
|
|
||||||
for stone in input
|
|
||||||
.split_whitespace()
|
|
||||||
.map(|s| s.parse::<usize>().unwrap())
|
|
||||||
{
|
|
||||||
stones.insert(stone, stones.get(&stone).unwrap_or(&0) + 1);
|
|
||||||
}
|
|
||||||
for step in 0..75 {
|
|
||||||
debug!("step {step}");
|
|
||||||
stones = blink_v2(stones);
|
|
||||||
}
|
|
||||||
|
|
||||||
stones.values().sum()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use tracing_test::traced_test;
|
|
||||||
|
|
||||||
use super::pt1;
|
|
||||||
use std::fs::read_to_string;
|
|
||||||
|
|
||||||
#[traced_test]
|
|
||||||
#[test]
|
|
||||||
pub fn test_pt1() {
|
|
||||||
let input =
|
|
||||||
read_to_string("./inputs/day11/example.txt").expect("Missing example.txt for day11");
|
|
||||||
assert_eq!(pt1(&input), 55312)
|
|
||||||
}
|
|
||||||
}
|
|
188
src/day12.rs
188
src/day12.rs
|
@ -1,188 +0,0 @@
|
||||||
#![allow(
|
|
||||||
clippy::cast_sign_loss,
|
|
||||||
clippy::cast_possible_wrap,
|
|
||||||
clippy::too_many_lines
|
|
||||||
)]
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
pub fn pt1(input: &str) -> usize {
|
|
||||||
let mut res = 0;
|
|
||||||
let mut visited: HashSet<(usize, usize)> = HashSet::new();
|
|
||||||
let fields = input
|
|
||||||
.lines()
|
|
||||||
.enumerate()
|
|
||||||
.flat_map(|(y, line)| line.chars().enumerate().map(move |(x, c)| (c, x, y)))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
for &(c, x, y) in &fields {
|
|
||||||
if visited.contains(&(x, y)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut current_matching: HashSet<(usize, usize)> = HashSet::new();
|
|
||||||
current_matching.insert((x, y));
|
|
||||||
visited.insert((x, y));
|
|
||||||
let mut previous_matching = 0;
|
|
||||||
let mut cur_matching = current_matching.len();
|
|
||||||
while previous_matching != cur_matching {
|
|
||||||
let m = current_matching.clone();
|
|
||||||
let v = visited.clone();
|
|
||||||
for &(_cc, cx, cy) in fields
|
|
||||||
.iter()
|
|
||||||
.filter(|(cc, _, _)| *cc == c)
|
|
||||||
.filter(|(_cc, cx, cy)| !v.contains(&(*cx, *cy)))
|
|
||||||
.filter(|&&(_cc, cx, cy)| {
|
|
||||||
m.iter().any(|&(mx, my)| {
|
|
||||||
(mx.abs_diff(cx) == 1 && my == cy) || (mx == cx && my.abs_diff(cy) == 1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
{
|
|
||||||
current_matching.insert((cx, cy));
|
|
||||||
visited.insert((cx, cy));
|
|
||||||
}
|
|
||||||
previous_matching = cur_matching;
|
|
||||||
cur_matching = current_matching.len();
|
|
||||||
}
|
|
||||||
debug!("char: {c} -> current_matching = {current_matching:?}");
|
|
||||||
let area = current_matching.len();
|
|
||||||
let perimeter = get_perim(¤t_matching);
|
|
||||||
debug!("area: {area}, perimeter:{perimeter}");
|
|
||||||
res += area * perimeter;
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
fn get_perim(current_matching: &HashSet<(usize, usize)>) -> usize {
|
|
||||||
let mut perim = 0;
|
|
||||||
for &(x, y) in current_matching {
|
|
||||||
if x == 0 || !current_matching.contains(&(x - 1, y)) {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if y == 0 || !current_matching.contains(&(x, y - 1)) {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if !current_matching.contains(&(x + 1, y)) {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if !current_matching.contains(&(x, y + 1)) {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
perim
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_perimv2(current_matching: &HashSet<(usize, usize)>) -> usize {
|
|
||||||
let mut perim = 0;
|
|
||||||
for &(x, y) in current_matching {
|
|
||||||
let left_is_empty = x == 0 || !current_matching.contains(&(x - 1, y));
|
|
||||||
let top_is_empty = y == 0 || !current_matching.contains(&(x, y - 1));
|
|
||||||
let right_is_empty = !current_matching.contains(&(x + 1, y));
|
|
||||||
let bottom_is_empty = !current_matching.contains(&(x, y + 1));
|
|
||||||
let top_left_is_empty = x == 0 || y == 0 || !current_matching.contains(&(x - 1, y - 1));
|
|
||||||
let top_right_is_empty = y == 0 || !current_matching.contains(&(x + 1, y - 1));
|
|
||||||
let bottom_left_is_empty = x == 0 || !current_matching.contains(&(x - 1, y + 1));
|
|
||||||
let bottom_right_is_empty = !current_matching.contains(&(x + 1, y + 1));
|
|
||||||
if left_is_empty && top_is_empty {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if left_is_empty && bottom_is_empty {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if right_is_empty && top_is_empty {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if right_is_empty && bottom_is_empty {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if !top_is_empty && top_left_is_empty && !left_is_empty {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if !bottom_is_empty && bottom_left_is_empty && !left_is_empty {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if top_right_is_empty && !top_is_empty && !right_is_empty {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
if bottom_right_is_empty && !bottom_is_empty && !right_is_empty {
|
|
||||||
perim += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
perim
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pt2(input: &str) -> usize {
|
|
||||||
let mut res = 0;
|
|
||||||
let mut visited: HashSet<(usize, usize)> = HashSet::new();
|
|
||||||
let fields = input
|
|
||||||
.lines()
|
|
||||||
.enumerate()
|
|
||||||
.flat_map(|(y, line)| line.chars().enumerate().map(move |(x, c)| (c, x, y)))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
for &(c, x, y) in &fields {
|
|
||||||
if visited.contains(&(x, y)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut current_matching: HashSet<(usize, usize)> = HashSet::new();
|
|
||||||
current_matching.insert((x, y));
|
|
||||||
visited.insert((x, y));
|
|
||||||
let mut previous_matching = 0;
|
|
||||||
let mut cur_matching = current_matching.len();
|
|
||||||
while previous_matching != cur_matching {
|
|
||||||
let m = current_matching.clone();
|
|
||||||
let v = visited.clone();
|
|
||||||
for &(_cc, cx, cy) in fields
|
|
||||||
.iter()
|
|
||||||
.filter(|(cc, _, _)| *cc == c)
|
|
||||||
.filter(|(_cc, cx, cy)| !v.contains(&(*cx, *cy)))
|
|
||||||
.filter(|&&(_cc, cx, cy)| {
|
|
||||||
m.iter().any(|&(mx, my)| {
|
|
||||||
(mx.abs_diff(cx) == 1 && my == cy) || (mx == cx && my.abs_diff(cy) == 1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
{
|
|
||||||
current_matching.insert((cx, cy));
|
|
||||||
visited.insert((cx, cy));
|
|
||||||
}
|
|
||||||
previous_matching = cur_matching;
|
|
||||||
cur_matching = current_matching.len();
|
|
||||||
}
|
|
||||||
debug!("char: {c} -> current_matching = {current_matching:?}");
|
|
||||||
let area = current_matching.len();
|
|
||||||
let perimeter = get_perimv2(¤t_matching);
|
|
||||||
debug!("area: {area}, perimeter:{perimeter}");
|
|
||||||
res += area * perimeter;
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use tracing_test::traced_test;
|
|
||||||
|
|
||||||
use super::{pt1, pt2};
|
|
||||||
use std::fs::read_to_string;
|
|
||||||
|
|
||||||
#[traced_test]
|
|
||||||
#[test]
|
|
||||||
pub fn test_pt1() {
|
|
||||||
let input =
|
|
||||||
read_to_string("./inputs/day12/example.txt").expect("Missing example.txt for day12");
|
|
||||||
assert_eq!(pt1(&input), 1930)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[traced_test]
|
|
||||||
#[test]
|
|
||||||
pub fn test_pt1_2() {
|
|
||||||
let input = read_to_string("./inputs/day12/example_2.txt")
|
|
||||||
.expect("Missing example_2.txt for day12");
|
|
||||||
assert_eq!(pt1(&input), 772)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[traced_test]
|
|
||||||
#[test]
|
|
||||||
pub fn test_pt2() {
|
|
||||||
let input =
|
|
||||||
read_to_string("./inputs/day12/example.txt").expect("Missing example.txt for day12");
|
|
||||||
|
|
||||||
assert_eq!(pt2(&input), 1206)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,10 +23,6 @@ mod day08;
|
||||||
mod day09;
|
mod day09;
|
||||||
#[cfg(feature = "day10")]
|
#[cfg(feature = "day10")]
|
||||||
mod day10;
|
mod day10;
|
||||||
#[cfg(feature = "day11")]
|
|
||||||
mod day11;
|
|
||||||
#[cfg(feature = "day12")]
|
|
||||||
mod day12;
|
|
||||||
|
|
||||||
fn main() -> eyre::Result<()> {
|
fn main() -> eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
@ -53,10 +49,6 @@ fn main() -> eyre::Result<()> {
|
||||||
solve_day(9, day09::pt1, day09::pt2);
|
solve_day(9, day09::pt1, day09::pt2);
|
||||||
#[cfg(feature = "day10")]
|
#[cfg(feature = "day10")]
|
||||||
solve_day(10, day10::pt1, day10::pt2);
|
solve_day(10, day10::pt1, day10::pt2);
|
||||||
#[cfg(feature = "day11")]
|
|
||||||
solve_day(11, day11::pt1, day11::pt2);
|
|
||||||
#[cfg(feature = "day12")]
|
|
||||||
solve_day(12, day12::pt1, day12::pt2);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue