This commit is contained in:
RingOfStorms (Joshua Bell) 2023-12-15 00:54:22 -06:00
parent bdc23f2638
commit 6c77637da5
4 changed files with 257 additions and 40 deletions

1
aoc_puzzle_cache/day_15 Normal file

File diff suppressed because one or more lines are too long

190
src/bin/day15.rs Normal file
View file

@ -0,0 +1,190 @@
use aoc23::prelude::*;
use derive_builder::Builder;
use itertools::Itertools;
use rayon::prelude::*;
use std::{str::FromStr, time::Instant};
static DAY: u8 = 15;
#[derive(Debug, Clone, Default, Eq, PartialEq)]
struct Seq {
chars: Vec<char>,
}
impl Seq {
fn new(s: &str) -> Result<Self> {
Ok(Self {
chars: s.trim().chars().filter(|c| !c.is_whitespace()).collect(),
})
}
fn hash(&self) -> usize {
self.chars.iter().fold(0, |acc, char| {
let mut value = acc;
value += *char as usize;
value *= 17;
value = value % 256;
value
})
}
}
#[derive(Debug, Clone)]
enum Operation {
Remove,
// focal length
Insert(usize),
}
#[derive(Debug, Clone)]
struct LensOp {
label: Seq,
box_index: usize,
operation: Operation,
}
impl LensOp {
fn new(value: &Seq) -> Self {
let label = Seq {
chars: value
.chars
.iter()
.take_while(|c| c != &&'=' && c != &&'-')
.map(|c| *c)
.collect(),
};
let box_index = label.hash();
let mut op = value.chars.iter().skip(label.chars.len());
Self {
label,
box_index,
operation: match op.next().unwrap() {
'-' => Operation::Remove,
'=' => Operation::Insert(op.next().unwrap().to_digit(10).unwrap() as usize),
o => panic!("Unknown op {o}"),
},
}
}
}
#[derive(Debug, Clone, Default)]
struct LensBox {
lenses: Vec<LensOp>,
}
fn part1(input: String) -> Result<usize> {
// parse
let start = Instant::now();
let sequences: Vec<Seq> = input.split(',').map(|seq| Seq::new(seq)).try_collect()?;
let parsed_time = start.elapsed();
// algo
let a_start = Instant::now();
let answer = sequences.par_iter().map(Seq::hash).sum();
let algo_time = a_start.elapsed();
// output
println!("part 1: {answer}\t[total: {:?}]", start.elapsed());
println!("\tparse: {parsed_time:?}");
println!("\talgo: {algo_time:?}");
Ok(answer)
}
fn part2(input: String) -> Result<usize> {
// parse
let start = Instant::now();
let lens_ops: Vec<LensOp> = input
.split(',')
.map(|seq| Seq::new(seq).unwrap())
.map(|s| LensOp::new(&s))
.collect();
let mut lens_boxes: Vec<LensBox> = std::iter::repeat_with(|| LensBox::default())
.take(256)
.collect();
let parsed_time = start.elapsed();
// algo
let a_start = Instant::now();
for lens_op in lens_ops {
if let Some(lens_box) = lens_boxes.get_mut(lens_op.box_index) {
match lens_op.operation {
Operation::Remove => lens_box.lenses.retain(|lop| lop.label != lens_op.label),
Operation::Insert(_) => {
if let Some(existing) = lens_box
.lenses
.iter_mut()
.find(|lop| lop.label == lens_op.label)
{
*existing = lens_op;
} else {
lens_box.lenses.push(lens_op);
}
}
}
}
}
let answer = lens_boxes
.iter()
.enumerate()
.map(|(box_idx, lens_box)| {
lens_box
.lenses
.iter()
.enumerate()
.map(|(lens_idx, lens)| {
if let Operation::Insert(focal_length) = lens.operation {
(box_idx + 1) * (lens_idx + 1) * focal_length
} else {
panic!("How did a removal lens get in there?");
}
})
.sum::<usize>()
})
.sum();
let algo_time = a_start.elapsed();
// output
println!("part 2: {answer}\t[total: {:?}]", start.elapsed());
println!("\tparse: {parsed_time:?}");
println!("\talgo: {algo_time:?}");
Ok(answer)
}
#[tokio::main]
async fn main() -> Result<()> {
println!("Day {DAY}");
println!("=====");
let input = utils::aoc::get_puzzle_input(DAY).await?;
part1(input.clone())?;
part2(input.clone())?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_1_a() -> Result<()> {
assert_eq!(part1("HASH".to_owned())?, 52);
Ok(())
}
#[test]
fn test_part_1_b() -> Result<()> {
assert_eq!(
part1("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7".to_owned())?,
1320
);
Ok(())
}
#[test]
fn test_part_2() -> Result<()> {
assert_eq!(
part2("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7".to_owned())?,
145
);
Ok(())
}
}

View file

@ -11,11 +11,13 @@ struct Todo {}
impl Todo { impl Todo {
fn new(s: &str) -> Result<Self> { fn new(s: &str) -> Result<Self> {
Ok(Todo {}) Ok(Self {})
} }
} }
fn part1(input: String) -> Result<usize> { fn part1(input: String) -> Result<usize> {
println!("Input\n====\n{input}\n\n");
// parse // parse
let start = Instant::now(); let start = Instant::now();
let todo = Todo::new(&input)?; let todo = Todo::new(&input)?;
@ -30,7 +32,6 @@ fn part1(input: String) -> Result<usize> {
println!("part 1: {answer}\t[total: {:?}]", start.elapsed()); println!("part 1: {answer}\t[total: {:?}]", start.elapsed());
println!("\tparse: {parsed_time:?}"); println!("\tparse: {parsed_time:?}");
println!("\talgo: {algo_time:?}"); println!("\talgo: {algo_time:?}");
println!("Input\n====\n{input}");
Ok(answer) Ok(answer)
} }
@ -58,8 +59,7 @@ async fn main() -> Result<()> {
println!("====="); println!("=====");
let input = utils::aoc::get_puzzle_input(DAY).await?; let input = utils::aoc::get_puzzle_input(DAY).await?;
part1(input.clone())?; part1(input.clone())?;
part2(input.clone())?;
// part2(input.clone())?;
Ok(()) Ok(())
} }
@ -67,17 +67,15 @@ async fn main() -> Result<()> {
mod tests { mod tests {
use super::*; use super::*;
static DATA: &'static str = "TODO_REPLACE";
#[test] #[test]
fn test_part_1() -> Result<()> { fn test_part_1() -> Result<()> {
assert_eq!(part1(DATA.to_owned())?, 0); assert_eq!(part1("REPLACE".to_owned())?, 0);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_part_2() -> Result<()> { fn test_part_2() -> Result<()> {
assert_eq!(part2(DATA.to_owned())?, 0); assert_eq!(part2("REPLACE".to_owned())?, 0);
Ok(()) Ok(())
} }
} }

View file

@ -1,38 +1,66 @@
use aoc23::prelude::*; use std::io::{self, Write};
use std::thread;
use std::time::Duration;
#[derive(Debug, Clone)] fn main() {
enum Tile { let mut food = (0, 0);
None, let mut snake = vec![(1, 2), (1, 3)];
Round, let mut direction = (0, 1);
Square, print_board(&snake, &food);
} loop {
let command: String = io::stdin().lock().read_line().unwrap();
fn get_columns<'a>(matrix: &'a mut Vec<Vec<Tile>>) -> Vec<Vec<&'a mut Tile>> { match command.as_str() {
let mut columns = vec![]; "\n" | "" => {}
let cols = matrix.first().map_or(0, Vec::len); "w" | "W" => direction = (0, -1),
let rows = matrix.len(); "a" | "A" => direction = (-1, 0),
for col_idx in 0..cols { "s" | "S" => direction = (0, 1),
let mut column = vec![]; "d" | "D" => direction = (1, 0),
for row_idx in 0..rows { _ => println!("Invalid command!"),
column.push(&mut matrix[row_idx][col_idx]);
} }
columns.push(column); let new_head = get_new_position(&snake.last().unwrap(), &direction);
} if snake.contains(&new_head)
columns || new_head.0 < 0
} || new_head.0 >= 20
|| new_head.1 < 0
fn tilt(rocks: &mut Vec<&mut Tile>) { || new_head.1 >= 20
for rock in rocks.iter_mut() { {
**rock = Tile::Round; println!("Game over!");
break;
}
snake.push(new_head);
if new_head == food {
food = get_new_food();
} else {
snake.remove(0);
}
print_board(&snake, &food);
thread::sleep(Duration::from_millis(50));
} }
} }
fn main() -> Result<()> { fn get_new_position(head: &(i32, i32), direction: &(i32, i32)) -> (i32, i32) {
// 10 x 10 grid of None let x = head.0 + direction.0;
let mut matrix: Vec<Vec<Tile>> = vec![vec![Tile::None; 10]; 10]; let y = head.1 + direction.1;
println!("BEFORE:\n{:?}", matrix); (x, y)
let mut columns = get_columns(&mut matrix); }
tilt(columns.get_mut(0).unwrap());
println!("AFTER:\n{:?}", matrix); fn get_new_food() -> (i32, i32) {
Ok(()) (rand::random::<i32>() % 20, rand::random::<i32>() % 20)
}
fn print_board(snake: &Vec<(i32, i32)>, food: &(i32, i32)) {
// Clear screen
print!("\x1b[H\x1b[J");
for y in 0..20 {
for x in 0..20 {
if snake.contains(&(x, y)) {
print!("");
} else if (x, y) == *food {
print!("*");
} else {
print!(".");
}
}
println!();
}
} }