mirror of
https://github.com/mx42/aoc2024.rs.git
synced 2026-01-13 21:39: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 9](./src/bin/09.rs) | `897.8µs` | `549.0ms` |
|
||||
| [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 --->
|
||||
|
||||
---
|
||||
|
||||
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