moved code around so each year is a package.

This commit is contained in:
2025-08-29 16:14:42 -04:00
parent 8aa7fe572f
commit 46b91dae30
67 changed files with 246 additions and 77 deletions
+8
View File
@@ -0,0 +1,8 @@
[package]
name="aoc2016"
version = "0.1.0"
edition = "2024"
[dependencies]
core = { path = "../core" }
md-5 = { workspace = true }
+118
View File
@@ -0,0 +1,118 @@
// Santa's sleigh uses a very high-precision clock to guide its movements, and the clock's
// oscillator is regulated by stars. Unfortunately, the stars have been stolen... by the Easter
// Bunny. To save Christmas, Santa needs you to retrieve all fifty stars by December 25th.
//
// Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent
// calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants one
// star. Good luck!
//
// You're airdropped near Easter Bunny Headquarters in a city somewhere. "Near", unfortunately, is
// as close as you can get - the instructions on the Easter Bunny Recruiting Document the Elves
// intercepted start here, and nobody had time to work them out further.
//
// The Document indicates that you should start at the given coordinates (where you just landed)
// and face North. Then, follow the provided sequence: either turn left (L) or right (R) 90
// degrees, then walk forward the given number of blocks, ending at a new intersection.
//
// There's no time to follow such ridiculous instructions on foot, though, so you take a moment
// and work out the destination. Given that you can only walk on the street grid of the city,
// how far is the shortest path to the destination?
//
// For example:
//
// Following R2, L3 leaves you 2 blocks East and 3 blocks North, or 5 blocks away.
// R2, R2, R2 leaves you 2 blocks due South of your starting position, which is 2 blocks away.
// R5, L5, R5, R3 leaves you 12 blocks away.
// How many blocks away is Easter Bunny HQ?
//
use std::{env, fs};
use core::read_data;
use crate::CardinalDirection::*;
#[derive(Debug)]
enum CardinalDirection {
North,
South,
East,
West
}
fn manhattan_distance(directions: &str) -> i32 {
let (mut x_distance, mut y_distance, mut x_move, mut y_move) = (0i32,0i32, 0i32, 0i32);
let mut current_direction = North;
for next_direction in directions.split(", ") {
let (direction, vector) = next_direction.split_at(1);
let distance = vector.parse().unwrap();
// print!("[{}] At {x_distance}x{y_distance}, FACING {current_direction:?}, TURN {} MOVE {} ::::::", next_direction, direction, distance);
x_move = 0; y_move = 0;
match direction {
"R" => {
match current_direction {
North => {
current_direction = East;
x_move = distance;
}
South => {
current_direction = West;
x_move = distance * -1 ;
}
East => {
current_direction = South;
y_move = distance * -1;
}
West => {
current_direction = North;
y_move = distance;
}
}
}
"L" => {
match current_direction {
North => {
current_direction = West;
x_move = distance * -1;
}
South => {
current_direction = East;
x_move = distance;
}
East => {
current_direction = North;
y_move = distance;
}
West => {
current_direction = South;
y_move = distance * -1;
}
}
}
_ => {
println!("INVALID DIRECTION");
}
}
x_distance += x_move;
y_distance += y_move;
// println!("facing {:?} at {}x{} (moved {}x{})", current_direction, x_distance, y_distance, x_move, y_move);
}
x_distance.abs() + y_distance.abs()
}
fn main() {
let binding = read_data("2016_01_data.txt");
let directions = binding.as_str();
let parmas: Vec<(&str, i32)> = vec![
("R2, L3", 5),
("R2, R2, R2", 2),
("R5, L5, R5, R3", 12),
(directions, 252)];
for (param, expected) in parmas {
println!("Manhattan Distance of {} Expected {}", manhattan_distance(param) , expected);
}
}
// 252
+177
View File
@@ -0,0 +1,177 @@
// Santa's sleigh uses a very high-precision clock to guide its movements, and the clock's
// oscillator is regulated by stars. Unfortunately, the stars have been stolen... by the Easter
// Bunny. To save Christmas, Santa needs you to retrieve all fifty stars by December 25th.
//
// Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent
// calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants one
// star. Good luck!
//
// You're airdropped near Easter Bunny Headquarters in a city somewhere. "Near", unfortunately, is
// as close as you can get - the instructions on the Easter Bunny Recruiting Document the Elves
// intercepted start here, and nobody had time to work them out further.
//
// The Document indicates that you should start at the given coordinates (where you just landed)
// and face North. Then, follow the provided sequence: either turn left (L) or right (R) 90
// degrees, then walk forward the given number of blocks, ending at a new intersection.
//
// There's no time to follow such ridiculous instructions on foot, though, so you take a moment
// and work out the destination. Given that you can only walk on the street grid of the city,
// how far is the shortest path to the destination?
//
// For example:
//
// Following R2, L3 leaves you 2 blocks East and 3 blocks North, or 5 blocks away.
// R2, R2, R2 leaves you 2 blocks due South of your starting position, which is 2 blocks away.
// R5, L5, R5, R3 leaves you 12 blocks away.
// How many blocks away is Easter Bunny HQ?
//
use std::{env, fs};
use std::collections::HashMap;
use core::read_data;
use crate::CardinalDirection::*;
fn manhattan_distance(directions: &str) -> i32 {
let mut num_directions = 0;
let mut visited_locations: HashMap<String, i32> = HashMap::new();
visited_locations.insert("0x0".to_string(), 0);
let (mut x_distance, mut y_distance, mut x_move, mut y_move) = (0i32, 0i32, 0i32, 0i32);
let mut current_direction = North;
for next_direction in directions.split(", ") {
let (direction, vector) = next_direction.split_at(1);
let distance = vector.parse().unwrap();
x_move = 0;
y_move = 0;
num_directions += 1;
match (direction, current_direction) {
("R", North) => {
current_direction = East;
x_move = distance;
}
("R", South) => {
current_direction = West;
x_move = distance * -1;
}
("R", East) => {
current_direction = South;
y_move = distance * -1;
}
("R", West) => {
current_direction = North;
y_move = distance;
}
("L", North) => {
current_direction = West;
x_move = distance * -1;
}
("L", South) => {
current_direction = East;
x_move = distance;
}
("L", East) => {
current_direction = North;
y_move = distance;
}
("L", West) => {
current_direction = South;
y_move = distance * -1;
}
_ => {
unreachable!("Invalid Direction and Previous Direction");
}
}
for _ in 0..=distance {
match current_direction {
North => y_distance += 1,
South => y_distance -= 1,
East => x_distance += 1,
West => x_distance -= 1
}
}
x_distance += x_move;
y_distance += y_move;
//
// // have we been here before?
// let new_index = format!("{}x{}", x_distance, y_distance);
// for key in visited_locations.keys() {
// if *key == new_index {
// panic!("********************************MATCH -> {} == {}x{}", key, x_distance, y_distance);
// } else {
// print!("\tNOT MATCH -> {key} {x_distance}x{y_distance}\n");
// }
// }
// println!("Adding {new_index} to visited list");
// visited_locations.insert(new_index, 0);
}
x_distance.abs() + y_distance.abs()
}
use std::collections::HashSet;
#[derive(Debug)]
enum CardinalDirection {
North,
South,
East,
West,
}
fn manhattan_distance_first_repeat(directions: &str) -> i32 {
let mut visited: HashSet<(i32, i32)> = HashSet::new();
let (mut x, mut y) = (0, 0);
let mut facing = CardinalDirection::North;
visited.insert((x, y));
for instr in directions.split(", ") {
let (turn, steps_str) = instr.split_at(1);
let steps: i32 = steps_str.parse().unwrap();
// turn
facing = match (facing, turn) {
(CardinalDirection::North, "R") => CardinalDirection::East,
(CardinalDirection::North, "L") => CardinalDirection::West,
(CardinalDirection::South, "R") => CardinalDirection::West,
(CardinalDirection::South, "L") => CardinalDirection::East,
(CardinalDirection::East, "R") => CardinalDirection::South,
(CardinalDirection::East, "L") => CardinalDirection::North,
(CardinalDirection::West, "R") => CardinalDirection::North,
(CardinalDirection::West, "L") => CardinalDirection::South,
_ => unreachable!(),
};
// walk one step at a time
for _ in 0..steps {
match facing {
CardinalDirection::North => y += 1,
CardinalDirection::South => y -= 1,
CardinalDirection::East => x += 1,
CardinalDirection::West => x -= 1,
}
// check repeat
if !visited.insert((x, y)) {
return x.abs() + y.abs(); // first repeat distance
}
}
}
panic!("No repeated location found");
}
fn main() {
let binding = read_data("2016_01_data.txt");
// let binding = "R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1";
let directions = binding.as_str();
let params: Vec<&str> = vec![directions];
for param in params {
println!("Manhattan Distance of {}", manhattan_distance_first_repeat(param));
}
}
// 143 -> ChatGPT helped. :(
+79
View File
@@ -0,0 +1,79 @@
// You arrive at Easter Bunny Headquarters under cover of darkness. However, you left in such a
// rush that you forgot to use the bathroom! Fancy office buildings like this one usually have
// keypad locks on their bathrooms, so you search the front desk for the code.
//
// "In order to improve security," the document you find says, "bathroom codes will no longer be
// written down. Instead, please memorize and follow the procedure below to access the bathrooms."
//
// The document goes on to explain that each button to be pressed can be found by starting on the
// previous button and moving to adjacent buttons on the keypad: U moves up, D moves down, L moves
// left, and R moves right. Each line of instructions corresponds to one button, starting at the
// previous button (or, for the first line, the "5" button); press whatever button you're on at the
// end of each line. If a move doesn't lead to a button, ignore it.
//
// You can't hold it much longer, so you decide to figure out the code as you walk to the bathroom.
// You picture a keypad like this:
//
// 1 2 3
// 4 5 6
// 7 8 9
// Suppose your instructions are:
//
// ULL
// RRDDD
// LURDL
// UUUUD
// You start at "5" and move up (to "2"), left (to "1"), and left (you can't, and stay on "1"), so the first button is 1.
// Starting from the previous button ("1"), you move right twice (to "3") and then down three times (stopping at "9" after two moves and ignoring the third), ending up with 9.
// Continuing from "9", you move left, up, right, down, and left, ending with 8.
// Finally, you move up four times (stopping at "2"), then down once, ending with 5.
// So, in this example, the bathroom code is 1985.
//
// Your puzzle input is the instructions from the document you found at the front desk. What is the bathroom code?
//
use core::read_data;
#[derive(Debug)]
struct Location {
pub x: u32,
pub y: u32
}
fn main() {
let mut current_position = Location { x: 1, y: 1 };
// let directions = read_data("2016_02_data.txt");
let directions = "ULL\nRRDDD\nLURDL\nUUUUUD";
for next_direction in directions.chars() {
match next_direction {
'R' => {
if current_position.x < 3 {
current_position.x += 1;
}
},
'L' => {
if current_position.x > 0 {
current_position.x -= 1;
}
},
'U' => {
if current_position.y < 3 {
current_position.y += 1;
}
},
'D' => {
if current_position.y > 0 {
current_position.y -= 1;
}
},
'\n' => {
println!("At {current_position:?}");
7 },
_ => { unreachable!("Invalid direction"); }
}
}
}
// 2445 is wrong.
// 3775 is wrong.
+40
View File
@@ -0,0 +1,40 @@
// --- Day 3: Squares With Three Sides ---
// Now that you can think clearly, you move deeper into the labyrinth of hallways and office
// furniture that makes up this part of Easter Bunny HQ. This must be a graphic design department;
// the walls are covered in specifications for triangles.
//
// Or are they?
//
// The design document gives the side lengths of each triangle it describes, but... 5 10 25? Some
// of these aren't triangles. You can't help but mark the impossible ones.
//
// In a valid triangle, the sum of any two sides must be larger than the remaining side. For
// example, the "triangle" given above is impossible, because 5 + 10 is not larger than 25.
//
use core::read_data;
// the total of the sides minus the largest side is greater then the longest side
fn possibly_valid_triangle(a: u32, b: u32, c: u32) -> bool {
a+b+c - a.max(b).max(c) > a.max(b).max(c)
}
fn main() {
let binding= read_data("2016_03_data.txt");
let possible_triangles = binding.lines();
let mut num_valid = 0;
for current in possible_triangles.clone() {
println!("Parsing {current}");
let (first, balance) = current.split_at(5);
let (second, balance) = balance.split_at(5);
let (third, _) = balance.split_at(5);
println!("FIRST = [{first}] SECOND = [{second}] THIRD = [{third}]");
if possibly_valid_triangle(first.trim().parse().unwrap(), second.trim().parse().unwrap(), third.trim().parse().unwrap()) {
num_valid += 1;
}
}
println!("Found {num_valid} triangles out of {}", possible_triangles.count());
}
// 1050
+58
View File
@@ -0,0 +1,58 @@
use core::read_data;
// the total of the sides minus the largest side is greater then the longest side
fn possibly_valid_triangle(a: u32, b: u32, c: u32) -> bool {
a+b+c - a.max(b).max(c) > a.max(b).max(c)
}
fn split_at_5_twice(input: &str) -> (u32, u32, u32) {
let (a_s, bal) = input.split_at(5);
let (b_s, bal) = bal.split_at(5);
let c_s = bal.trim();
let a = a_s.trim().parse().unwrap();
let b = b_s.trim().parse().unwrap();
let c = c_s.trim().parse().unwrap();
(a, b, c)
}
fn main() {
// read 3 lines...
let mut num_valid = 0;
let binding = read_data("2016_03_data.txt");
let lines = binding.lines();
let mut lines_batch: Vec<&str> = vec![];
let mut current_batch_numbers = vec![
vec![0; 3],
vec![0; 3],
vec![0; 3]
];
for line in lines {
let (a, b, c) = split_at_5_twice(line);
let index = lines_batch.len();
lines_batch.push(line);
current_batch_numbers[0][index] = a;
current_batch_numbers[1][index] = b;
current_batch_numbers[2][index] = c;
if lines_batch.len() == 3 {
println!("Processing batch...");
for current_batch_set in current_batch_numbers.clone() {
if possibly_valid_triangle(
current_batch_set[0],
current_batch_set[1],
current_batch_set[2],
) {
num_valid += 1;
}
}
lines_batch.clear();
}
}
println!("There are {num_valid} valid triangles.");
}
// 1921
+45
View File
@@ -0,0 +1,45 @@
use std::collections::{BTreeMap, HashMap};
use core::read_data;
struct EncryptedRoom {
encrypted_name: String,
sector_id: u32,
checksum: String,
}
fn main() {
let binding = read_data("2016_04_data.txt");
let inputs = binding.lines();
for line in inputs {
let (balance, checksum) = line.split_once('[').unwrap();
let (checksum, _) = checksum.split_at(5);
let parts = balance.split("-");
let num_parts = parts.clone().count();
let mut working_enc = String::new();
let mut sector_id = 0;
// walk through the parts to find the sector id
for (index, part) in parts.enumerate() {
if index < ( num_parts - 1 ) {
working_enc += part;
} else {
sector_id = part.parse::<u32>().unwrap();
}
}
// count the characters in the string
let mut hash: HashMap<char, u32> = HashMap::new();
for current_char in working_enc.chars() {
hash.entry(current_char).and_modify(|mut x| *x += 1).or_insert(1);
}
// sort the hash into a flipped btreemap
println!("[{line}] / ENCRYPTED = [{working_enc}] / Sector ID : [{sector_id}] / checksum [{checksum}]");
println!("HASH: {hash:?}");
// now find the 5 most frequently occurring characters in alphabetical order
// once we have the new value, compare it to the checksum
}
}
+25
View File
@@ -0,0 +1,25 @@
use std::io::{stdout, Write};
use md5::{Digest, Md5};
fn main() {
let input = "wtnhxymk";
let mut password = String::new();
for index in 2231253..=u32::MAX {
let to_hash = format!("{}{}", input, index);
let as_hash = format!("{:x}", Md5::digest(to_hash.as_bytes()));
if as_hash.starts_with("00000") {
// println!("Found hash with {index} -> {as_hash}");
let ( sixth,_ ) = as_hash.as_str().split_at(5).1.split_at(1);
println!("sixth = {sixth}");
// print!(".");
stdout().flush().unwrap();
password += sixth;
if password.len() == 8 {
break;
}
}
}
println!("\nPassword is [{password}]");
}
// 2414bc77
+26
View File
@@ -0,0 +1,26 @@
use std::io::{stdout, Write};
use md5::{Digest, Md5};
fn main() {
let input = "wtnhxymk";
let mut password = vec![' '; 8];
for index in 2231253..=u32::MAX {
let to_hash = format!("{}{}", input, index);
let as_hash = format!("{:x}", Md5::digest(to_hash.as_bytes()));
if as_hash.starts_with("00000") {
println!("Found hash with {index} -> {as_hash}");
let ( sixth,balance ) = as_hash.as_str().split_at(5).1.split_at(1);
let ( seventh, _) = balance.split_at(1);
stdout().flush().unwrap();
let index_for_new_char = sixth.parse::<u32>().unwrap_or(9);
if index_for_new_char < 9 && password[index_for_new_char as usize] == ' ' {
password[index_for_new_char as usize] = seventh.parse().unwrap();
}
println!("Index for new char = {index_for_new_char} / Password [{password:?}] / Seventh {seventh}"); // bail out if we have 8 characters
}
}
println!("\nPassword is [{password:?}]");
}
// 437e60fc
+63
View File
@@ -0,0 +1,63 @@
// Something is jamming your communications with Santa. Fortunately, your signal is only partially
// jammed, and protocol in situations like this is to switch to a simple repetition code to get the
// message through.
//
// In this model, the same message is sent repeatedly. You've recorded the repeating message signal
// (your puzzle input), but the data seems quite corrupted - almost too badly to recover. Almost.
//
// All you need to do is figure out which character is most frequent for each position. For example,
// suppose you had recorded the following messages:
//
// eedadn
// drvtee
// eandsr
// raavrd
// atevrs
// tsrnev
// sdttsa
// rasrtv
// nssdts
// ntnada
// svetve
// tesnvt
// vntsnd
// vrdear
// dvrsen
// enarar
// The most common character in the first column is e; in the second, a; in the third, s, and so on.
// Combining these characters returns the error-corrected message, easter.
//
// Given the recording in your puzzle input, what is the error-corrected version of the message
// being sent?
//
use std::collections::HashMap;
use core::read_data;
fn main() {
let binding = read_data("2016_06_data.txt");
let lines = binding.lines();
let mut final_string = String::new();
let mut results = vec![HashMap::<char, u32>::new(); 8];
for line in lines {
for (index, char) in line.chars().enumerate() {
println!("Index {index} -> {char}");
results[index].entry(char).and_modify(|x| *x += 1).or_insert(1);
}
}
for results_column in &results {
let mut current_max =0 ;
let mut current_char = ' ';
for (index, (key, value)) in results_column.iter().enumerate() {
if current_max < *value {
current_max = *value;
current_char = *key;
}
}
println!("Character {current_char} with {current_max}");
final_string.push(current_char);
}
println!("Password is {final_string}");
}
// kjxfwkdh
+46
View File
@@ -0,0 +1,46 @@
// Of course, that would be the message - if you hadn't agreed to use a modified repetition code
// instead.
//
// In this modified code, the sender instead transmits what looks like random data, but for each
// character, the character they actually want to send is slightly less likely than the others.
// Even after signal-jamming noise, you can look at the letter distributions in each column and
// choose the least common letter to reconstruct the original message.
//
// In the above example, the least common character in the first column is a; in the second, d,
// and so on. Repeating this process for the remaining characters produces the original message,
// advent.
//
// Given the recording in your puzzle input and this new decoding methodology, what is the original
// message that Santa is trying to send?
//
use std::collections::HashMap;
use core::read_data;
fn main() {
let binding = read_data("2016_06_data.txt");
let lines = binding.lines();
let mut final_string = String::new();
let mut results = vec![HashMap::<char, u32>::new(); 8];
for line in lines {
for (index, char) in line.chars().enumerate() {
println!("Index {index} -> {char}");
results[index].entry(char).and_modify(|x| *x += 1).or_insert(1);
}
}
for results_column in &results {
let mut current_min = u32::MAX ;
let mut current_char = ' ';
for (index, (key, value)) in results_column.iter().enumerate() {
if current_min > *value {
current_min = *value;
current_char = *key;
}
}
println!("Character {current_char} with {current_min}");
final_string.push(current_char);
}
println!("Password is {final_string}");
}
// xrwcsnps
+35
View File
@@ -0,0 +1,35 @@
// While snooping around the local network of EBHQ, you compile a list of IP addresses (they're
// IPv7, of course; IPv6 is much too limited). You'd like to figure out which IPs support TLS
// (transport-layer snooping).
//
// An IP supports TLS if it has an Autonomous Bridge Bypass Annotation, or ABBA. An ABBA is any
// four-character sequence which consists of a pair of two different characters followed by the
// reverse of that pair, such as xyyx or abba. However, the IP also must not have an ABBA within
// any hypernet sequences, which are contained by square brackets.
//
// For example:
//
// abba[mnop]qrst supports TLS (abba outside square brackets).
// abcd[bddb]xyyx does not support TLS (bddb is within square brackets, even though xyyx is outside
// square brackets).
// aaaa[qwer]tyui does not support TLS (aaaa is invalid; the interior characters must be different).
// ioxxoj[asdfgh]zxcvbn supports TLS (oxxo is outside square brackets, even though it's within a
// larger string).
// How many IPs in your puzzle input support TLS?
fn has_tls(input: &str) -> bool {
true
}
fn main() {
let params = vec![
("abba[mnop]qrst", true),
("abcd[bddb]xyyx", false),
("aaaa[qwer]tyui", false),
("ioxxoj[asdfgh]zxcvbn", true)
];
for (input, expected) in params {
assert_eq!(has_tls(input), expected)
}
}