Compare commits

..

10 Commits

Author SHA1 Message Date
22d9f7c42a Day 17, part 2 2022-12-20 15:59:53 +01:00
1e7eb12da9 Day 17, part 1 2022-12-20 14:35:12 +01:00
06786817f3 More tries at day 16 2022-12-20 00:45:14 +01:00
46d409fa92 First attempt at day 16 2022-12-20 00:44:56 +01:00
0d30968d3e Multithread it 2022-12-16 11:07:36 +01:00
62e9d17eff Day 15, puzzle 2 2022-12-16 10:54:48 +01:00
619c77aab7 Day 15, puzzle 1 2022-12-16 10:48:51 +01:00
d05780e64a Day 14, puzzle 2 2022-12-16 09:52:48 +01:00
215e067d33 Day 14, puzzle 1 2022-12-16 09:46:19 +01:00
6d3686c946 Fix warning 2022-12-16 08:43:14 +01:00
16 changed files with 1005 additions and 1 deletions

14
Cargo.lock generated
View File

@ -17,8 +17,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"itertools", "itertools",
"lazy_static",
"log", "log",
"num", "num",
"once_cell",
"regex", "regex",
] ]
@ -138,6 +140,12 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.138" version = "0.2.138"
@ -243,6 +251,12 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "once_cell"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.4.6" version = "0.4.6"

View File

@ -11,6 +11,8 @@ itertools = "^0.10"
log = "^0.4" log = "^0.4"
env_logger = "^0.10" env_logger = "^0.10"
num = "^0.1" num = "^0.1"
lazy_static = "^1.4"
once_cell = "^1.16"
[[bin]] [[bin]]
name = "d1p1" name = "d1p1"

17
src/bin/d14p1.rs Normal file
View File

@ -0,0 +1,17 @@
use aoc22::{day14, util};
pub fn main() {
let mut cave = day14::parse_cave(&util::parse_input(), false);
println!("Initial cave:");
cave.print();
let mut sand = 0;
while cave.drop_sand() {
sand += 1;
}
println!("Final cave:");
cave.print();
println!("Total sand dropped: {}", sand);
}

17
src/bin/d14p2.rs Normal file
View File

@ -0,0 +1,17 @@
use aoc22::{day14, util};
pub fn main() {
let mut cave = day14::parse_cave(&util::parse_input(), true);
println!("Initial cave:");
cave.print();
let mut sand = 0;
while cave.drop_sand() {
sand += 1;
}
println!("Final cave:");
cave.print();
println!("Total sand dropped: {}", sand);
}

39
src/bin/d15p1.rs Normal file
View File

@ -0,0 +1,39 @@
use aoc22::{day15, util};
use itertools::Itertools;
const ROW: isize = 2000000;
pub fn main() {
let sensors = day15::parse_sensors(&util::parse_input());
let covered = sensors
.iter()
.map(|s| s.covered(ROW))
.filter(|r| !r.is_empty())
.sorted_by_key(|r| *r.start());
let mut max_x = isize::MIN;
let mut total_covered = 0;
for range in covered {
let (min, max) = range.into_inner();
if min > max_x {
total_covered += max - min + 1;
} else if max > max_x {
total_covered += max - max_x;
}
max_x = max_x.max(max);
}
let n_beacons = sensors
.iter()
.map(|s| s.loc_b)
.filter(|b| b.0 == ROW)
.unique()
.count() as isize;
println!(
"Impossible positions in row {}: {}",
ROW,
total_covered - n_beacons
);
}

70
src/bin/d15p2.rs Normal file
View File

