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