Compare commits
2 Commits
bd91fcb60c
...
5e8b974137
Author | SHA1 | Date | |
---|---|---|---|
5e8b974137 | |||
28a88e1aa7 |
164
src/day14.rs
164
src/day14.rs
@ -1,12 +1,26 @@
|
|||||||
use aoc_runner_derive::aoc;
|
use aoc_runner_derive::aoc;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use grid::{AsCoord2d, Coord2d, Grid};
|
use grid::{AsCoord2d, Grid};
|
||||||
use regex::Regex;
|
use misc::CustomWrapped;
|
||||||
use std::str::FromStr;
|
use nom::{
|
||||||
|
bytes::complete::tag,
|
||||||
|
character::complete::digit1,
|
||||||
|
combinator::{map_res, opt, recognize},
|
||||||
|
sequence::{preceded, separated_pair},
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
type Coord = (CustomWrapped<i64>, CustomWrapped<i64>);
|
||||||
struct Robot {
|
struct Robot {
|
||||||
pos: Coord2d,
|
pos: Coord,
|
||||||
vel: Coord2d,
|
vel: (i64, i64),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Robots {
|
||||||
|
robots: Vec<Robot>,
|
||||||
|
width: i64,
|
||||||
|
height: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
@ -17,52 +31,37 @@ enum Quadrant {
|
|||||||
SE = 3,
|
SE = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Robot {
|
fn nom_i64(input: &str) -> IResult<&str, i64> {
|
||||||
type Err = Box<dyn std::error::Error>;
|
let (i, number) = map_res(recognize(preceded(opt(tag("-")), digit1)), i64::from_str)(input)?;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
Ok((i, number))
|
||||||
let re = Regex::new(r"p=(\d+),(\d+) v=([+-]?\d+),([+-]?\d+)").unwrap();
|
}
|
||||||
match re.captures(s) {
|
fn nom_i64_pair(input: &str) -> IResult<&str, (i64, i64)> {
|
||||||
Some(c) => Ok(Self {
|
let (i, pair) = separated_pair(nom_i64, tag(","), nom_i64)(input)?;
|
||||||
pos: (
|
Ok((i, pair))
|
||||||
c.get(1).unwrap().as_str().parse::<i64>().unwrap(),
|
|
||||||
c.get(2).unwrap().as_str().parse().unwrap(),
|
|
||||||
)
|
|
||||||
.to_coord(),
|
|
||||||
vel: (
|
|
||||||
c.get(3).unwrap().as_str().parse::<i64>().unwrap(),
|
|
||||||
c.get(4).unwrap().as_str().parse().unwrap(),
|
|
||||||
)
|
|
||||||
.to_coord(),
|
|
||||||
}),
|
|
||||||
None => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Robot {
|
impl Robot {
|
||||||
fn step(&mut self, bounds: (i64, i64)) {
|
fn from_str(s: &str, bounds: (i64, i64)) -> Self {
|
||||||
let mut candidate_new_pos = ((self.pos.x() + self.vel.x()), (self.pos.y() + self.vel.y()));
|
let (s, pos) = preceded(tag("p="), nom_i64_pair)(s).unwrap();
|
||||||
if candidate_new_pos.0 < 0 {
|
let (_, vel) = preceded(tag(" v="), nom_i64_pair)(s).unwrap();
|
||||||
// if pos goes negative, add the upper bound
|
Self {
|
||||||
candidate_new_pos.0 += bounds.0;
|
pos: (CustomWrapped::new(pos.0, bounds.0), CustomWrapped::new(pos.1, bounds.1)),
|
||||||
|
vel,
|
||||||
}
|
}
|
||||||
if candidate_new_pos.1 < 0 {
|
}
|
||||||
candidate_new_pos.1 += bounds.1;
|
fn step(&mut self, count: i64) {
|
||||||
}
|
self.pos.0 += self.vel.x() * count;
|
||||||
candidate_new_pos.0 %= bounds.0;
|
self.pos.1 += self.vel.y() * count;
|
||||||
candidate_new_pos.1 %= bounds.1;
|
|
||||||
|
|
||||||
self.pos = candidate_new_pos.to_coord();
|
|
||||||
}
|
}
|
||||||
fn quad(&self, bounds: (i64, i64)) -> Option<Quadrant> {
|
fn quad(&self, bounds: (i64, i64)) -> Option<Quadrant> {
|
||||||
let splits = (bounds.0 / 2, bounds.1 / 2);
|
let splits = (bounds.0 / 2, bounds.1 / 2);
|
||||||
if self.pos.x() < splits.0 && self.pos.y() < splits.1 {
|
if self.pos.0 < splits.0 && self.pos.1 < splits.1 {
|
||||||
Some(Quadrant::NW)
|
Some(Quadrant::NW)
|
||||||
} else if self.pos.x() > splits.0 && self.pos.y() < splits.1 {
|
} else if self.pos.0 > splits.0 && self.pos.1 < splits.1 {
|
||||||
Some(Quadrant::NE)
|
Some(Quadrant::NE)
|
||||||
} else if self.pos.x() < splits.0 && self.pos.y() > splits.1 {
|
} else if self.pos.0 < splits.0 && self.pos.1 > splits.1 {
|
||||||
Some(Quadrant::SW)
|
Some(Quadrant::SW)
|
||||||
} else if self.pos.x() > splits.0 && self.pos.y() > splits.1 {
|
} else if self.pos.0 > splits.0 && self.pos.1 > splits.1 {
|
||||||
Some(Quadrant::SE)
|
Some(Quadrant::SE)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -70,49 +69,58 @@ impl Robot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
impl Robots {
|
||||||
fn display(robots: &Vec<Robot>, bounds: (i64, i64)) {
|
fn from_vec(robots: Vec<Robot>, width: i64, height: i64) -> Self {
|
||||||
let grid = as_grid(robots, bounds);
|
Self { robots, width, height }
|
||||||
for row in 0..grid.height() {
|
}
|
||||||
for col in 0..grid.width() {
|
fn as_grid(&self) -> Grid<usize> {
|
||||||
print!(
|
let mut grid = Grid::with_shape(self.width as usize, self.height as usize, 0usize);
|
||||||
"{}",
|
for r in &self.robots {
|
||||||
if *grid.get(&(col, row)).unwrap() != 0 {
|
grid.increment(&(r.pos.0.val, r.pos.1.val), 1usize);
|
||||||
"█".green()
|
}
|
||||||
} else {
|
grid
|
||||||
" ".color(colored::Color::Black)
|
}
|
||||||
}
|
fn count_quads(&self) -> [u64; 4] {
|
||||||
);
|
let mut counts = [0; 4];
|
||||||
|
for r in &self.robots {
|
||||||
|
if let Some(q) = r.quad((self.width, self.height)) {
|
||||||
|
counts[q as usize] += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counts
|
||||||
|
}
|
||||||
|
fn step(&mut self, count: i64) {
|
||||||
|
for robot in &mut self.robots {
|
||||||
|
robot.step(count)
|
||||||
}
|
}
|
||||||
println!();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_grid(robots: &Vec<Robot>, bounds: (i64, i64)) -> Grid<usize> {
|
impl Display for Robots {
|
||||||
let mut grid = Grid::with_shape(bounds.0 as usize, bounds.1 as usize, 0);
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
for r in robots {
|
let grid = self.as_grid();
|
||||||
grid.increment(&r.pos, 1usize);
|
for row in 0..grid.height() {
|
||||||
|
for col in 0..grid.width() {
|
||||||
|
if *grid.get(&(col, row)).unwrap() != 0 {
|
||||||
|
"█".green().fmt(f)?;
|
||||||
|
} else {
|
||||||
|
" ".color(colored::Color::Black).fmt(f)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(f)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
grid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(input: &str) -> Vec<Robot> {
|
fn parse(input: &str, width: i64, height: i64) -> Vec<Robot> {
|
||||||
input.lines().map(|l| l.parse().unwrap()).collect()
|
input.lines().map(|l| Robot::from_str(l, (width, height))).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1_impl(input: &str, width: i64, height: i64) -> u64 {
|
fn part1_impl(input: &str, width: i64, height: i64) -> u64 {
|
||||||
let mut robots = parse(input);
|
let mut robots = Robots::from_vec(parse(input, width, height), width, height);
|
||||||
for _ in 0..100 {
|
robots.step(100);
|
||||||
for r in &mut robots {
|
let counts = robots.count_quads();
|
||||||
r.step((width, height))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut counts = [0; 4];
|
|
||||||
for r in robots {
|
|
||||||
if let Some(q) = r.quad((width, height)) {
|
|
||||||
counts[q as usize] += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
counts.iter().product()
|
counts.iter().product()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,13 +133,11 @@ pub fn part1(input: &str) -> u64 {
|
|||||||
pub fn part2(input: &str) -> u64 {
|
pub fn part2(input: &str) -> u64 {
|
||||||
let width = 101;
|
let width = 101;
|
||||||
let height = 103;
|
let height = 103;
|
||||||
let mut robots = parse(input);
|
let mut robots = Robots::from_vec(parse(input, width, height), width, height);
|
||||||
for i in 1.. {
|
for i in 1.. {
|
||||||
for r in &mut robots {
|
robots.step(1);
|
||||||
r.step((width, height))
|
|
||||||
}
|
|
||||||
// collect into lines
|
// collect into lines
|
||||||
let g = as_grid(&robots, (width, height));
|
let g = robots.as_grid();
|
||||||
if g.data
|
if g.data
|
||||||
.chunk_by(|a, b| *a != 0 && *b != 0)
|
.chunk_by(|a, b| *a != 0 && *b != 0)
|
||||||
.filter(|c| !c.is_empty() && c[0] != 0)
|
.filter(|c| !c.is_empty() && c[0] != 0)
|
||||||
|
@ -16,7 +16,7 @@ impl Display for Warehouse {
|
|||||||
|
|
||||||
impl Warehouse {
|
impl Warehouse {
|
||||||
fn step_robot(&mut self, dir: Move) {
|
fn step_robot(&mut self, dir: Move) {
|
||||||
let start = self.robot_pos.clone();
|
let start = self.robot_pos;
|
||||||
if self.push(&start, &dir) {
|
if self.push(&start, &dir) {
|
||||||
self.robot_pos = &self.robot_pos + dir.ofs();
|
self.robot_pos = &self.robot_pos + dir.ofs();
|
||||||
}
|
}
|
||||||
@ -40,12 +40,12 @@ impl Warehouse {
|
|||||||
// move both parts
|
// move both parts
|
||||||
self.push(&target, dir);
|
self.push(&target, dir);
|
||||||
self.push(&(&target + (-1, 0)), dir);
|
self.push(&(&target + (-1, 0)), dir);
|
||||||
self.map.swap(&target, pos);
|
self.map.swap(target, pos);
|
||||||
}
|
}
|
||||||
b'[' => {
|
b'[' => {
|
||||||
self.push(&target, dir);
|
self.push(&target, dir);
|
||||||
self.push(&(&target + (1, 0)), dir);
|
self.push(&(&target + (1, 0)), dir);
|
||||||
self.map.swap(&target, pos);
|
self.map.swap(target, pos);
|
||||||
}
|
}
|
||||||
c => panic!("unexpected char {}", c),
|
c => panic!("unexpected char {}", c),
|
||||||
}
|
}
|
||||||
|
26
src/day16.rs
26
src/day16.rs
@ -1,9 +1,9 @@
|
|||||||
use aoc_runner_derive::aoc;
|
use aoc_runner_derive::aoc;
|
||||||
use grid::Grid;
|
use grid::Grid;
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
collections::{BinaryHeap, HashMap},
|
collections::{BinaryHeap, HashMap},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
usize,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type CoordType = i16;
|
type CoordType = i16;
|
||||||
@ -73,7 +73,7 @@ impl Ord for PathState {
|
|||||||
}
|
}
|
||||||
impl PartialOrd for PathState {
|
impl PartialOrd for PathState {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
self.state.partial_cmp(&other.state)
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,14 +177,18 @@ impl Maze {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if state.position == finish {
|
if state.position == finish {
|
||||||
if state.cost < best_cost {
|
match state.cost.cmp(&best_cost) {
|
||||||
path.push(state.position);
|
Ordering::Less => {
|
||||||
best_paths.clear();
|
path.push(state.position);
|
||||||
best_paths.push(path);
|
best_paths.clear();
|
||||||
best_cost = state.cost
|
best_paths.push(path);
|
||||||
} else if state.cost == best_cost {
|
best_cost = state.cost
|
||||||
path.push(state.position);
|
}
|
||||||
best_paths.push(path);
|
Ordering::Equal => {
|
||||||
|
path.push(state.position);
|
||||||
|
best_paths.push(path);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -204,7 +208,7 @@ impl Maze {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (best_cost, best_paths);
|
(best_cost, best_paths)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use num_traits::Signed;
|
use num_traits::Signed;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use std::fmt::{Debug, Display};
|
|
||||||
|
|
||||||
/// Wrapped signed integer with custom upper bound with wrapping of 0s to the upper bound
|
/// Wrapped signed integer with custom upper bound with wrapping of 0s to the upper bound
|
||||||
#[derive(Eq, Clone, Copy)]
|
#[derive(Eq, Clone, Copy)]
|
||||||
@ -71,7 +71,10 @@ impl<T: Signed + PartialOrd + Copy> PartialOrd<T> for CustomWrapped<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Display + Signed + Copy> Display for CustomWrapped<T> where T: Display {
|
impl<T: Display + Signed + Copy> Display for CustomWrapped<T>
|
||||||
|
where
|
||||||
|
T: Display,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
self.val.fmt(f)
|
self.val.fmt(f)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user