@ -0,0 +1,70 @@
use std::{
sync::{Arc, Mutex},
thread,
};
use aoc22::{day15, util};
use itertools::Itertools;
const COORD_MAX: isize = 4000000;
const NUM_THREADS: usize = 16;
pub fn main() {
let sensors = Arc::new(day15::parse_sensors(&util::parse_input()));
let mut handles = vec![];
let next_row = Arc::new(Mutex::new(0));
for _ in 0..NUM_THREADS {
let sensors = sensors.clone();
let next_row = next_row.clone();
let handle = thread::spawn(move || find_possible(&sensors, next_row));
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
pub fn find_possible(sensors: &Vec<day15::Sensor>, next_row: Arc<Mutex<isize>>) {
loop {
let row = {
let mut handle = next_row.lock().unwrap();
let result = *handle;
if result > COORD_MAX {
return;
}
*handle += 1;
result
};
let covered = sensors
.iter()
.map(|s| s.covered(row))
.filter(|r| !r.is_empty())
.sorted_by_key(|r| *r.start());
let mut max_x = isize::MIN;
for range in covered {
let (min, max) = range.into_inner();
if min == max_x + 2 {
let x = max_x + 1;
println!(
"x = {}, y = {}, tuning freq = {}",
x,
row,
x * 4000000 + row
)
} else if max_x != isize::MIN && min > max_x + 2 {
panic!(
"More than one coordinate free in row {}: {}..{}",
row,
max_x + 1,
min - 1
);
}
max_x = max_x.max(max);
}
}
}

73
src/bin/d16p1.rs Normal file
View File

@ -0,0 +1,73 @@
use std::{
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
thread,
};
use aoc22::{day16, util};
pub fn main() {
let valves = Arc::new(day16::parse_valves(&util::parse_input()));
let dists = Arc::new(day16::calc_dists(&valves));
let state = day16::State::new(&valves);
let possible_states = Arc::new(Mutex::new(Vec::new()));
possible_states.lock().unwrap().push(state);
let lower_bound = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for _ in 0..16 {
let s = possible_states.clone();
let l = lower_bound.clone();
let v = valves.clone();
let d = dists.clone();
handles.push(thread::spawn(move || check_states(s, l, v, d)));
}
for handle in handles {
handle.join().unwrap();
}
println!(
"Most pressure released is {}",
lower_bound.load(Ordering::Relaxed)
);
}
pub fn check_states(
possible_states: Arc<Mutex<Vec<day16::State>>>,
lower_bound: Arc<AtomicUsize>,
valves: Arc<Vec<day16::Valve>>,
dists: Arc<Vec<Vec<usize>>>,
) {
let mut i = 0;
loop {
i += 1;
let state = { possible_states.lock().unwrap().pop() };
if state.is_none() {
break;
}
let state = state.unwrap();
if state.finished() {
let score = state.lower_bound();
dbg!(score);
lower_bound.fetch_max(score, Ordering::Relaxed);
} else {
let x = day16::possible_actions(&state, &valves, &dists);
// let x = state.possible_actions(&valves, &dists);
// let state_upper = state.upper_bound(&valves, &dists);
for action in x {
let action_upper = action.upper_bound(&valves, &dists);
// assert!(action_upper <= state_upper);
if action_upper > lower_bound.load(Ordering::Relaxed) {
possible_states.lock().unwrap().push(action);
}
}
}
}
dbg!(i);
}

13
src/bin/d17p1.rs Normal file
View File

@ -0,0 +1,13 @@
use aoc22::{day17, util};
const BLOCKS: usize = 2022;
pub fn main() {
let jets = day17::parse_jets(&util::parse_input());
println!(
"Height after {} blocks: {}",
BLOCKS,
day17::do_moves(&jets, BLOCKS)
);
}

13
src/bin/d17p2.rs Normal file
View File

@ -0,0 +1,13 @@
use aoc22::{day17, util};
const BLOCKS: usize = 1_000_000_000_000;
pub fn main() {
let jets = day17::parse_jets(&util::parse_input());
println!(
"Height after {} blocks: {}",
BLOCKS,
day17::do_moves(&jets, BLOCKS)
);
}

View File

@ -175,7 +175,7 @@ pub fn do_round(monkeys: &Vec<RefCell<Monkey>>, do_worry: bool) {
item, item,
target target
); );
let mut target = &mut monkeys.get(target).unwrap().borrow_mut().items; let target = &mut monkeys.get(target).unwrap().borrow_mut().items;
target.push_back(item); target.push_back(item);
} }
} }

135
src/day14.rs Normal file
View File

