1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
/* Copyright 2023 Mario Finelli
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! Advent of Code 2015 Day 10: <https://adventofcode.com/2015/day/10>
//!
//! It took me a little while to wrap my head around what look-and-say is but
//! once I figured it out this was relatively easy to implement.
/// The solution for the day ten challenge.
///
/// We take the input as a string and the number of times to perform the
/// look-and-say as arguments and then return the length of the resulting
/// string as requested by the prompt. We start by converting the input string
/// into a vector of integers and then run the look-and-say function on the
/// result over and over until we've run it the requested number of times.
///
/// # Example
/// ```rust
/// # use aoc::y15d10::y15d10;
/// // probably read this from the input file...
/// let input = "1";
/// assert_eq!(y15d10(input, 10), 26);
/// ```
pub fn y15d10(input: &str, times: u32) -> u32 {
let mut nums: Vec<u32> = input
.trim()
.chars()
.map(|n| n.to_digit(10).unwrap())
.collect();
for _ in 0..times {
nums = lookandsay(nums);
}
nums.len().try_into().unwrap()
}
/// This function implements the look-and-say algorithm. It's relatively
/// straightforward: we mark the first digit as the "previous" and then start
/// counting from the second digit (if there is one). At each digit we check
/// to see if the digit is the same as the previous one. If it is then we
/// increment the current counter. If it's not then we add the current count
/// and "previous" into the final result, and then set the "previous" to the
/// new current and then reset the counter. Finally, push the final count and
/// digit onto the vector and we're done.
fn lookandsay(input: Vec<u32>) -> Vec<u32> {
let mut result: Vec<u32> = Vec::new();
let mut previous = input[0];
let mut count = 1;
for digit in input.iter().skip(1) {
if *digit != previous {
result.push(count);
result.push(previous);
previous = *digit;
count = 1;
} else {
count += 1;
}
}
result.push(count);
result.push(previous);
result
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn test_lookandsay() {
assert_eq!(lookandsay(vec!(1)), vec!(1, 1));
assert_eq!(lookandsay(vec!(1, 1)), vec!(2, 1));
assert_eq!(lookandsay(vec!(2, 1)), vec!(1, 2, 1, 1));
assert_eq!(lookandsay(vec!(1, 2, 1, 1)), vec!(1, 1, 1, 2, 2, 1));
assert_eq!(lookandsay(vec!(1, 1, 1, 2, 2, 1)), vec!(3, 1, 2, 2, 1, 1));
}
#[test]
fn it_works() {
let input = "1\n";
assert_eq!(y15d10(input, 1), 2); // 11
assert_eq!(y15d10(input, 2), 2); // 21
assert_eq!(y15d10(input, 3), 4); // 1211
assert_eq!(y15d10(input, 4), 6); // 111221
assert_eq!(y15d10(input, 5), 6); // 312211
}
#[test]
fn the_solution() {
let contents = fs::read_to_string("input/2015/day10.txt").unwrap();
assert_eq!(y15d10(&contents, 40), 360154);
assert_eq!(y15d10(&contents, 50), 5103798);
}
}