codetrotter_aoc_2019_solutions 0.9.0

Advent of Code 2019 Solutions
Documentation
/*
 * Copyright (c) 2019, 2020 Erik Nordstrøm <erik@nordstroem.no>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

solution_printer!(1, print_solution, input_generator, INPUT, solve_part_1, solve_part_2);

pub const INPUT: &str = include_str!("../input/2019/day1.txt");

/// ### Day 1: The Tyranny of the Rocket Equation
///
/// [https://adventofcode.com/2019/day/1](https://adventofcode.com/2019/day/1)
///
/// Santa has become stranded at the edge of the Solar System while
/// delivering presents to other planets! To accurately calculate his
/// position in space, safely align his warp drive, and return to Earth in
/// time to save Christmas, he needs you to bring him measurements from *fifty
/// stars*.
///
/// 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!
///
/// The Elves quickly load you into a spacecraft and prepare to launch.
///
/// At the first Go / No Go poll, every Elf is Go until the Fuel Counter-
/// Upper.  They haven't determined the amount of fuel required yet.
///
/// Fuel required to launch a given *module* is based on its *mass*.
/// Specifically, to find the fuel required for a module, take its mass,
/// divide by three, round down, and subtract 2.
///
/// For example:
///
///   - For a mass of `12`, divide by 3 and round down to get `4`, then subtract
///     2 to get `2`.
///   - For a mass of `14`, dividing by 3 and rounding down still yields `4`, so
///     the fuel required is also `2`.
///   - For a mass of `1969`, the fuel required is `654`.
///   - For a mass of `100756`, the fuel required is `33583`.
///
/// The Fuel Counter-Upper needs to know the total fuel requirement.  To find
/// it, individually calculate the fuel needed for the mass of each module
/// (your puzzle input), then add together all the fuel values.
///
/// *What is the sum of the fuel requirements* for all of the modules on your
/// spacecraft?
///
/// ### Examples
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_01::{input_generator, solve_part_1};
/// assert_eq!(solve_part_1(&mut input_generator("12")), 2);
/// ```
///
/// ```
/// # use codetrotter_aoc_2019_solutions::day_01::{input_generator, solve_part_1};
/// assert_eq!(solve_part_1(&mut input_generator("14")), 2);
/// ```
///
/// ```
/// # use codetrotter_aoc_2019_solutions::day_01::{input_generator, solve_part_1};
/// assert_eq!(solve_part_1(&mut input_generator("1969")), 654);
/// ```
///
/// ```
/// # use codetrotter_aoc_2019_solutions::day_01::{input_generator, solve_part_1};
/// assert_eq!(solve_part_1(&mut input_generator("100756")), 33583);
/// ```
///
/// ### Solution
///
/// ⚠️ SPOILER ALERT ⚠️
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_01::{INPUT, input_generator, solve_part_1};
/// assert_eq!(solve_part_1(&mut input_generator(INPUT)), 3403509);
/// ```
pub fn solve_part_1<T> (input_masses: &mut T) -> FuelRequired
  where T: Iterator<Item = Mass>
{
  input_masses.map(|mass| (mass / 3) - 2).sum()
}

pub type Mass = i64;
pub type FuelRequired = i64;

pub fn input_generator (input: &'static str) -> impl Iterator<Item = Mass>
{
  input.lines().map(|mass_str| mass_str.parse::<Mass>().unwrap())
}

/// ### Day 1, Part Two
///
/// [https://adventofcode.com/2019/day/1#part2](https://adventofcode.com/2019/day/1#part2)
///
/// During the second Go / No Go poll, the Elf in charge of the Rocket
/// Equation Double-Checker stops the launch sequence.  Apparently, you forgot
/// to include additional fuel for the fuel you just added.
///
/// Fuel itself requires fuel just like a module - take its mass, divide by
/// three, round down, and subtract 2.  However, that fuel *also* requires fuel,
/// and *that* fuel requires fuel, and so on.  Any mass that would require
/// *negative fuel* should instead be treated as if it requires *zero fuel*; the
/// remaining mass, if any, is instead handled by *wishing really hard*, which
/// has no mass and is outside the scope of this calculation.
///
/// So, for each module mass, calculate its fuel and add it to the total.
/// Then, treat the fuel amount you just calculated as the input mass and
/// repeat the process, continuing until a fuel requirement is zero or
/// negative. For example:
///
///   - A module of mass `14` requires `2` fuel.  This fuel requires no further
///     fuel (2 divided by 3 and rounded down is `0`, which would call for a
///     negative fuel), so the total fuel required is still just `2`.
///   - At first, a module of mass `1969` requires `654` fuel.  Then, this fuel
///     requires `216` more fuel (`654 / 3 - 2`).  `216` then requires `70` more fuel,
///     which requires `21` fuel, which requires `5` fuel, which requires no
///     further fuel.  So, the total fuel required for a module of mass `1969`
///     is `654 + 216 + 70 + 21 + 5 = 966`.
///   - The fuel required by a module of mass `100756` and its fuel is:
///     `33583 + 11192 + 3728 + 1240 + 411 + 135 + 43 + 12 + 2 = 50346`.
///
/// *What is the sum of the fuel requirements* for all of the modules on your
/// spacecraft when also taking into account the mass of the added fuel?
/// (Calculate the fuel requirements for each module separately, then add
/// them all up at the end.)
///
/// ### Examples
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_01::{input_generator, solve_part_2};
/// assert_eq!(solve_part_2(&mut input_generator("14")), 2);
/// ```
///
/// ```
/// # use codetrotter_aoc_2019_solutions::day_01::{input_generator, solve_part_2};
/// assert_eq!(solve_part_2(&mut input_generator("1969")), 966);
/// ```
///
/// ```
/// # use codetrotter_aoc_2019_solutions::day_01::{input_generator, solve_part_2};
/// assert_eq!(solve_part_2(&mut input_generator("100756")), 50346);
/// ```
///
/// ### Solution
///
/// ⚠️ SPOILER ALERT ⚠️
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_01::{INPUT, input_generator, solve_part_2};
/// assert_eq!(solve_part_2(&mut input_generator(INPUT)), 5102369);
/// ```
pub fn solve_part_2<T> (input_masses: &mut T) -> FuelRequired
  where T: Iterator<Item = Mass>
{
  input_masses.map(|mass|
  {
    let mut total_fuel_needed_for_mass = 0;

    let fuel_needed_for_mass = (mass / 3) - 2;
    total_fuel_needed_for_mass += fuel_needed_for_mass;

    let mut fuel_needed_for_fuel = (fuel_needed_for_mass / 3) - 2;
    while fuel_needed_for_fuel > 0
    {
      total_fuel_needed_for_mass += fuel_needed_for_fuel;
      fuel_needed_for_fuel = (fuel_needed_for_fuel / 3) - 2;
    }

    total_fuel_needed_for_mass
  }).sum()
}