@ -0,0 +1,135 @@
use std::collections::HashSet;
use itertools::Itertools;
use crate::util::Coord;
pub const SAND_SOURCE: Coord = (0, 500);
pub struct Cave {
pub rows: usize,
pub cols: usize,
pub blocked: Vec<Vec<bool>>,
pub sand_source: Coord,
}
impl Cave {
pub fn print(&self) {
for row in 0..self.rows {
for col in 0..self.cols {
if (row, col) == self.sand_source {
print!("+")
} else if self.blocked[row][col] {
print!("#");
} else {
print!(".");
}
}
print!("\n")
}
}
pub fn drop_sand(&mut self) -> bool {
let (y, x) = self.sand_source;
let mut y = y as isize;
let mut x = x as isize;
while y < self.rows as isize
&& x > 0
&& x < self.cols as isize
&& !self.is_blocked(self.sand_source.0 as isize, self.sand_source.1 as isize)
{
if !self.is_blocked(y + 1, x) {
y += 1;
} else if !self.is_blocked(y + 1, x - 1) {
y += 1;
x -= 1;
} else if !self.is_blocked(y + 1, x + 1) {
y += 1;
x += 1;
} else {
self.blocked[y as usize][x as usize] = true;
return true;
}
}
false
}
fn is_blocked(&self, y: isize, x: isize) -> bool {
!(y < 0 || y >= self.rows as isize || x < 0 || x >= self.cols as isize)
&& self.blocked[y as usize][x as usize]
}
}
pub fn parse_cave(input: &String, do_floor: bool) -> Cave {
let mut paths = HashSet::new();
for line in input.lines() {
let mut path = line.split("->").map(|n| parse_coord(n.trim()));
let mut last = path.next().unwrap();
paths.insert(last);
for next in path {
let y_min = next.0.min(last.0);
let y_max = next.0.max(last.0);
let x_min = next.1.min(last.1);
let x_max = next.1.max(last.1);
let horizontal = y_min == y_max;
let vertical = x_min == x_max;
assert!(horizontal ^ vertical);
if horizontal {
for x in x_min..=x_max {
paths.insert((y_min, x));
}
} else {
for y in y_min..=y_max {
paths.insert((y, x_max));
}
}
last = next;
}
}
assert_ne!(paths.len(), 0);
let (mut y_min, mut y_max) = paths.iter().map(|c| c.0).minmax().into_option().unwrap();
let (mut x_min, mut x_max) = paths.iter().map(|c| c.1).minmax().into_option().unwrap();
// Sand source is always at y = 0, x = 500
y_min = y_min.min(SAND_SOURCE.0);
x_min = x_min.min(SAND_SOURCE.1);
x_max = x_max.max(SAND_SOURCE.1);
if do_floor {
y_max += 2;
let dy = y_max - y_min;
x_min = x_min.min(SAND_SOURCE.1 - dy - 1);
x_max = x_max.max(SAND_SOURCE.1 + dy + 1);
for x in x_min..x_max {
paths.insert((y_max, x));
}
}
let rows = y_max - y_min + 1;
let cols = x_max - x_min + 1;
let mut blocked = vec![vec![false; cols]; rows];
for coord in paths {
blocked[coord.0 - y_min][coord.1 - x_min] = true;
}
Cave {
rows,
cols,
blocked,
sand_source: (SAND_SOURCE.0 - y_min, SAND_SOURCE.1 - x_min),
}
}
fn parse_coord(input: &str) -> Coord {
let (x, y) = input.split_once(',').unwrap();
(y.parse().unwrap(), x.parse().unwrap())
}

50
src/day15.rs Normal file
View File

@ -0,0 +1,50 @@
use std::ops::RangeInclusive;
use regex::Regex;
use crate::util::{Coordinate, SignedCoord};
#[derive(Debug)]
pub struct Sensor {
pub loc_s: SignedCoord,
pub loc_b: SignedCoord,
pub range: usize,
}
impl Sensor {
pub fn covered(&self, y: isize) -> RangeInclusive<isize> {
let dy = self.loc_s.0.abs_diff(y);
if dy > self.range {
1..=0
} else {
let range_x = (self.range - dy) as isize;
(self.loc_s.1 - range_x)..=(self.loc_s.1 + range_x)
}
}
}
pub fn parse_sensors(input: &String) -> Vec<Sensor> {
let mut result = Vec::new();
let re =
Regex::new(r"^Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)$")
.unwrap();
for line in input.lines() {
let captures = re.captures(line).unwrap();
let loc_s: SignedCoord = (
captures.get(2).unwrap().as_str().parse().unwrap(),
captures.get(1).unwrap().as_str().parse().unwrap(),
);
let loc_b: SignedCoord = (
captures.get(4).unwrap().as_str().parse().unwrap(),
captures.get(3).unwrap().as_str().parse().unwrap(),
);
result.push(Sensor {
loc_s,
loc_b,
range: loc_s.manhattan(&loc_b),
});
}
result
}

202
src/day16.rs Normal file
View File

@ -0,0 +1,202 @@
use std::{
collections::{HashMap, HashSet, VecDeque},
hash::Hash,
sync::Mutex,
};
use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
const TOTAL_TIME: usize = 30;
#[derive(Debug, Clone)]
pub struct Valve {
pub flow_rate: usize,
pub tunnels: Vec<usize>,
}
pub fn parse_valves(input: &String) -> Vec<Valve> {
let indices: HashMap<String, usize> = input
.lines()
.map(|l| l.split(' ').nth(1).unwrap())
.sorted()
.enumerate()
.map(|(i, n)| (n.to_owned(), i))
.collect();
let mut valves = HashMap::new();
let re =
Regex::new(r"^Valve (\w+) has flow rate=(\d+); tunnels? leads? to valves? (.*)$").unwrap();
for line in input.lines() {
let captures = re
.captures(line)
.expect(&format!("No captures for {}", line));
let name = captures.get(1).unwrap().as_str().to_owned();
let flow_rate = captures.get(2).unwrap().as_str().parse().unwrap();
let tunnels = captures
.get(3)
.unwrap()
.as_str()
.split(", ")
.map(|s| indices[s])
.collect();
let valve = Valve { flow_rate, tunnels };
valves.insert(name, valve);
}
valves
.iter()
.map(|(name, v)| (indices[name], v))
.sorted_by_key(|(i, _)| *i)
.map(|(_, v)| v.clone())
.collect()
}
pub fn calc_dists(valves: &Vec<Valve>) -> Vec<Vec<usize>> {
let n = valves.len();
let mut result = vec![vec![usize::MAX; n]; n];
for i in 0..n {
result[i][i] = 0;
let mut next = VecDeque::new();
next.push_back(i);
while let Some(j) = next.pop_front() {
let d_next = result[i][j] + 1;
for j in &valves[j].tunnels {
let d_tunnel = result[i].get_mut(*j).unwrap();
if d_next < *d_tunnel {
*d_tunnel = d_next;
next.push_back(*j);
}
}
}
}
result
}
#[derive(Debug, Clone)]
pub struct State {
pub opened: u64,
pub location: usize,
pub flow_rate: usize,
pub time_remaining: usize,
pub pressure_released: usize,
interesting: HashSet<usize>,
}
impl State {
pub fn new(valves: &Vec<Valve>) -> State {
let interesting = valves
.iter()
.enumerate()
.filter_map(|(i, v)| if v.flow_rate > 0 { Some(i) } else { None })
.collect();
State {
opened: 0,
location: 0, // "AA" has to be the first valve alphabetically
flow_rate: 0,
time_remaining: TOTAL_TIME,
pressure_released: 0,
interesting,
}
}
pub fn finished(&self) -> bool {
self.time_remaining == 0 || self.interesting.is_empty()
}
pub fn lower_bound(&self) -> usize {
self.pressure_released + self.flow_rate * self.time_remaining
}
pub fn upper_bound(&self, valves: &Vec<Valve>, dists: &Vec<Vec<usize>>) -> usize {
let mut additional_flow = 0;
for i in &self.interesting {
let i = i.clone();
if (self.opened & (1 << i)) != 0 {
continue;
}
let dist = dists[self.location][i];
if dist < self.time_remaining {
let flow = valves[i].flow_rate;
additional_flow += flow * (self.time_remaining - dist - 1);
}
}
self.lower_bound() + additional_flow
}
pub fn possible_actions(&self, valves: &Vec<Valve>, dists: &Vec<Vec<usize>>) -> Vec<State> {
assert!(!self.finished());
let mut result = Vec::new();
let loc = self.location;
if (self.opened & (1 << loc)) == 0 && self.interesting.contains(&loc) {
let mut open = self.clone();
open.opened |= 1 << loc;
// First increase pressure released, then the flow rate
open.pressure_released += open.flow_rate;
open.flow_rate += valves[loc].flow_rate;
open.time_remaining -= 1;
open.interesting.remove(&loc);
result.push(open);
}
for l in &self.interesting {
let i = l.clone();
let dist = dists[loc][i];
if dist == 0 || dist == usize::MAX || self.time_remaining < dist {
continue;
}
let mut moved = self.clone();
moved.location = i;
moved.pressure_released += moved.flow_rate * dist;
moved.time_remaining -= dist;
result.push(moved);
}
result
}
}
impl Hash for State {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.opened.hash(state);
self.location.hash(state);
self.time_remaining.hash(state);
self.pressure_released.hash(state);
}
}
impl PartialEq for State {
fn eq(&self, other: &Self) -> bool {
self.opened == other.opened
&& self.location == other.location
&& self.time_remaining == other.time_remaining
&& self.pressure_released == other.pressure_released
}
}
impl Eq for State {}
pub fn possible_actions(state: &State, valves: &Vec<Valve>, dists: &Vec<Vec<usize>>) -> Vec<State> {
lazy_static! {
static ref CACHE: Mutex<HashMap<State, Vec<State>>> = Mutex::new(HashMap::new());
}
let actions = { CACHE.lock().unwrap().get(state).cloned() };
if let Some(actions) = actions {
// assert_eq!(actions, state.possible_actions(valves, dists));
actions.clone()
} else {
let mut cache = CACHE.lock().unwrap();
cache.insert(state.clone(), state.possible_actions(valves, dists));
cache[state].clone()
}
}

