There is no readme
use std::fs;
use std::path::PathBuf;
use clap::Parser;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// A file containing the input
input_file: PathBuf,
#[arg(short, long)]
part: Option,
}
fn main() {
// Parse CLI arguments
let cli = Cli::parse();
// Read file
let input_text = fs::read_to_string(&cli.input_file)
.expect(format!("File \"{}\" not found", cli.input_file.display()).as_str());
let (run_part_1, run_part_2) = if let Some(part) = cli.part {
match part {
1 => (true, false),
2 => (false, true),
_ => unimplemented!(),
}
} else {
(true, true)
};
let (it1, it2) = preprocess(&input_text);
if run_part_1 {
let solution = solve(it1);
println!("Part 1: {solution}");
}
if run_part_2 {
let solution = solve(it2);
println!("Part 2: {solution}");
}
}
/// Preprocessing for solving
/// Extracts important information from the input
/// `part` specifies which part to preprocess for.
/// Returns a vector for each part containing a direction and amount
fn preprocess(input: &str) -> (Vec<((isize, isize), isize)>, Vec<((isize, isize), isize)>) {
let it = input.lines().map(|l| {
let line: Vec<_> = l.split(' ').collect();
let direction: char = line[0].chars().nth(0).unwrap();
let amount: isize = line[1].parse().unwrap();
let color: &str = &line[2][2..8];
let direction = match direction {
'R' => (1, 0),
'L' => (-1, 0),
'U' => (0, 1),
'D' => (0, -1),
_ => unreachable!(),
};
((direction, amount), {
let amount: isize = isize::from_str_radix(&color[..5], 16).unwrap();
let direction = match &color[5..] {
"0" => (1, 0),
"1" => (0, -1),
"2" => (-1, 0),
"3" => (0, 1),
_ => unreachable!(),
};
(direction, amount)
})
});
let it1 = it.clone().map(|(i1, _i2)| i1).collect();
let it2 = it.map(|(_i1, i2)| i2).collect();
(it1, it2)
}
/// Finds the area using the shoelace formula
fn solve(it: Vec<((isize, isize), isize)>) -> isize {
// Get coordinates from it
let mut coords: Vec<(isize, isize)> = Vec::with_capacity(it.len() + 1);
// Start at (0, 0)
coords.push((0, 0)); // Needs to be at both begining and end
let (mut x, mut y) = (0, 0);
let mut perimeter_length = 0;
// Compute and add the coords
for (direction, amount) in it {
let translation = (direction.0 * amount, direction.1 * amount);
x += translation.0;
y += translation.1;
coords.push((x, y));
perimeter_length += amount;
}
// Should end where it started
assert_eq!(coords.last().unwrap(), &(0, 0));
// Shoelace formula
let a = coords
.iter()
.zip(coords.iter().skip(1))
.map(|((x1, y1), (x2, y2))| (x1 * y2) - (x2 * y1))
.sum::()
/ 2;
// Found by drawing, then trial and error
// Shoelace area missing 1/2 of perimeter
// Inside and outside corners cancel out except for one
a.abs() + perimeter_length / 2 + 1
}
args ← {1↓⍵/⍨∨\⍵∊⊂'--'} ⎕ARG
inputs ← ⎕FIO[49]¨ args
words ← 'one' 'two' 'three' 'four' 'five' 'six' 'seven' 'eight' 'nine'
digits ← '123456789'
part1 ← {↑↑+/{(10×↑⍵)+¯1↑⍵}¨{⍵~0}¨+⊃(⍳9)+.×digits∘.⍷⍵}
"Part 1: ", part1¨ inputs
part2 ← {↑↑+/{(10×↑⍵)+¯1↑⍵}¨{⍵~0}¨+⊃(⍳9)+.×(words∘.⍷⍵)+digits∘.⍷⍵}
"Part 2: ", part2¨ inputs
)OFF
use std::fs;
use std::path::PathBuf;
use clap::Parser;
use rayon::prelude::*;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
input_file: PathBuf,
}
#[derive(Copy, Clone)]
enum TileState {
None,
Energized(BeamState),
}
#[derive(Default, Copy, Clone)]
struct BeamState {
up: bool,
down: bool,
left: bool,
right: bool,
}
fn main() {
// Parse CLI arguments
let cli = Cli::parse();
// Read file
let input_text = fs::read_to_string(&cli.input_file)
.expect(format!("File \"{}\" not found", cli.input_file.display()).as_str());
let tiles: Vec> = input_text.lines().map(|l| l.chars().collect()).collect();
// Part 1
let part_1 = test_beam(&tiles, (0, 0), (0, 1));
println!("Part 1: {}", part_1);
// Part 2
let part_2: usize = (0..4)
.into_par_iter()
.map(|dir| {
(0..tiles.len())
.into_par_iter()
.map(move |x| (dir.clone(), x))
})
.flatten()
.map(|(dir, x)| match dir {
0 => ((0, x), (1, 0)),
1 => ((x, tiles[0].len() - 1), (0, -1)),
2 => ((tiles.len() - 1, x), (-1, 0)),
3 => ((x, 0), (0, 1)),
_ => unreachable!(),
})
.map(|(loc, dir)| test_beam(&tiles, loc, dir))
.max()
.unwrap();
println!("Part 2: {}", part_2);
}
fn test_beam(
tiles: &Vec>,
start_location: (usize, usize),
start_direction: (i64, i64),
) -> usize {
let mut energized: Vec> =
vec![vec![TileState::None; tiles[0].len()]; tiles.len()];
continue_beam(
&mut energized,
&tiles,
start_location,
start_direction,
true,
0,
);
energized
.iter()
.map(|r| {
r.iter()
.filter(|t| matches!(t, TileState::Energized(_)))
.count()
})
.sum()
}
fn continue_beam(
energized: &mut Vec>,
tiles: &Vec>,
beam_location: (usize, usize),
beam_direction: (i64, i64),
start_hack: bool,
depth: usize,
) {
assert_ne!(beam_direction, (0, 0));
// Set current tile to energized with the direction
let current_state = energized[beam_location.0][beam_location.1];
if !start_hack {
energized[beam_location.0][beam_location.1] = match current_state {
TileState::None => TileState::Energized(match beam_direction {
(0, 1) => BeamState {
right: true,
..BeamState::default()
},
(0, -1) => BeamState {
left: true,
..BeamState::default()
},
(1, 0) => BeamState {
down: true,
..BeamState::default()
},
(-1, 0) => BeamState {
up: true,
..BeamState::default()
},
_ => unreachable!(),
}),
TileState::Energized(state) => TileState::Energized(match beam_direction {
(0, 1) => {
if state.right {
return;
}
BeamState {
right: true,
..state
}
}
(0, -1) => {
if state.left {
return;
}
BeamState {
left: true,
..state
}
}
(1, 0) => {
if state.down {
return;
}
BeamState {
down: true,
..state
}
}
(-1, 0) => {
if state.up {
return;
}
BeamState { up: true, ..state }
}
_ => unreachable!(),
}),
};
}
// energized[beam_location.0][beam_location.1] = TileState::Energized(BeamState { up: , down: , left: , right: });
let next_beam_location = {
let loc = (
(beam_location.0 as i64 + beam_direction.0),
(beam_location.1 as i64 + beam_direction.1),
);
if start_hack {
beam_location
} else if loc.0 < 0
|| loc.0 >= tiles.len() as i64
|| loc.1 < 0
|| loc.1 >= tiles[0].len() as i64
{
return;
} else {
(loc.0 as usize, loc.1 as usize)
}
};
let next_beam_tile = tiles[next_beam_location.0][next_beam_location.1];
let next_beam_directions: Vec<(i64, i64)> = match next_beam_tile {
'.' => vec![beam_direction],
'/' => match beam_direction {
(0, 1) => vec![(-1, 0)],
(0, -1) => vec![(1, 0)],
(1, 0) => vec![(0, -1)],
(-1, 0) => vec![(0, 1)],
_ => unreachable!(),
},
'\\' => match beam_direction {
(0, 1) => vec![(1, 0)],
(0, -1) => vec![(-1, 0)],
(1, 0) => vec![(0, 1)],
(-1, 0) => vec![(0, -1)],
_ => unreachable!(),
},
'|' => match beam_direction {
(0, 1) => vec![(1, 0), (-1, 0)],
(0, -1) => vec![(1, 0), (-1, 0)],
(1, 0) => vec![(1, 0)],
(-1, 0) => vec![(-1, 0)],
_ => unreachable!(),
},
'-' => match beam_direction {
(0, 1) => vec![(0, 1)],
(0, -1) => vec![(0, -1)],
(1, 0) => vec![(0, 1), (0, -1)],
(-1, 0) => vec![(0, 1), (0, -1)],
_ => unreachable!(),
},
_ => unreachable!(),
};
for dir in next_beam_directions {
continue_beam(energized, tiles, next_beam_location, dir, false, depth + 1);
}
}
26.28 line-seconds
What does the line-seconds score mean?
i stopped using X.com when it stopped being just a single “x”.
Fortunately, his name is Yeff.
Caelus