Skip to content

Commit 46ba50c

Browse files
committed
Add comprehensive documentation to dayXX.rs with detailed solution explanations.
These comments were generated using Zed editor with `kimi-k2-0711-preview`` model.
1 parent e1d704d commit 46ba50c

25 files changed

+709
-9
lines changed

src/day01.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
//! Day 1: Report Repair
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Find two numbers in the expense report that sum to 2020 and return their product.
6+
//! Part 2: Find three numbers in the expense report that sum to 2020 and return their product.
7+
//!
8+
//! ## Solution Approach
9+
//!
10+
//! **Input Parsing**: Converts the multiline string input into a vector of integers.
11+
//!
12+
//! **Part 1 Strategy**: Uses a brute-force nested loop approach:
13+
//! - Iterates through all pairs of numbers using two nested loops
14+
//! - Outer loop: takes each number `a` from the start to second-to-last
15+
//! - Inner loop: takes each number `b` from current `a` position to end
16+
//! - Checks if `a + b == 2020`
17+
//! - Returns `a * b` immediately when found
18+
//!
19+
//! **Part 2 Strategy**: Extends the same approach to three numbers:
20+
//! - Uses three nested loops with similar indexing pattern
21+
//! - Outer loop: number `a` from start to third-to-last
22+
//! - Middle loop: number `b` from current `a` position to second-to-last
23+
//! - Inner loop: number `c` from current `b` position to end
24+
//! - Checks if `a + b + c == 2020`
25+
//! - Returns `a * b * c` immediately when found
26+
//!
27+
//! **Complexity**: O(n²) for part 1, O(n³) for part 2 where n is the number of entries.
28+
//! **Optimization Note**: Could be improved with hash sets for O(n) part 1 and O(n²) part 2.
29+
130
fn parse_input(input: &str) -> Vec<i32> {
231
input.trim().lines().map(|s| s.parse().unwrap()).collect()
332
}

src/day02.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,35 @@
1+
//! Day 2: Password Philosophy
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Validate passwords based on character count policy - each line contains
6+
//! a range (min-max), a character, and a password. Count how many passwords
7+
//! have the character appear between min and max times (inclusive).
8+
//!
9+
//! Part 2: Validate passwords based on position policy - each line contains
10+
//! two positions (1-indexed), a character, and a password. Count how many
11+
//! passwords have the character appear in exactly one of the two positions.
12+
//!
13+
//! ## Solution Approach
14+
//!
15+
//! **Input Parsing**: Parses each line in format "min-max char: password" into:
16+
//! - Policy tuple: (min_position, max_position, character)
17+
//! - Password string
18+
//!
19+
//! **Part 1 Strategy**: Character frequency counting
20+
//! - For each password, count occurrences of the specified character
21+
//! - Check if count falls within the min-max range
22+
//! - Count valid passwords using iterator filters
23+
//!
24+
//! **Part 2 Strategy**: XOR position checking
25+
//! - Check if character appears at first position (min-1 for 0-indexing)
26+
//! - Check if character appears at second position (max-1 for 0-indexing)
27+
//! - Valid when exactly one position contains the character (XOR logic)
28+
//! - Count valid passwords using iterator filters
29+
//!
30+
//! **Parsing Notes**: Uses split on ['-', ' ', ':'] delimiters and careful indexing
31+
//! to extract policy components and password from each line.
32+
133
type Policy = (usize, usize, char);
234

335
fn parse_input(input: &str) -> Vec<(Policy, &str)> {

src/day03.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
//! Day 3: Toboggan Trajectory
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Count trees (#) encountered while sledding down a slope following
6+
//! a specific path: right 3, down 1. The terrain repeats horizontally.
7+
//!
8+
//! Part 2: Multiply the tree counts for multiple slope patterns:
9+
//! right 1 down 1, right 3 down 1, right 5 down 1, right 7 down 1, right 1 down 2.
10+
//!
11+
//! ## Solution Approach
12+
//!
13+
//! **Input Parsing**: Converts each line of the terrain map into a vector of characters,
14+
//! creating a 2D grid representation.
15+
//!
16+
//! **Part 1 Strategy**: Single slope traversal
17+
//! - Start at top-left position (0,0)
18+
//! - Move right 3, down 1 repeatedly
19+
//! - Use modulo arithmetic to handle horizontal repeating pattern
20+
//! - Count '#' characters encountered
21+
//!
22+
//! **Part 2 Strategy**: Multiple slope multiplication
23+
//! - Apply the same traversal logic to 5 different slope patterns
24+
//! - Calculate tree count for each slope individually
25+
//! - Multiply all counts together for final result
26+
//!
27+
//! **Grid Traversal**: The `slope` function handles the core logic:
28+
//! - Takes dx (right movement) and dy (down movement) parameters
29+
//! - Uses modulo on x-coordinate to handle infinite horizontal repetition
30+
//! - Returns tree count for the specified slope pattern
31+
132
fn parse_input(input: &str) -> Vec<Vec<char>> {
233
input.trim().lines().map(|s| s.chars().collect()).collect()
334
}

src/day04.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
//! Day 4: Passport Processing
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Count valid passports based on required fields presence.
6+
//! Part 2: Count valid passports based on field presence AND field value validation.
7+
//!
8+
//! Required fields: byr, iyr, eyr, hgt, hcl, ecl, pid (cid is optional)
9+
//!
10+
//! ## Solution Approach
11+
//!
12+
//! **Input Parsing**: Splits input by double newlines to separate passports,
13+
//! then parses each passport into a HashMap of field-value pairs.
14+
//!
15+
//! **Part 1 Strategy**: Field presence validation
16+
//! - Checks if all required fields (except cid) are present
17+
//! - Uses a predefined list of required field keys
18+
//!
19+
//! **Part 2 Strategy**: Field value validation
20+
//! - Applies all Part 1 validations first
21+
//! - Validates each field value according to specific rules:
22+
//! - byr: 1920-2002 (birth year)
23+
//! - iyr: 2010-2020 (issue year)
24+
//! - eyr: 2020-2030 (expiration year)
25+
//! - hgt: 150-193cm or 59-76in (height)
26+
//! - hcl: # followed by 6 hex digits (hair color)
27+
//! - ecl: one of [amb, blu, brn, gry, grn, hzl, oth] (eye color)
28+
//! - pid: 9-digit number (passport ID)
29+
//!
30+
//! **Validation Logic**: Uses pattern matching for clean validation of each field type.
31+
132
use std::collections::HashMap;
233

334
fn parse_input(input: &str) -> Vec<HashMap<&str, &str>> {

src/day05.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
//! Day 5: Binary Boarding
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Find the highest seat ID on a boarding pass using binary space partitioning.
6+
//! Part 2: Find your seat ID which is missing from the list but has adjacent seats.
7+
//!
8+
//! ## Solution Approach
9+
//!
10+
//! **Input Parsing**: Reads each line as a boarding pass string (e.g., "FBFBBFFRLR").
11+
//!
12+
//! **Binary Space Partitioning**: The `decode` function uses binary search:
13+
//! - 'F'/'L' = take lower half (reduce upper bound)
14+
//! - 'B'/'R' = take upper half (increase lower bound)
15+
//! - First 7 chars: row (0-127), Last 3 chars: column (0-7)
16+
//! - Seat ID = row * 8 + column
17+
//!
18+
//! **Part 1 Strategy**: Find maximum seat ID
19+
//! - Decode all boarding passes to seat IDs
20+
//! - Return the maximum value
21+
//!
22+
//! **Part 2 Strategy**: Find missing seat ID
23+
//! - Decode all boarding passes and sort seat IDs
24+
//! - Find gap where seat ID+1 doesn't equal next seat ID
25+
//! - Return the missing seat ID (your seat)
26+
//!
27+
//! **Binary Search Logic**: Uses half-interval search to efficiently determine
28+
//! row/column from boarding pass characters.
29+
130
fn parse_input(input: &str) -> Vec<&str> {
231
input.trim().lines().collect()
332
}

src/day06.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
//! Day 6: Custom Customs
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Count the total number of unique questions answered "yes" across all groups.
6+
//! Part 2: Count the total number of questions answered "yes" by everyone in each group.
7+
//!
8+
//! ## Solution Approach
9+
//!
10+
//! **Input Parsing**: Splits input by double newlines to separate groups,
11+
//! then converts each person's answers into byte slices for efficient processing.
12+
//!
13+
//! **Part 1 Strategy**: Union of answers per group
14+
//! - For each group, track which letters (a-z) appear in any person's answers
15+
//! - Uses boolean vector as a simple set representation
16+
//! - Counts unique questions per group and sums across all groups
17+
//!
18+
//! **Part 2 Strategy**: Intersection of answers per group
19+
//! - For each group, count how many people answered each question
20+
//! - Questions are valid only if count equals group size
21+
//! - Uses integer vector to track frequency of each letter
22+
//! - Counts questions answered by everyone per group and sums across all groups
23+
//!
24+
//! **Efficiency**: Uses byte arithmetic (ch - b'a') for O(1) character indexing,
25+
//! avoiding string allocations and leveraging contiguous memory access.
26+
127
fn parse_input(input: &str) -> Vec<Vec<&[u8]>> {
228
input
329
.trim()

src/day07.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
//! Day 7: Handy Haversacks
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Count how many bag colors can eventually contain at least one "shiny gold" bag.
6+
//! Part 2: Count how many individual bags are required inside a single "shiny gold" bag.
7+
//!
8+
//! ## Solution Approach
9+
//!
10+
//! **Input Parsing**: Creates a directed graph where:
11+
//! - Nodes are bag colors (e.g., "shiny gold", "dark red")
12+
//! - Edges represent containment relationships with weights (bag counts)
13+
//! - Uses nested HashMap: outer map keys are container bags, inner maps store contained bags and their counts
14+
//!
15+
//! **Part 1 Strategy**: Reverse traversal (containment check)
16+
//! - Uses recursive depth-first search to check if any bag can eventually contain "shiny gold"
17+
//! - For each bag, checks direct containment or recursive containment through any contained bag
18+
//! - Counts all bags that can reach "shiny gold" (excluding "shiny gold" itself)
19+
//!
20+
//! **Part 2 Strategy**: Forward traversal (bag counting)
21+
//! - Uses recursive depth-first search to count total bags inside "shiny gold"
22+
//! - For each contained bag: count = quantity * (recursive count of bags inside it + 1)
23+
//! - Sums all individual bag counts required
24+
//!
25+
//! **Algorithm**: Recursive DFS with memoization implicit in function calls handles the tree-like structure efficiently.
26+
127
use std::collections::HashMap;
228

329
fn parse_input(input: &str) -> HashMap<String, HashMap<String, usize>> {

src/day08.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
//! Day 8: Handheld Halting
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Find the accumulator value before the program loops infinitely.
6+
//! Part 2: Fix the program by changing exactly one "jmp" to "nop" or "nop" to "jmp"
7+
//! so the program terminates normally, then return the accumulator value.
8+
//!
9+
//! ## Solution Approach
10+
//!
11+
//! **Input Parsing**: Converts each line into (operation, value) tuples where:
12+
//! - Operations: "acc" (accumulate), "jmp" (jump), "nop" (no operation)
13+
//! - Values: signed integers for jump offsets or accumulator changes
14+
//!
15+
//! **Part 1 Strategy**: Detect infinite loop
16+
//! - Execute instructions sequentially while tracking visited positions
17+
//! - Stop when hitting a previously visited instruction
18+
//! - Return accumulator value at loop detection point
19+
//!
20+
//! **Part 2 Strategy**: Brute-force repair
21+
//! - Identify all "jmp" and "nop" instructions as candidates for modification
22+
//! - Try changing each candidate one at a time
23+
//! - Test if modified program terminates successfully
24+
//! - Return accumulator value when program reaches end
25+
//!
26+
//! **Execution Model**: Uses Result type where Ok() = successful termination,
27+
//! Err() = infinite loop detected, with accumulator value as payload.
28+
129
fn parse_input(input: &str) -> Vec<(&str, i32)> {
230
input
331
.trim()

src/day09.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
//! Day 9: Encoding Error
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Find the first number that is NOT the sum of any two of the previous N numbers.
6+
//! Part 2: Find a contiguous range that sums to the invalid number, then return
7+
//! the sum of the smallest and largest numbers in that range.
8+
//!
9+
//! ## Solution Approach
10+
//!
11+
//! **Input Parsing**: Converts input lines into a vector of unsigned 64-bit integers.
12+
//!
13+
//! **Part 1 Strategy**: XMAS cipher validation
14+
//! - Uses sliding window of previous N numbers (N=5 for example, N=25 for real input)
15+
//! - For each number, checks if it can be expressed as sum of any two distinct numbers
16+
//! - Returns the first number that fails this validation
17+
//!
18+
//! **Part 2 Strategy**: Contiguous sum search
19+
//! - Uses sliding window approach to find contiguous range summing to invalid number
20+
//! - Expands window when sum is too small, shrinks when sum is too large
21+
//! - Once found, returns sum of min and max values in the contiguous range
22+
//!
23+
//! **Window Algorithm**: Efficient O(n) sliding window technique to find contiguous sum.
24+
125
fn parse_input(input: &str) -> Vec<u64> {
226
input.trim().lines().map(|s| s.parse().unwrap()).collect()
327
}

src/day10.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
//! Day 10: Adapter Array
2+
//!
3+
//! ## Problem Description
4+
//!
5+
//! Part 1: Find the product of 1-jolt differences and 3-jolt differences in the adapter chain.
6+
//! Part 2: Count the total number of distinct ways to arrange the adapters to connect device.
7+
//!
8+
//! ## Solution Approach
9+
//!
10+
//! **Input Parsing**: Converts input lines into a vector of adapter joltage ratings.
11+
//!
12+
//! **Part 1 Strategy**: Joltage difference analysis
13+
//! - Add charging outlet (0 jolts) and built-in adapter (max + 3 jolts)
14+
//! - Sort all adapters and calculate differences between consecutive adapters
15+
//! - Count 1-jolt and 3-jolt differences, return their product
16+
//!
17+
//! **Part 2 Strategy**: Dynamic programming for arrangement counting
18+
//! - Uses DP array where dp[i] = number of ways to reach adapter i
19+
//! - For each adapter, sum ways from previous adapters within 3-jolt range
20+
//! - Optimized by working backwards and breaking when range exceeds 3 jolts
21+
//! - Returns total arrangements to reach the final adapter
22+
//!
23+
//! **Algorithm**: Dynamic programming with sliding window optimization for efficient counting.
24+
125
fn parse_input(input: &str) -> Vec<i32> {
226
input.trim().lines().map(|s| s.parse().unwrap()).collect()
327
}

0 commit comments

Comments
 (0)