317
src/day17.rs Normal file
View File

@ -0,0 +1,317 @@
use std::collections::HashMap;
use once_cell::sync::OnceCell;
use crate::util;
const VENT_WIDTH: usize = 7;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Jet {
Left,
Right,
}
impl Jet {
pub fn shift(&self) -> isize {
match &self {
Jet::Left => 1,
Jet::Right => -1,
}
}
}
pub fn parse_jets(input: &String) -> Vec<Jet> {
assert_eq!(input.lines().count(), 1);
input
.chars()
.filter_map(|c| match c {
'<' => Some(Jet::Left),
'>' => Some(Jet::Right),
'\n' => None,
_ => panic!("Unknown jet {}", c),
})
.collect()
}
#[derive(Clone, Copy, Debug)]
pub enum BlockKind {
Dash = 0,
Plus,
L,
Pipe,
Square,
}
impl BlockKind {
pub fn from_num(n: usize) -> BlockKind {
match n {
0 => BlockKind::Dash,
1 => BlockKind::Plus,
2 => BlockKind::L,
3 => BlockKind::Pipe,
4 => BlockKind::Square,
_ => panic!("Unknown block kind {}", n),
}
}
pub fn height(&self) -> usize {
match self {
BlockKind::Dash => 1,
BlockKind::Plus => 3,
BlockKind::L => 3,
BlockKind::Pipe => 4,
BlockKind::Square => 2,
}
}
pub fn width(&self) -> usize {
match self {
BlockKind::Dash => 4,
BlockKind::Plus => 3,
BlockKind::L => 3,
BlockKind::Pipe => 1,
BlockKind::Square => 2,
}
}
pub fn vent_rows(&self) -> &'static Vec<VentRow> {
static ROWS: OnceCell<Vec<Vec<VentRow>>> = OnceCell::new();
let rows = ROWS.get_or_init(|| {
vec![
vec![0b1111], // Dash
vec![0b010, 0b111, 0b010], // Plus
vec![0b001, 0b001, 0b111], // L
vec![0b1; 4], // Pipe
vec![0b11, 0b11], // Square
]
});
&rows
.get(*self as usize)
.unwrap_or_else(|| panic!("Unknown BlockKind: {:?}", self))
}
}
const N_BLOCK_TYPES: usize = 5;
pub struct Block {
kind: BlockKind,
pos: util::Coord,
}
impl Block {
pub fn spawn(&self, vent: &mut Vec<VentRow>) {
for (i, row) in self.kind.vent_rows().iter().rev().enumerate() {
vent[self.pos.0 + i] |= *row << self.shift_dist();
}
}
pub fn row_range(&self) -> std::ops::Range<usize> {
let row = self.pos.0;
row..(row + self.kind.height())
}
pub fn can_push(&self, vent: &Vec<VentRow>, jet: Jet) -> bool {
if (jet == Jet::Left && self.pos.1 == 0)
|| (jet == Jet::Right && self.pos.1 + self.kind.width() >= VENT_WIDTH)
{
return false;
}
let shift = self.shift_dist() as isize + jet.shift();
for (i, row) in self.row_range().rev().enumerate() {
let bitmask = self.kind.vent_rows()[i] << shift;
if vent[row] & bitmask != 0 {
return false;
}
}
true
}
pub fn can_fall(&self, vent: &Vec<VentRow>) -> bool {
if self.pos.0 == 0 {
return false;
}
for (i, row) in self.row_range().rev().enumerate() {
let bitmask = self.kind.vent_rows()[i] << self.shift_dist();
if vent[row - 1] & bitmask != 0 {
return false;
}
}
true
}
pub fn shift_dist(&self) -> usize {
VENT_WIDTH - self.pos.1 - self.kind.width()
}
}
type VentRow = u8;
const CYCLE_SIZE: usize = 5;
pub fn do_moves(jets: &Vec<Jet>, n_blocks: usize) -> usize {
let mut block_i = 0;
let mut current_block: Option<Block> = None;
let mut vent = vec![0; 4];
// {jet_i: {round: (height, block_i)}}
let mut block_cycles: HashMap<usize, HashMap<usize, (usize, usize)>> = HashMap::new();
let mut skipped_height: Option<usize> = None;
let mut round = 0;
while block_i <= n_blocks {
let jet_i = round % jets.len();
if current_block.is_none() {
if block_i % N_BLOCK_TYPES == 0 {
if skipped_height.is_none() {
let entry = block_cycles.entry(jet_i).or_insert_with(HashMap::new);
entry.insert(round, (vent.len() - empty_rows(&vent), block_i));
if let Some((cycle_height, cycle_blocks)) = find_cycle(entry, CYCLE_SIZE) {
println!(
"Found cycle ({} height per {} blocks) after {} rounds!",
cycle_height, cycle_blocks, round
);
let remaining_blocks = n_blocks - block_i;
let cycles = remaining_blocks / cycle_blocks;
block_i += cycles * cycle_blocks;
skipped_height = Some(cycles * cycle_height);
println!(
"Skipping {} blocks, {} height",
cycles * cycle_blocks,
cycles * cycle_height
);
}
}
}
current_block = Some(spawn_block(&mut vent, block_i % N_BLOCK_TYPES));
// println!("Vent as block {} begins falling:", block_i);
// print_vent(&vent, current_block.as_ref());
// println!("");
block_i += 1;
}
let block = current_block.as_mut().unwrap();
// Push left/right
let jet = jets[jet_i];
if block.can_push(&vent, jet) {
let (_, ref mut col) = block.pos;
*col = ((*col as isize) - jet.shift()) as usize;
}
// Fall
if block.can_fall(&vent) {
let (ref mut row, _) = block.pos;
*row -= 1;
} else {
block.spawn(&mut vent);
current_block = None;
}
round += 1;
}
vent.len() - empty_rows(&vent) + skipped_height.unwrap_or(0)
}
const SPAWN_OFFSET_X: usize = 2;
const SPAWN_OFFSET_Y: usize = 3;
fn spawn_block(vent: &mut Vec<VentRow>, block: usize) -> Block {
let block = BlockKind::from_num(block);
let mut empty_rows = empty_rows(vent);
let diff = (block.height() as isize + 3) - (empty_rows as isize);
if diff > 0 {
vent.append(&mut vec![0; diff as usize]);
empty_rows += diff as usize;
}
let row = vent.len() - empty_rows + SPAWN_OFFSET_Y;
let col = SPAWN_OFFSET_X;
assert!(row + block.height() - 1 < vent.len());
let block = Block {
kind: block,
pos: (row, col),
};
block
}
fn empty_rows(vent: &Vec<VentRow>) -> usize {
vent.iter()
.rev()
.position(|r| *r != 0)
.unwrap_or_else(|| vent.len())
}
pub fn print_vent(vent: &Vec<VentRow>, block: Option<&Block>) {
let block_rows = block.map(|b| b.kind.vent_rows());
let block_range = block.map(|b| b.row_range());
let block_range = block_range.as_ref();
for (r, vent_row) in vent.iter().enumerate().rev() {
print!("|");
let block_collision = if block.is_some() && block_range.unwrap().contains(&r) {
block_rows.unwrap()[block_range.unwrap().end - r - 1] << block.unwrap().shift_dist()
} else {
0
};
for i in (0..VENT_WIDTH).rev() {
let col = 1 << i;
let c = if (block_collision & col) != 0 {
'@'
} else if (vent_row & col) == 0 {
'.'
} else {
'#'
};
print!("{}", c);
}
print!("|\n");
}
println!("+-------+");
}
fn find_cycle(
jet_heights: &HashMap<usize, (usize, usize)>,
n_cycles: usize,
) -> Option<(usize, usize)> {
if jet_heights.len() < n_cycles {
return None;
}
for (init_round, (init_height, init_blocks)) in jet_heights {
'next_loop: for (next_round, (next_height, next_blocks)) in jet_heights {
if init_round == next_round || *next_height < *init_height {
continue;
}
let cycle = next_round - init_round;
let cycle_height = next_height - init_height;
let cycle_blocks = next_blocks - init_blocks;
for n in 2..=n_cycles {
let round = init_round + n * cycle;
let entry = jet_heights.get(&round);
if let Some((height, blocks)) = entry {
let expected_height = init_height + n * cycle_height;
let expected_blocks = init_blocks + n * cycle_blocks;
if *height != expected_height || *blocks != expected_blocks {
continue 'next_loop;
}
} else {
continue 'next_loop;
}
}
return Some((cycle_height, cycle_blocks));
}
}
None
}

View File

@ -3,6 +3,10 @@ pub mod day10;
pub mod day11; pub mod day11;
pub mod day12; pub mod day12;
pub mod day13; pub mod day13;
pub mod day14;
pub mod day15;
pub mod day16;
pub mod day17;
pub mod day2; pub mod day2;
pub mod day3; pub mod day3;
pub mod day4; pub mod day4;

View File

@ -35,3 +35,41 @@ pub fn max_n<T: Ord + Copy>(slice: &[T], n: usize) -> Result<Vec<T>, ()> {
Ok(max_vals) Ok(max_vals)
} }
pub type Coord = (usize, usize);
pub trait Coordinate {
fn manhattan(&self, other: &Self) -> usize;
fn to_signed(&self) -> SignedCoord;
fn to_unsigned(&self) -> Coord;
}
impl Coordinate for Coord {
fn manhattan(&self, other: &Self) -> usize {
return self.0.abs_diff(other.0) + self.1.abs_diff(other.1);
}
fn to_signed(&self) -> SignedCoord {
(self.0 as isize, self.1 as isize)
}
fn to_unsigned(&self) -> Coord {
*self
}
}
pub type SignedCoord = (isize, isize);
impl Coordinate for SignedCoord {
fn manhattan(&self, other: &Self) -> usize {
return self.0.abs_diff(other.0) + self.1.abs_diff(other.1);
}
fn to_signed(&self) -> SignedCoord {
*self
}
fn to_unsigned(&self) -> Coord {
(self.0 as usize, self.1 as usize)
}
}