day15
This commit is contained in:
parent
bdc23f2638
commit
6c77637da5
4 changed files with 257 additions and 40 deletions
1
aoc_puzzle_cache/day_15
Normal file
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
190
src/bin/day15.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue