mirror of
https://github.com/mx42/aoc2024.rs.git
synced 2026-01-14 05:49:53 +01:00
feat: add day 14
This commit is contained in:
@@ -39,8 +39,12 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
|
|||||||
| [Day 8](./src/bin/08.rs) | `110.0µs` | `395.4µs` |
|
| [Day 8](./src/bin/08.rs) | `110.0µs` | `395.4µs` |
|
||||||
| [Day 9](./src/bin/09.rs) | `897.8µs` | `549.0ms` |
|
| [Day 9](./src/bin/09.rs) | `897.8µs` | `549.0ms` |
|
||||||
| [Day 10](./src/bin/10.rs) | `836.4µs` | `1.6ms` |
|
| [Day 10](./src/bin/10.rs) | `836.4µs` | `1.6ms` |
|
||||||
|
| [Day 11](./src/bin/11.rs) | `829.0ns` | `1.0µs` |
|
||||||
|
| [Day 12](./src/bin/12.rs) | `332.4ms` | `318.8ms` |
|
||||||
|
| [Day 13](./src/bin/13.rs) | `133.3µs` | `92.0µs` |
|
||||||
|
| [Day 14](./src/bin/14.rs) | `210.1µs` | `105.4ms` |
|
||||||
|
|
||||||
**Total: 12226.66ms**
|
**Total: 12983.70ms**
|
||||||
<!--- benchmarking table --->
|
<!--- benchmarking table --->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
12
data/examples/14.txt
Normal file
12
data/examples/14.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
p=0,4 v=3,-3
|
||||||
|
p=6,3 v=-1,-3
|
||||||
|
p=10,3 v=-1,2
|
||||||
|
p=2,0 v=2,-1
|
||||||
|
p=0,0 v=1,3
|
||||||
|
p=3,0 v=-2,-2
|
||||||
|
p=7,6 v=-1,-3
|
||||||
|
p=3,0 v=-1,-2
|
||||||
|
p=9,3 v=2,3
|
||||||
|
p=7,3 v=-1,2
|
||||||
|
p=2,4 v=2,-3
|
||||||
|
p=9,5 v=-3,-3
|
||||||
214
src/bin/14.rs
Normal file
214
src/bin/14.rs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
advent_of_code::solution!(14);
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::iter::successors;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
struct Pos {
|
||||||
|
x: u16,
|
||||||
|
y: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pos {
|
||||||
|
fn add_with_wrap(&self, v: &Velocity, wrap: &Pos) -> Self {
|
||||||
|
Self {
|
||||||
|
x: (((wrap.x as i16) + (self.x as i16) + (v.x as i16)) as u16) % wrap.x,
|
||||||
|
y: (((wrap.y as i16) + (self.y as i16) + (v.y as i16)) as u16) % wrap.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&&str> for Pos {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(input: &&str) -> Result<Pos, Self::Error> {
|
||||||
|
let coords: Vec<u16> = input[2..]
|
||||||
|
.split(",")
|
||||||
|
.map(|s| s.parse::<u16>().map_err(|_| ()))
|
||||||
|
.collect::<Result<_, ()>>()?;
|
||||||
|
let coords = coords.as_slice();
|
||||||
|
match coords {
|
||||||
|
[x, y] => Ok(Self { x: *x, y: *y }),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Velocity {
|
||||||
|
x: i8,
|
||||||
|
y: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&&str> for Velocity {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(input: &&str) -> Result<Self, Self::Error> {
|
||||||
|
let coords: Vec<i8> = input[2..]
|
||||||
|
.split(",")
|
||||||
|
.map(|s| s.parse::<i8>().map_err(|_| ()))
|
||||||
|
.collect::<Result<_, ()>>()?;
|
||||||
|
let coords = coords.as_slice();
|
||||||
|
match coords {
|
||||||
|
[x, y] => Ok(Self { x: *x, y: *y }),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Bot {
|
||||||
|
p: Pos,
|
||||||
|
v: Velocity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bot {
|
||||||
|
fn next(&self, wrap: &Pos) -> Self {
|
||||||
|
Self {
|
||||||
|
v: self.v.clone(),
|
||||||
|
p: self.p.add_with_wrap(&self.v, wrap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Bot {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||||
|
let parts = input.split_whitespace().collect::<Vec<_>>();
|
||||||
|
let parts = parts.as_slice();
|
||||||
|
match parts {
|
||||||
|
[p, v] => Ok(Self {
|
||||||
|
p: Pos::try_from(p).map_err(|_| ())?,
|
||||||
|
v: Velocity::try_from(v).map_err(|_| ())?,
|
||||||
|
}),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
bots: Vec<Bot>,
|
||||||
|
size: Pos,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for State {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
bots: input
|
||||||
|
.lines()
|
||||||
|
.map(Bot::try_from)
|
||||||
|
.collect::<Result<Vec<Bot>, ()>>()?,
|
||||||
|
size: Pos { x: 101, y: 103 },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for State {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
for y in 0..self.size.y {
|
||||||
|
for x in 0..self.size.x {
|
||||||
|
let p = Pos { x, y };
|
||||||
|
if self.bots.iter().any(|b| b.p == p) {
|
||||||
|
write!(f, "*")?;
|
||||||
|
} else {
|
||||||
|
write!(f, " ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn anomaly(&self) -> bool {
|
||||||
|
let mut ys: HashMap<u16, usize> = HashMap::new();
|
||||||
|
let mut xs: HashMap<u16, usize> = HashMap::new();
|
||||||
|
|
||||||
|
self.bots.iter().for_each(|b| {
|
||||||
|
*ys.entry(b.p.y).or_default() += 1;
|
||||||
|
*xs.entry(b.p.x).or_default() += 1;
|
||||||
|
});
|
||||||
|
let mut xs = xs.into_values().collect::<Vec<usize>>();
|
||||||
|
let mut ys = ys.into_values().collect::<Vec<usize>>();
|
||||||
|
ys.sort();
|
||||||
|
xs.sort();
|
||||||
|
ys[ys.len() - 1] > 30 && xs[xs.len() - 1] > 30
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quadrants(&self) -> [u32; 4] {
|
||||||
|
self.bots
|
||||||
|
.iter()
|
||||||
|
.map(|b| b.p.clone())
|
||||||
|
.fold([0, 0, 0, 0], |[ul, ur, ll, lr], p| match p {
|
||||||
|
Pos { x: px, y: py } if px < self.size.x / 2 && py < self.size.y / 2 => {
|
||||||
|
[ul + 1, ur, ll, lr]
|
||||||
|
}
|
||||||
|
Pos { x: px, y: py } if px < self.size.x / 2 && py > self.size.y / 2 => {
|
||||||
|
[ul, ur, ll + 1, lr]
|
||||||
|
}
|
||||||
|
Pos { x: px, y: py } if px > self.size.x / 2 && py < self.size.y / 2 => {
|
||||||
|
[ul, ur + 1, ll, lr]
|
||||||
|
}
|
||||||
|
Pos { x: px, y: py } if px > self.size.x / 2 && py > self.size.y / 2 => {
|
||||||
|
[ul, ur, ll, lr + 1]
|
||||||
|
}
|
||||||
|
_ => [ul, ur, ll, lr],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&self) -> Option<State> {
|
||||||
|
Some(Self {
|
||||||
|
bots: self
|
||||||
|
.bots
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|b| b.next(&self.size))
|
||||||
|
.collect(),
|
||||||
|
size: self.size.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
successors(State::try_from(input).ok(), |st| st.next())
|
||||||
|
.nth(100)
|
||||||
|
.unwrap()
|
||||||
|
.quadrants()
|
||||||
|
.iter()
|
||||||
|
.product::<u32>()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
successors(State::try_from(input).ok(), |s| s.next())
|
||||||
|
.enumerate()
|
||||||
|
.find(|(i, st)| st.anomaly() || *i > 10000)
|
||||||
|
.map(|(i, st)| {
|
||||||
|
if st.anomaly() {
|
||||||
|
println!("{:?}", st);
|
||||||
|
Some(i)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(21));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user