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 {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue