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 {
fn new(s: &str) -> Result<Self> {
Ok(Todo {})
Ok(Self {})
}
}
fn part1(input: String) -> Result<usize> {
println!("Input\n====\n{input}\n\n");
// parse
let start = Instant::now();
let todo = Todo::new(&input)?;
@ -30,7 +32,6 @@ fn part1(input: String) -> Result<usize> {
println!("part 1: {answer}\t[total: {:?}]", start.elapsed());
println!("\tparse: {parsed_time:?}");
println!("\talgo: {algo_time:?}");
println!("Input\n====\n{input}");
Ok(answer)
}
@ -58,8 +59,7 @@ async fn main() -> Result<()> {
println!("=====");
let input = utils::aoc::get_puzzle_input(DAY).await?;
part1(input.clone())?;
// part2(input.clone())?;
part2(input.clone())?;
Ok(())
}
@ -67,17 +67,15 @@ async fn main() -> Result<()> {
mod tests {
use super::*;
static DATA: &'static str = "TODO_REPLACE";
#[test]
fn test_part_1() -> Result<()> {
assert_eq!(part1(DATA.to_owned())?, 0);
assert_eq!(part1("REPLACE".to_owned())?, 0);
Ok(())
}
#[test]
fn test_part_2() -> Result<()> {
assert_eq!(part2(DATA.to_owned())?, 0);
assert_eq!(part2("REPLACE".to_owned())?, 0);
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)]
enum Tile {
None,
Round,
Square,
}
fn get_columns<'a>(matrix: &'a mut Vec<Vec<Tile>>) -> Vec<Vec<&'a mut Tile>> {
let mut columns = vec![];
let cols = matrix.first().map_or(0, Vec::len);
let rows = matrix.len();
for col_idx in 0..cols {
let mut column = vec![];
for row_idx in 0..rows {
column.push(&mut matrix[row_idx][col_idx]);
fn main() {
let mut food = (0, 0);
let mut snake = vec![(1, 2), (1, 3)];
let mut direction = (0, 1);
print_board(&snake, &food);
loop {
let command: String = io::stdin().lock().read_line().unwrap();
match command.as_str() {
"\n" | "" => {}
"w" | "W" => direction = (0, -1),
"a" | "A" => direction = (-1, 0),
"s" | "S" => direction = (0, 1),
"d" | "D" => direction = (1, 0),
_ => println!("Invalid command!"),
}
columns.push(column);
}
columns
}
fn tilt(rocks: &mut Vec<&mut Tile>) {
for rock in rocks.iter_mut() {
**rock = Tile::Round;
let new_head = get_new_position(&snake.last().unwrap(), &direction);
if snake.contains(&new_head)
|| new_head.0 < 0
|| new_head.0 >= 20
|| new_head.1 < 0
|| new_head.1 >= 20
{
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<()> {
// 10 x 10 grid of None
let mut matrix: Vec<Vec<Tile>> = vec![vec![Tile::None; 10]; 10];
println!("BEFORE:\n{:?}", matrix);
let mut columns = get_columns(&mut matrix);
tilt(columns.get_mut(0).unwrap());
println!("AFTER:\n{:?}", matrix);
Ok(())
fn get_new_position(head: &(i32, i32), direction: &(i32, i32)) -> (i32, i32) {
let x = head.0 + direction.0;
let y = head.1 + direction.1;
(x, y)
}
fn get_new_food() -> (i32, i32) {
(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!();
}
}