Day 14
This commit is contained in:
parent
e291f48c84
commit
11999d177e
4 changed files with 224 additions and 0 deletions
61
2024/day14/Cargo.lock
generated
Normal file
61
2024/day14/Cargo.lock
generated
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day14"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
8
2024/day14/Cargo.toml
Normal file
8
2024/day14/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "day14"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lazy_static = "1.5.0"
|
||||||
|
regex = "1.11.1"
|
18
2024/day14/README.md
Normal file
18
2024/day14/README.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Solution Day 14
|
||||||
|
|
||||||
|
Again one of those I couldn't do without inspiration. Reading the input is easily enough with the regex crate.
|
||||||
|
Simulating the robots is also very easy, although I got it wrong in the first try. I initially thought `x %= WIDTH`
|
||||||
|
is enough to bring `x` back into the allowed range, completely forgetting about negative numbers. After fixing
|
||||||
|
this issue, the task is actually easy enough.
|
||||||
|
|
||||||
|
Task 2, however... "form the shape of a Christmas tree"... What? I didn't even have an idea what this would look like.
|
||||||
|
Just the outer edge? Filled? Centered? What means "most" line up to form the Christmas tree? I was at a complete
|
||||||
|
loss. Some inspiration from Reddit later, I assumed, that the Christmas tree might form an outline, centered around
|
||||||
|
`x = 50`, so I calculated the number of robots, where the distance from the center column was roughly half the
|
||||||
|
distance from the top of the map (basically any robot, that roughly lies on one of two lines falling
|
||||||
|
away from the center column). Didn't work at all. Not within the first 100,000 seconds.
|
||||||
|
|
||||||
|
After some more digging someone suggested, that the safety factor from task 1 might actually be the key. They
|
||||||
|
proposed to calculate the safety factor for each second and render the map every time a new minimum occured. This
|
||||||
|
assumed, that the robots would cluster together, leaving large parts of the map empty and therefore have a very
|
||||||
|
low safety factor. This does actually work. After a few false positives, the tree emerges in a rendered map.
|
137
2024/day14/src/main.rs
Normal file
137
2024/day14/src/main.rs
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
use std::{
|
||||||
|
i32,
|
||||||
|
io::{stdin, Read},
|
||||||
|
};
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
struct Robot {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
v_x: i32,
|
||||||
|
v_y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const WIDTH: i32 = 101;
|
||||||
|
const HEIGHT: i32 = 103;
|
||||||
|
|
||||||
|
impl Robot {
|
||||||
|
fn step(&mut self) {
|
||||||
|
self.x += self.v_x;
|
||||||
|
self.x %= WIDTH;
|
||||||
|
if self.x < 0 {
|
||||||
|
self.x += WIDTH;
|
||||||
|
};
|
||||||
|
self.y += self.v_y;
|
||||||
|
self.y %= HEIGHT;
|
||||||
|
if self.y < 0 {
|
||||||
|
self.y += HEIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ROBOT_REGEX: Regex = Regex::new(r"p=(\d+),(\d+)\s+v=([-\d]+),([-\d]+)").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_input() -> Vec<Robot> {
|
||||||
|
include_str!("../input.txt")
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, line)| {
|
||||||
|
let captures = ROBOT_REGEX
|
||||||
|
.captures(line)
|
||||||
|
.expect(format!("Failed to parse robot in line {idx}").as_str());
|
||||||
|
Robot {
|
||||||
|
x: captures
|
||||||
|
.get(1)
|
||||||
|
.and_then(|m| m.as_str().parse().ok())
|
||||||
|
.expect(format!("Couldn't parse x position in line {idx}").as_str()),
|
||||||
|
y: captures
|
||||||
|
.get(2)
|
||||||
|
.and_then(|m| m.as_str().parse().ok())
|
||||||
|
.expect(format!("Couldn't parse y position in line {idx}").as_str()),
|
||||||
|
v_x: captures
|
||||||
|
.get(3)
|
||||||
|
.and_then(|m| m.as_str().parse().ok())
|
||||||
|
.expect(format!("Couldn't parse x velocity in line {idx}").as_str()),
|
||||||
|
v_y: captures
|
||||||
|
.get(4)
|
||||||
|
.and_then(|m| m.as_str().parse().ok())
|
||||||
|
.expect(format!("Couldn't parse y velocity in line {idx}").as_str()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_safety_factor(robots: &Vec<Robot>) -> i32 {
|
||||||
|
let (q1, q2, q3, q4) = robots.iter().fold((0, 0, 0, 0), |(q1, q2, q3, q4), robot| {
|
||||||
|
if robot.x < 50 {
|
||||||
|
if robot.y < 51 {
|
||||||
|
return (q1 + 1, q2, q3, q4);
|
||||||
|
}
|
||||||
|
if robot.y > 51 {
|
||||||
|
return (q1, q2 + 1, q3, q4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if robot.x > 50 {
|
||||||
|
if robot.y < 51 {
|
||||||
|
return (q1, q2, q3 + 1, q4);
|
||||||
|
}
|
||||||
|
if robot.y > 51 {
|
||||||
|
return (q1, q2, q3, q4 + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(q1, q2, q3, q4)
|
||||||
|
});
|
||||||
|
|
||||||
|
q1 * q2 * q3 * q4
|
||||||
|
}
|
||||||
|
|
||||||
|
fn task1() {
|
||||||
|
let mut robots = load_input();
|
||||||
|
|
||||||
|
for i in 0..100 {
|
||||||
|
robots.iter_mut().for_each(|r| r.step());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Task 1: safety factor: {}",
|
||||||
|
calculate_safety_factor(&robots)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_map(robots: &Vec<Robot>) {
|
||||||
|
for y in 0..HEIGHT {
|
||||||
|
for x in 0..WIDTH {
|
||||||
|
if robots.iter().any(|r| r.x == x && r.y == y) {
|
||||||
|
print!("#");
|
||||||
|
} else {
|
||||||
|
print!(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn task2() {
|
||||||
|
let mut robots = load_input();
|
||||||
|
let mut min_safety = i32::MAX;
|
||||||
|
for sec in 1..1000000 {
|
||||||
|
robots.iter_mut().for_each(|r| r.step());
|
||||||
|
|
||||||
|
let safety = calculate_safety_factor(&robots);
|
||||||
|
if safety < min_safety {
|
||||||
|
min_safety = safety;
|
||||||
|
render_map(&robots);
|
||||||
|
println!("Seconds: {sec}");
|
||||||
|
stdin().read(&mut [0u8]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
task1();
|
||||||
|
task2();
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue