Moved aoc challenges from their own repos to this one
This commit is contained in:
20
y2022/src/bin/d1.rs
Normal file
20
y2022/src/bin/d1.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d1;
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/1_input.txt")).unwrap();
|
||||
println!("{}", d1::process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/1_input.txt")).unwrap();
|
||||
println!("{}", d1::process_part2(&content));
|
||||
}
|
20
y2022/src/bin/d2.rs
Normal file
20
y2022/src/bin/d2.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d2::{process_part1, process_part2};
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/2_input.txt")).unwrap();
|
||||
println!("{}", process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/2_input.txt")).unwrap();
|
||||
println!("{}", process_part2(&content));
|
||||
}
|
20
y2022/src/bin/d3.rs
Normal file
20
y2022/src/bin/d3.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d3;
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/3_input.txt")).unwrap();
|
||||
println!("{}", d3::process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/3_input.txt")).unwrap();
|
||||
println!("{}", d3::process_part2(&content));
|
||||
}
|
20
y2022/src/bin/d4.rs
Normal file
20
y2022/src/bin/d4.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d4;
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/4_input.txt")).unwrap();
|
||||
println!("{}", d4::process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/4_input.txt")).unwrap();
|
||||
println!("{}", d4::process_part2(&content));
|
||||
}
|
20
y2022/src/bin/d5.rs
Normal file
20
y2022/src/bin/d5.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d5;
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/5_input.txt")).unwrap();
|
||||
println!("{}", d5::process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/5_input.txt")).unwrap();
|
||||
println!("{}", d5::process_part2(&content));
|
||||
}
|
20
y2022/src/bin/d6.rs
Normal file
20
y2022/src/bin/d6.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d6;
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/6_input.txt")).unwrap();
|
||||
println!("{}", d6::process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/6_input.txt")).unwrap();
|
||||
println!("{}", d6::process_part2(&content));
|
||||
}
|
20
y2022/src/bin/d7.rs
Normal file
20
y2022/src/bin/d7.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d7;
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/7_input.txt")).unwrap();
|
||||
println!("{}", d7::process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/7_input.txt")).unwrap();
|
||||
println!("{}", d7::process_part2(&content));
|
||||
}
|
20
y2022/src/bin/d8.rs
Normal file
20
y2022/src/bin/d8.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d8;
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/8_input.txt")).unwrap();
|
||||
println!("{}", d8::process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/8_input.txt")).unwrap();
|
||||
println!("{}", d8::process_part2(&content));
|
||||
}
|
20
y2022/src/bin/d9.rs
Normal file
20
y2022/src/bin/d9.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
|
||||
use y2022::days::d9;
|
||||
|
||||
fn main() {
|
||||
part1();
|
||||
part2();
|
||||
}
|
||||
|
||||
fn part1() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/9_input.txt")).unwrap();
|
||||
println!("{}", d9::process_part1(&content));
|
||||
}
|
||||
|
||||
fn part2() {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
let content = fs::read_to_string(format!("{root}/resources/9_input.txt")).unwrap();
|
||||
println!("{}", d9::process_part2(&content));
|
||||
}
|
49
y2022/src/days/d1.rs
Normal file
49
y2022/src/days/d1.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
pub fn process_part1(input: &str) -> i32 {
|
||||
let mut vector: Vec<i32> = input
|
||||
.split("\n\n")
|
||||
.map(|lines| lines.lines().map(|line| line.parse::<i32>().unwrap()).sum())
|
||||
.collect();
|
||||
vector.sort_by(|a, b| b.cmp(a));
|
||||
vector[0]
|
||||
}
|
||||
|
||||
pub fn process_part2(input: &str) -> i32 {
|
||||
let mut vector: Vec<i32> = input
|
||||
.split("\n\n")
|
||||
.map(|lines| lines.lines().map(|line| line.parse::<i32>().unwrap()).sum())
|
||||
.collect();
|
||||
vector.sort_by(|a, b| b.cmp(a));
|
||||
vector.iter().take(3).sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "1000
|
||||
2000
|
||||
3000
|
||||
|
||||
4000
|
||||
|
||||
5000
|
||||
6000
|
||||
|
||||
7000
|
||||
8000
|
||||
9000
|
||||
|
||||
10000";
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = process_part1(INPUT);
|
||||
assert_eq!(result, 24000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT);
|
||||
assert_eq!(result, 45000);
|
||||
}
|
||||
}
|
106
y2022/src/days/d2.rs
Normal file
106
y2022/src/days/d2.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
pub fn process_part1(input: &str) -> i32 {
|
||||
let result = input
|
||||
.lines()
|
||||
.map(|round| {
|
||||
let (opponent_move, my_move) = round.split_once(" ").unwrap();
|
||||
get_points(opponent_move, my_move)
|
||||
})
|
||||
.sum();
|
||||
result
|
||||
}
|
||||
|
||||
fn get_points(opponent_move: &str, my_move: &str) -> i32 {
|
||||
let mut round_score = 0;
|
||||
match my_move {
|
||||
"X" => {
|
||||
round_score += 1;
|
||||
match opponent_move {
|
||||
"C" => round_score += 6,
|
||||
"A" => round_score += 3,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
"Y" => {
|
||||
round_score += 2;
|
||||
match opponent_move {
|
||||
"A" => round_score += 6,
|
||||
"B" => round_score += 3,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
"Z" => {
|
||||
round_score += 3;
|
||||
match opponent_move {
|
||||
"B" => round_score += 6,
|
||||
"C" => round_score += 3,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => panic!("Shouldn't happen"),
|
||||
}
|
||||
round_score
|
||||
}
|
||||
|
||||
pub fn process_part2(input: &str) -> i32 {
|
||||
let result = input
|
||||
.lines()
|
||||
.map(|round| {
|
||||
let (opponent_move, round_result) = round.split_once(" ").unwrap();
|
||||
get_points_fixed(opponent_move, round_result)
|
||||
})
|
||||
.sum();
|
||||
result
|
||||
}
|
||||
|
||||
fn get_points_fixed(opponent_move: &str, round_result: &str) -> i32 {
|
||||
let mut round_score = 0;
|
||||
match round_result {
|
||||
"X" => match opponent_move {
|
||||
"A" => round_score += 3,
|
||||
"B" => round_score += 1,
|
||||
"C" => round_score += 2,
|
||||
_ => (),
|
||||
},
|
||||
"Y" => {
|
||||
round_score += 3;
|
||||
match opponent_move {
|
||||
"A" => round_score += 1,
|
||||
"B" => round_score += 2,
|
||||
"C" => round_score += 3,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
"Z" => {
|
||||
round_score += 6;
|
||||
match opponent_move {
|
||||
"A" => round_score += 2,
|
||||
"B" => round_score += 3,
|
||||
"C" => round_score += 1,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => panic!("Shouldn't happen"),
|
||||
}
|
||||
round_score
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "A Y
|
||||
B X
|
||||
C Z";
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = process_part1(INPUT);
|
||||
assert_eq!(result, 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT);
|
||||
assert_eq!(result, 12);
|
||||
}
|
||||
}
|
68
y2022/src/days/d3.rs
Normal file
68
y2022/src/days/d3.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
fn char_to_prio(char: char) -> i32 {
|
||||
let ascii = char as i32;
|
||||
match ascii {
|
||||
65..=90 => ascii - 38,
|
||||
97..=122 => ascii - 96,
|
||||
_ => panic!("Shouldn't happen"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_part1(input: &str) -> i32 {
|
||||
input
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let (first, second) = line.split_at(line.len() / 2);
|
||||
let mut prio = 0;
|
||||
for first_char in first.chars() {
|
||||
let found_char = second
|
||||
.chars()
|
||||
.find(|second_char| second_char.eq(&first_char));
|
||||
if let Some(char) = found_char {
|
||||
prio = char_to_prio(char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
prio
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub fn process_part2(input: &str) -> i32 {
|
||||
let mut lines = input.lines();
|
||||
let mut result = 0;
|
||||
while let Some(elf_1) = lines.next() {
|
||||
let elf_2 = lines.next().unwrap();
|
||||
let elf_3 = lines.next().unwrap();
|
||||
for elf_1_char in elf_1.chars() {
|
||||
if elf_2.contains(elf_1_char) && elf_3.contains(elf_1_char) {
|
||||
result += char_to_prio(elf_1_char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "vJrwpWtwJgWrhcsFMMfFFhFp
|
||||
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
|
||||
PmmdzqPrVvPwwTWBwg
|
||||
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
|
||||
ttgJtRGJQctTZtZT
|
||||
CrZsJsPPZsGzwwsLwLmpwMDw";
|
||||
|
||||
#[test]
|
||||
fn part1() {
|
||||
let result = process_part1(INPUT);
|
||||
assert_eq!(result, 157);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT);
|
||||
assert_eq!(result, 70);
|
||||
}
|
||||
}
|
79
y2022/src/days/d4.rs
Normal file
79
y2022/src/days/d4.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
pub fn process_part1(input: &str) -> i32 {
|
||||
let result = input
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let (elf1, elf2) = line.split_once(",").unwrap();
|
||||
let elf1 = elf1
|
||||
.split_once("-")
|
||||
.map(|(start, end)| start.parse::<i32>().unwrap()..=end.parse::<i32>().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let elf2 = elf2
|
||||
.split_once("-")
|
||||
.map(|(start, end)| start.parse::<i32>().unwrap()..=end.parse::<i32>().unwrap())
|
||||
.unwrap();
|
||||
|
||||
if (elf1.contains(elf2.start()) && elf1.contains(elf2.end()))
|
||||
|| (elf2.contains(elf1.start()) && elf2.contains(elf1.end()))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
0
|
||||
})
|
||||
.sum();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn process_part2(input: &str) -> i32 {
|
||||
let result = input
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let (elf1, elf2) = line.split_once(",").unwrap();
|
||||
let elf1 = elf1
|
||||
.split_once("-")
|
||||
.map(|(start, end)| start.parse::<i32>().unwrap()..=end.parse::<i32>().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let elf2 = elf2
|
||||
.split_once("-")
|
||||
.map(|(start, end)| start.parse::<i32>().unwrap()..=end.parse::<i32>().unwrap())
|
||||
.unwrap();
|
||||
|
||||
if elf1.contains(elf2.start())
|
||||
|| elf1.contains(elf2.end())
|
||||
|| elf2.contains(elf1.start())
|
||||
|| elf2.contains(elf1.end())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
0
|
||||
})
|
||||
.sum();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "2-4,6-8
|
||||
2-3,4-5
|
||||
5-7,7-9
|
||||
2-8,3-7
|
||||
6-6,4-6
|
||||
2-6,4-8";
|
||||
|
||||
#[test]
|
||||
fn part1() {
|
||||
let result = process_part1(INPUT);
|
||||
assert_eq!(result, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
110
y2022/src/days/d5.rs
Normal file
110
y2022/src/days/d5.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
fn is_string_numeric(str: &str) -> bool {
|
||||
for c in str.chars() {
|
||||
if !c.is_numeric() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_move(crane_move: &str) -> (usize, usize, usize) {
|
||||
let mut crane_move = crane_move
|
||||
.split(" ")
|
||||
.filter(|s| is_string_numeric(s))
|
||||
.map(|m| m.parse::<usize>().unwrap());
|
||||
(
|
||||
crane_move.next().unwrap(),
|
||||
crane_move.next().unwrap() - 1,
|
||||
crane_move.next().unwrap() - 1,
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_moves(crane_moves: &str) -> Vec<(usize, usize, usize)> {
|
||||
crane_moves.lines().map(parse_move).collect()
|
||||
}
|
||||
|
||||
fn parse_stacks(stacks: &str) -> Vec<String> {
|
||||
let mut lines: Vec<&str> = stacks.lines().collect();
|
||||
let mut last_line = lines.pop().unwrap().trim().to_string();
|
||||
let num_stacks = last_line.pop().unwrap().to_digit(10).unwrap();
|
||||
let mut parsed_stacks = vec!["".to_string(); num_stacks as usize];
|
||||
|
||||
for line in lines {
|
||||
for (idx, c) in line.chars().skip(1).step_by(4).enumerate() {
|
||||
if c.is_alphabetic() {
|
||||
parsed_stacks[idx].insert(0, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
parsed_stacks
|
||||
}
|
||||
|
||||
pub fn process_part1(input: &str) -> String {
|
||||
let (stacks, crane_moves) = input.split_once("\n\n").unwrap();
|
||||
let mut stacks = parse_stacks(stacks);
|
||||
let crane_moves = parse_moves(crane_moves);
|
||||
|
||||
for (iterations, from, to) in crane_moves {
|
||||
for _i in 0..iterations {
|
||||
let popped = stacks[from].pop().unwrap();
|
||||
stacks[to].push(popped);
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = "".to_string();
|
||||
|
||||
for mut stack in stacks {
|
||||
result.push(stack.pop().unwrap());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn process_part2(input: &str) -> String {
|
||||
let (stacks, crane_moves) = input.split_once("\n\n").unwrap();
|
||||
let mut stacks = parse_stacks(stacks);
|
||||
let crane_moves = parse_moves(crane_moves);
|
||||
|
||||
for (iterations, from, to) in crane_moves {
|
||||
let from_len = stacks[from].len();
|
||||
let from_old = stacks[from].clone();
|
||||
let (new_from, popped) = from_old.split_at(from_len - iterations);
|
||||
stacks[from] = new_from.to_string();
|
||||
stacks[to].push_str(popped);
|
||||
}
|
||||
|
||||
let mut result = "".to_string();
|
||||
|
||||
for mut stack in stacks {
|
||||
result.push(stack.pop().unwrap());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = " [D]
|
||||
[N] [C]
|
||||
[Z] [M] [P]
|
||||
1 2 3
|
||||
|
||||
move 1 from 2 to 1
|
||||
move 3 from 1 to 3
|
||||
move 2 from 2 to 1
|
||||
move 1 from 1 to 2";
|
||||
|
||||
#[test]
|
||||
fn part1() {
|
||||
let result = process_part1(INPUT);
|
||||
assert_eq!(result, "CMZ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT);
|
||||
assert_eq!(result, "MCD");
|
||||
}
|
||||
}
|
68
y2022/src/days/d6.rs
Normal file
68
y2022/src/days/d6.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
pub fn process_part1(input: &str) -> i32 {
|
||||
let mut marker = "".to_string();
|
||||
for (idx, c) in input.chars().enumerate() {
|
||||
if marker.len() == 4 {
|
||||
return (idx).try_into().unwrap();
|
||||
}
|
||||
if let Some(c_pos) = marker.find(c) {
|
||||
let (_, new_marker) = marker.split_at(c_pos + 1);
|
||||
marker = new_marker.to_string();
|
||||
}
|
||||
marker.push(c);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub fn process_part2(input: &str) -> i32 {
|
||||
let mut marker = "".to_string();
|
||||
for (idx, c) in input.chars().enumerate() {
|
||||
if marker.len() == 14 {
|
||||
return (idx).try_into().unwrap();
|
||||
}
|
||||
if let Some(c_pos) = marker.find(c) {
|
||||
let (_, new_marker) = marker.split_at(c_pos + 1);
|
||||
marker = new_marker.to_string();
|
||||
}
|
||||
marker.push(c);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT1: &str = "mjqjpqmgbljsphdztnvjfqwrcgsmlb";
|
||||
const INPUT2: &str = "bvwbjplbgvbhsrlpgdmjqwftvncz";
|
||||
const INPUT3: &str = "nppdvjthqldpwncqszvftbrmjlhg";
|
||||
const INPUT4: &str = "nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg";
|
||||
const INPUT5: &str = "zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw";
|
||||
|
||||
#[test]
|
||||
fn part1() {
|
||||
let result = process_part1(INPUT1);
|
||||
assert_eq!(result, 7);
|
||||
let result = process_part1(INPUT2);
|
||||
assert_eq!(result, 5);
|
||||
let result = process_part1(INPUT3);
|
||||
assert_eq!(result, 6);
|
||||
let result = process_part1(INPUT4);
|
||||
assert_eq!(result, 10);
|
||||
let result = process_part1(INPUT5);
|
||||
assert_eq!(result, 11);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT1);
|
||||
assert_eq!(result, 19);
|
||||
let result = process_part2(INPUT2);
|
||||
assert_eq!(result, 23);
|
||||
let result = process_part2(INPUT3);
|
||||
assert_eq!(result, 23);
|
||||
let result = process_part2(INPUT4);
|
||||
assert_eq!(result, 29);
|
||||
let result = process_part2(INPUT5);
|
||||
assert_eq!(result, 26);
|
||||
}
|
||||
}
|
200
y2022/src/days/d7.rs
Normal file
200
y2022/src/days/d7.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Directory {
|
||||
size: i64,
|
||||
children: Vec<String>,
|
||||
}
|
||||
|
||||
fn get_size(dir: Directory, directories: HashMap<String, Directory>) -> i64 {
|
||||
let mut size = dir.size;
|
||||
if !dir.children.is_empty() {
|
||||
size += dir
|
||||
.children
|
||||
.iter()
|
||||
.map(|child_name| {
|
||||
if let Some((_dir_name, child_dir)) = directories.get_key_value(child_name) {
|
||||
return get_size(child_dir.clone(), directories.clone());
|
||||
}
|
||||
0
|
||||
})
|
||||
.sum::<i64>();
|
||||
}
|
||||
size
|
||||
}
|
||||
|
||||
pub fn process_part1(input: &str) -> i64 {
|
||||
let mut pwd = String::from("");
|
||||
let mut directories: HashMap<String, Directory> = HashMap::new();
|
||||
let lines: Vec<Vec<&str>> = input
|
||||
.lines()
|
||||
.map(|line| line.split_whitespace().collect::<Vec<&str>>())
|
||||
.collect();
|
||||
for line in lines {
|
||||
if line[0] == "$" {
|
||||
if line[1] == "cd" {
|
||||
let arg = line[2].to_string();
|
||||
match arg.as_str() {
|
||||
"/" => pwd = arg,
|
||||
".." => match pwd.as_str() {
|
||||
"/" => println!("Already at root"),
|
||||
_ => pwd.truncate(pwd.rfind("/").unwrap()),
|
||||
},
|
||||
_ => match pwd.as_str() {
|
||||
"/" => pwd.push_str(&arg),
|
||||
_ => pwd.push_str(&("/".to_owned() + &arg)),
|
||||
},
|
||||
}
|
||||
if !directories.contains_key(&pwd) {
|
||||
directories.insert(
|
||||
pwd.clone(),
|
||||
Directory {
|
||||
size: 0,
|
||||
children: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if line[0] == "dir" {
|
||||
if let Some((dir_name, dir)) = directories.get_key_value(&pwd) {
|
||||
let mut new_children = dir.children.clone();
|
||||
let mut new_child = "/".to_owned() + line[1];
|
||||
if pwd != *"/" {
|
||||
new_child.insert_str(0, &pwd);
|
||||
}
|
||||
new_children.push(new_child);
|
||||
let new_dir = Directory {
|
||||
size: dir.size,
|
||||
children: new_children,
|
||||
};
|
||||
directories.insert(dir_name.to_string(), new_dir);
|
||||
}
|
||||
} else if line[0] != "dir" {
|
||||
if let Some((dir_name, dir)) = directories.get_key_value(&pwd) {
|
||||
let new_dir = Directory {
|
||||
size: dir.size + line[0].parse::<i64>().unwrap(),
|
||||
children: dir.children.clone(),
|
||||
};
|
||||
directories.insert(dir_name.to_string(), new_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
let sizes = directories
|
||||
.values()
|
||||
.map(|directory| get_size(directory.clone(), directories.clone()));
|
||||
|
||||
sizes.filter(|size| size <= &100000).sum()
|
||||
}
|
||||
|
||||
pub fn process_part2(input: &str) -> i64 {
|
||||
const TOTAL: i64 = 70000000;
|
||||
const MIN_UNUSED: i64 = 30000000;
|
||||
let mut pwd = String::from("");
|
||||
let mut directories: HashMap<String, Directory> = HashMap::new();
|
||||
let lines: Vec<Vec<&str>> = input
|
||||
.lines()
|
||||
.map(|line| line.split_whitespace().collect::<Vec<&str>>())
|
||||
.collect();
|
||||
for line in lines {
|
||||
if line[0] == "$" {
|
||||
if line[1] == "cd" {
|
||||
let arg = line[2].to_string();
|
||||
match arg.as_str() {
|
||||
"/" => pwd = arg,
|
||||
".." => match pwd.as_str() {
|
||||
"/" => println!("Already at root"),
|
||||
_ => pwd.truncate(pwd.rfind("/").unwrap()),
|
||||
},
|
||||
_ => match pwd.as_str() {
|
||||
"/" => pwd.push_str(&arg),
|
||||
_ => pwd.push_str(&("/".to_owned() + &arg)),
|
||||
},
|
||||
}
|
||||
if !directories.contains_key(&pwd) {
|
||||
directories.insert(
|
||||
pwd.clone(),
|
||||
Directory {
|
||||
size: 0,
|
||||
children: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if line[0] == "dir" {
|
||||
if let Some((dir_name, dir)) = directories.get_key_value(&pwd) {
|
||||
let mut new_children = dir.children.clone();
|
||||
let mut new_child = "/".to_owned() + line[1];
|
||||
if pwd != *"/" {
|
||||
new_child.insert_str(0, &pwd);
|
||||
}
|
||||
new_children.push(new_child);
|
||||
let new_dir = Directory {
|
||||
size: dir.size,
|
||||
children: new_children,
|
||||
};
|
||||
directories.insert(dir_name.to_string(), new_dir);
|
||||
}
|
||||
} else if line[0] != "dir" {
|
||||
if let Some((dir_name, dir)) = directories.get_key_value(&pwd) {
|
||||
let new_dir = Directory {
|
||||
size: dir.size + line[0].parse::<i64>().unwrap(),
|
||||
children: dir.children.clone(),
|
||||
};
|
||||
directories.insert(dir_name.to_string(), new_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
let sizes = directories
|
||||
.values()
|
||||
.map(|directory| get_size(directory.clone(), directories.clone()));
|
||||
let unused = TOTAL - sizes.clone().max().unwrap();
|
||||
let mut sizes = sizes.collect::<Vec<i64>>();
|
||||
sizes.sort();
|
||||
for size in sizes {
|
||||
if size >= (MIN_UNUSED - unused) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "$ cd /
|
||||
$ ls
|
||||
dir a
|
||||
14848514 b.txt
|
||||
8504156 c.dat
|
||||
dir d
|
||||
$ cd a
|
||||
$ ls
|
||||
dir e
|
||||
29116 f
|
||||
2557 g
|
||||
62596 h.lst
|
||||
$ cd e
|
||||
$ ls
|
||||
584 i
|
||||
$ cd ..
|
||||
$ cd ..
|
||||
$ cd d
|
||||
$ ls
|
||||
4060174 j
|
||||
8033020 d.log
|
||||
5626152 d.ext
|
||||
7214296 k";
|
||||
|
||||
#[test]
|
||||
fn part1() {
|
||||
let result = process_part1(INPUT);
|
||||
assert_eq!(result, 95437);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT);
|
||||
assert_eq!(result, 24933642);
|
||||
}
|
||||
}
|
125
y2022/src/days/d8.rs
Normal file
125
y2022/src/days/d8.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn is_visible_from_line(tree_line: Vec<u32>, tree_height: u32) -> bool {
|
||||
for &tree in tree_line.iter() {
|
||||
if tree >= tree_height {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn is_tree_visible(tree_grid: Vec<Vec<u32>>, tree_pos: (usize, usize), tree_height: u32) -> bool {
|
||||
let horizontal_line = tree_grid[tree_pos.0].clone();
|
||||
let vertical_line: Vec<u32> = tree_grid
|
||||
.iter()
|
||||
.map(|horizontal_line| horizontal_line[tree_pos.1])
|
||||
.collect();
|
||||
|
||||
let (left, right) = vertical_line.split_at(tree_pos.0);
|
||||
let (top, bottom) = horizontal_line.split_at(tree_pos.1);
|
||||
is_visible_from_line(left.to_vec(), tree_height)
|
||||
|| is_visible_from_line(right[1..].to_vec(), tree_height)
|
||||
|| is_visible_from_line(top.to_vec(), tree_height)
|
||||
|| is_visible_from_line(bottom[1..].to_vec(), tree_height)
|
||||
}
|
||||
|
||||
pub fn process_part1(input: &str) -> u32 {
|
||||
let tree_grid = input
|
||||
.lines()
|
||||
.map(|line| {
|
||||
line.chars()
|
||||
.map(|char| char.to_digit(10).unwrap())
|
||||
.collect()
|
||||
})
|
||||
.collect::<Vec<Vec<u32>>>();
|
||||
let dimension = (tree_grid.len(), tree_grid[0].len());
|
||||
let outer_trees = (dimension.0 as u32 * 2) + (dimension.1 as u32 * 2) - 4;
|
||||
let mut visible_trees: HashSet<(usize, usize)> = HashSet::new();
|
||||
for x in 1..dimension.0 - 1 {
|
||||
for y in 1..dimension.1 - 1 {
|
||||
if is_tree_visible(tree_grid.clone(), (x, y), tree_grid[x][y]) {
|
||||
visible_trees.insert((x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
outer_trees + visible_trees.len() as u32
|
||||
}
|
||||
|
||||
fn seeing_distance(tree_line: Vec<u32>, tree_height: u32) -> u32 {
|
||||
let mut count = 0;
|
||||
for tree in tree_line {
|
||||
count += 1;
|
||||
if tree >= tree_height {
|
||||
break;
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
fn calc_score(tree_grid: Vec<Vec<u32>>, tree_pos: (usize, usize), tree_height: u32) -> u32 {
|
||||
let horizontal_line = tree_grid[tree_pos.0].clone();
|
||||
let vertical_line: Vec<u32> = tree_grid
|
||||
.iter()
|
||||
.map(|horizontal_line| horizontal_line[tree_pos.1])
|
||||
.collect();
|
||||
|
||||
let (left, right) = vertical_line.split_at(tree_pos.0);
|
||||
let mut left = left.to_vec();
|
||||
left.reverse();
|
||||
let (top, bottom) = horizontal_line.split_at(tree_pos.1);
|
||||
let mut top = top.to_vec();
|
||||
top.reverse();
|
||||
seeing_distance(left.to_vec(), tree_height)
|
||||
* seeing_distance(right[1..].to_vec(), tree_height)
|
||||
* seeing_distance(top.to_vec(), tree_height)
|
||||
* seeing_distance(bottom[1..].to_vec(), tree_height)
|
||||
}
|
||||
|
||||
pub fn process_part2(input: &str) -> u32 {
|
||||
let tree_grid = input
|
||||
.lines()
|
||||
.map(|line| {
|
||||
line.chars()
|
||||
.map(|char| char.to_digit(10).unwrap())
|
||||
.collect()
|
||||
})
|
||||
.collect::<Vec<Vec<u32>>>();
|
||||
let dimension = (tree_grid.len(), tree_grid[0].len());
|
||||
let mut highest_score = 0;
|
||||
for x in 1..dimension.0 - 1 {
|
||||
for y in 1..dimension.1 - 1 {
|
||||
if is_tree_visible(tree_grid.clone(), (x, y), tree_grid[x][y]) {
|
||||
let score = calc_score(tree_grid.clone(), (x, y), tree_grid[x][y]);
|
||||
if score > highest_score {
|
||||
highest_score = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
highest_score
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "30373
|
||||
25512
|
||||
65332
|
||||
33549
|
||||
35390
|
||||
";
|
||||
|
||||
#[test]
|
||||
fn part1() {
|
||||
let result = process_part1(INPUT);
|
||||
assert_eq!(result, 21);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT);
|
||||
assert_eq!(result, 8);
|
||||
}
|
||||
}
|
229
y2022/src/days/d9.rs
Normal file
229
y2022/src/days/d9.rs
Normal file
@@ -0,0 +1,229 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Move {
|
||||
Up(i32),
|
||||
Down(i32),
|
||||
Left(i32),
|
||||
Right(i32),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Position {
|
||||
head: (i32, i32),
|
||||
tail: (i32, i32),
|
||||
visited_positions: HashSet<(i32, i32)>,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
fn new() -> Position {
|
||||
Position {
|
||||
head: (0, 0),
|
||||
tail: (0, 0),
|
||||
visited_positions: HashSet::from([(0, 0)]),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_touching(&self) -> bool {
|
||||
if self.head == self.tail {
|
||||
return true;
|
||||
}
|
||||
if self.head.0.abs_diff(self.tail.0) <= 1 && self.head.1.abs_diff(self.tail.1) <= 1 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, movement: Move) {
|
||||
match movement {
|
||||
Move::Up(distance) => {
|
||||
for _ in 1..=distance {
|
||||
self.step(Move::Up(1));
|
||||
}
|
||||
}
|
||||
Move::Down(distance) => {
|
||||
for _ in 1..=distance {
|
||||
self.step(Move::Down(1));
|
||||
}
|
||||
}
|
||||
Move::Left(distance) => {
|
||||
for _ in 1..=distance {
|
||||
self.step(Move::Left(1));
|
||||
}
|
||||
}
|
||||
Move::Right(distance) => {
|
||||
for _ in 1..=distance {
|
||||
self.step(Move::Right(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn square(a: i32) -> i32 {
|
||||
a * a
|
||||
}
|
||||
|
||||
fn get_distance(point_a: (i32, i32), point_b: (i32, i32)) -> f64 {
|
||||
(Self::square(point_b.0 - point_a.0) as f64 + Self::square(point_b.1 - point_a.1) as f64)
|
||||
.sqrt()
|
||||
}
|
||||
|
||||
fn move_tail(&mut self, movement: Move) {
|
||||
let big_distance = Self::get_distance(self.head, self.tail) > 2f64.sqrt();
|
||||
if !&self.is_touching() {
|
||||
match movement {
|
||||
Move::Up(_) => {
|
||||
self.tail.0 = self.head.0 - 1;
|
||||
if big_distance {
|
||||
self.tail.1 -= 1;
|
||||
} else {
|
||||
self.tail.1 = self.head.1;
|
||||
}
|
||||
}
|
||||
Move::Down(_) => {
|
||||
self.tail.0 = self.head.0 + 1;
|
||||
if big_distance {
|
||||
self.tail.1 += 1;
|
||||
} else {
|
||||
self.tail.1 = self.head.1;
|
||||
}
|
||||
}
|
||||
Move::Left(_) => {
|
||||
self.tail.1 = self.head.1 + 1;
|
||||
if big_distance {
|
||||
self.tail.0 += 1;
|
||||
} else {
|
||||
self.tail.0 = self.head.0;
|
||||
}
|
||||
}
|
||||
Move::Right(_) => {
|
||||
self.tail.1 = self.head.1 - 1;
|
||||
if big_distance {
|
||||
self.tail.0 -= 1;
|
||||
} else {
|
||||
self.tail.0 = self.head.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.visited_positions.insert(self.tail);
|
||||
}
|
||||
|
||||
fn step(&mut self, movement: Move) {
|
||||
match movement {
|
||||
Move::Up(distance) => self.head.0 += distance,
|
||||
Move::Down(distance) => self.head.0 -= distance,
|
||||
Move::Left(distance) => self.head.1 -= distance,
|
||||
Move::Right(distance) => self.head.1 += distance,
|
||||
}
|
||||
self.move_tail(movement);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_line(line: &str) -> Move {
|
||||
let mut line = line.split_whitespace();
|
||||
let direction = line.next();
|
||||
let distance = line.next().unwrap().parse::<i32>().unwrap();
|
||||
match direction {
|
||||
Some("U") => Move::Up(distance),
|
||||
Some("D") => Move::Down(distance),
|
||||
Some("L") => Move::Left(distance),
|
||||
Some("R") => Move::Right(distance),
|
||||
_ => panic!("Shouldn_t happen"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_part1(input: &str) -> usize {
|
||||
let mut position = Position {
|
||||
head: (0, 0),
|
||||
tail: (0, 0),
|
||||
visited_positions: HashSet::from([(0, 0)]),
|
||||
};
|
||||
let moves = input.lines().map(parse_line);
|
||||
for movement in moves {
|
||||
position.update(movement);
|
||||
}
|
||||
position.visited_positions.len()
|
||||
}
|
||||
pub fn process_part2(input: &str) -> usize {
|
||||
let mut positions = vec![Position::new(); 10];
|
||||
let moves = input.lines().map(parse_line);
|
||||
for movement in moves {
|
||||
let mut steps = 0;
|
||||
if let Move::Up(distance) = movement {
|
||||
steps = distance;
|
||||
} else if let Move::Down(distance) = movement {
|
||||
steps = distance;
|
||||
} else if let Move::Left(distance) = movement {
|
||||
steps = distance;
|
||||
} else if let Move::Right(distance) = movement {
|
||||
steps = distance;
|
||||
}
|
||||
for _ in 0..steps {
|
||||
for (idx, position) in positions.clone().iter().enumerate() {
|
||||
let mut position = position.clone();
|
||||
if idx == 0 {
|
||||
match movement {
|
||||
Move::Up(_) => {
|
||||
position.step(Move::Up(1));
|
||||
}
|
||||
Move::Down(_) => {
|
||||
position.step(Move::Down(1));
|
||||
}
|
||||
Move::Left(_) => {
|
||||
position.step(Move::Left(1));
|
||||
}
|
||||
Move::Right(_) => {
|
||||
position.step(Move::Right(1));
|
||||
}
|
||||
}
|
||||
println!("head: {:?}", position.head)
|
||||
} else {
|
||||
position.head = positions[idx - 1].tail;
|
||||
position.move_tail(movement);
|
||||
println!("knot nr{}: {:?}", idx, position.head);
|
||||
}
|
||||
if idx == 9 {
|
||||
println!("tail: {:?}", position.tail)
|
||||
}
|
||||
positions[idx] = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
positions[9].visited_positions.len()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT1: &str = "R 4
|
||||
U 4
|
||||
L 3
|
||||
D 1
|
||||
R 4
|
||||
D 1
|
||||
L 5
|
||||
R 2";
|
||||
|
||||
const INPUT2: &str = "R 5
|
||||
U 8
|
||||
L 8
|
||||
D 3
|
||||
R 17
|
||||
D 10
|
||||
L 25
|
||||
U 20";
|
||||
|
||||
#[test]
|
||||
fn part1() {
|
||||
let result = process_part1(INPUT1);
|
||||
assert_eq!(result, 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2() {
|
||||
let result = process_part2(INPUT2);
|
||||
assert_eq!(result, 36);
|
||||
}
|
||||
}
|
9
y2022/src/days/mod.rs
Normal file
9
y2022/src/days/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
pub mod d1;
|
||||
pub mod d2;
|
||||
pub mod d3;
|
||||
pub mod d4;
|
||||
pub mod d5;
|
||||
pub mod d6;
|
||||
pub mod d7;
|
||||
pub mod d8;
|
||||
pub mod d9;
|
1
y2022/src/lib.rs
Normal file
1
y2022/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod days;
|
Reference in New Issue
Block a user