DEV Community

Cover image for Advent of Code 2019 Solution Megathread - Day 1: The Tyranny of the Rocket Equation
Jon Bristow
Jon Bristow

Posted on • Edited on

Advent of Code 2019 Solution Megathread - Day 1: The Tyranny of the Rocket Equation

Well, we're off to a running start! Since the leaderboard is safely locked, we can go ahead and post our solutions now!

Day 1 - The Problem

Looks like Santa got himself in a bit of trouble this year! Seems like we're not dealing with time shenanigans, printer trouble, high-precision clock fueling or weather machine woes. This year we need to get Santa and his sleigh back from the edge of space.

But first... we need to get to him. Getting to him requires fuel. Let's calculate how much we'll need to make the trip.

Part 1 of this problem was a nice straightforward warmup. Part 2 was only a little bit trickier than the first, but I overthought it and gave myself the wrong answer anyway.

Ongoing Meta

Dev.to List of Leaderboards

If you were part of Ryan Palo's leaderboard last year, you're still a member of that!

If you want me to add your leaderboard code to this page, reply to one of these posts and/or send me a DM containing your code and any theming or notes you’d like me to add. (You can find your private leaderboard code on your "Private Leaderboard" page.)

I'll edit in any leaderboards that people want to post, along with any description for the kinds of people you want to have on it. (My leaderboard is being used as my office's leaderboard.) And if I get something wrong, please call me out or message me and I’ll fix it ASAP.

There's no limit to the number of leaderboards you can join, so there's no problem belonging to a "Beginner" and a language specific one if you want.

Neat Statistics

I'm planning on adding some statistics, but other than "what languages did we see yesterday" does anyone have any ideas?

Top comments (33)

Collapse
 
lindakatcodes profile image
Linda Thompson

I would love to be part of some DEV leaderboards! I'm not showing myself as part of any right now, and am more than willing to use my own private one as a DEV-centric one if we need it. :) Do we have a way to share those so we can start joining?

I'll be doing my solutions in JavaScript - they're usually fairly verbose, since it helps me think better that way. lol :) So likely pretty beginner-friendly!

Here's day 1's solutions:

const fs = require('fs');
const data = fs.readFileSync('../2019 Solutions/inputs/day01input.txt').toString();
const input = data.split('\r\n').map(Number);
const testInput = [
  14,
  1969,
  100756
];

// mass / 3, round down, -2
function formula(mass) {
  return Math.floor(mass / 3) - 2;
}

// Part 1
const fuelOfMass = input.map((curr) => {
  return formula(curr);
});

const totalFuel = fuelOfMass.reduce((acc, curr) => {
  return acc + curr;
}, 0);

console.log(`Part 1: ${totalFuel}`);

// Part 2

const totalFuelOfMass = input.map((curr) => {
  let value = curr;
  let accumulator = [];

  do {
    value = formula(value);
    if (value > 0) {
      accumulator.push(value);
    }
  } while (value > 0);

  return accumulator.reduce((acc, curr) => {
    return acc + curr;
  }, 0);
});

const newTotalFuel = totalFuelOfMass.reduce((acc, curr) => {
  return acc + curr;
}, 0);

console.log(`Part 2: ${newTotalFuel}`);
Collapse
 
jnschrag profile image
Jacque Schrag

Ooh this is a great use of do...while. I went with the less elegant nested if for mine πŸ˜…

Part 1 (JavaScript)

const fs = require("fs");
const path = require("path");
const text = fs.readFileSync(path.join(__dirname) + "/input.txt", "utf8");
let output = text
  .split("\n")
  .map(d => calcFuelPerModule(d))
  .reduce((acc, curr) => acc + curr, 0);
console.log(output);

function calcFuelPerModule(mass) {
  return Math.floor(+mass / 3) - 2;
}

Part 2 (JavaScript)

const fs = require("fs");
const path = require("path");
const text = fs.readFileSync(path.join(__dirname) + "/input.txt", "utf8");
let output = text
  .split("\n")
  .map(d => calcFuel(d))
  .reduce((acc, curr) => acc + curr, 0);
console.log(output);

function calcFuel(mass) {
  let fuel = calcFuelPerModule(mass);
  if (fuel > 0) {
    let fuelFuel = calcFuel(fuel);
    if (fuelFuel > 0) {
      fuel += calcFuel(fuel);
    }
  }
  return fuel;
}

function calcFuelPerModule(mass) {
  return Math.floor(+mass / 3) - 2;
}
Collapse
 
lindakatcodes profile image
Linda Thompson

Still works well! As long as it gets you the answer, it's good! :)

I saw someone use a ternary operator, and was super impressed. Might refactor mine later today to test that. :) Didn't even think of it!

Collapse
 
lindakatcodes profile image
Linda Thompson

Also, I couldn't resist the idea of the poem challenge they're doing on the subreddit, so I wrote an acrostic. :) Sharing here as well!

Adventure awaits!
Discover the cosmos
Venture into the unknown
Earn fifty stars to save Christmas!
No one goes alone, however
There's friendly folks to help

Overly dramatic situations await
Find Santa and bring him home!

Come code with us!
Outer space is calling
Don't be afraid
Elves will guide the way!

Collapse
 
brandonschreck profile image
Brandon Schreck • Edited

Very nice, I need to step my JS game up!

const data = [148216, 142030, 129401, 74642, 108051, 54128, 145495, 67818, 120225, 67113, 
107672, 101032, 147714, 55788, 87732, 73681, 114646, 76586, 116436, 139788, 125150, 136675, 
90527, 74674, 105505, 146059, 52735, 101389, 108121, 62897, 132337, 51963, 129188, 122308, 
84677, 66433, 118374, 66822, 94714, 101162, 54030, 136580, 55677, 114051, 133898, 95026, 
112964, 68662, 85139, 53559, 84703, 92053, 132197, 60130, 63184, 86182, 113038, 52659, 
140463, 123234, 97887, 70216, 131832, 108162, 116759, 111828, 132815, 113476, 127734, 
134545, 99643, 141911, 74705, 65720, 95640, 51581, 66787, 147590, 72937, 148774, 
119881, 139875, 131976, 68238, 100342, 134691, 112320, 86107, 100045, 120458, 
54459, 52047, 108226, 102138, 141233, 54452, 67859, 105132, 81903, 104282];

function calculateFuel(mass) {
  return Math.floor(mass / 3) - 2;
}

function getTotalFuel(mass) {
  let fuel = calculateFuel(mass);
  return fuel > 0 ? fuel += getTotalFuel(fuel) : 0;
}

function outputTestResults(index, testCase, result) {
  console.log(`Test ${ index + 1 }\n` +
    `Input: ${ testCase.input }\n` +
    `Expected Result: ${ testCase.output }\n` +
    `Result: ${ result }\n` +
    `Passes Test: ${ testCase.output === result }\n\n`);
}

// Part One
console.log(`Begin Part One`);
let partOneResult = 0;
const partOneTestCases = [{
    input: 12,
    output: 2
  },
  {
    input: 14,
    output: 2
  },
  {
    input: 1969,
    output: 654
  },
  {
    input: 100756,
    output: 33583
  },
];

// part one test cases
partOneTestCases.forEach(function(testCase, index) {
  outputTestResults(index, testCase, calculateFuel(testCase.input));
});

// part one do work
data.forEach(function(mass) {
  partOneResult += calculateFuel(mass);
});
console.log(`Part One Answer: ${partOneResult}`);

// Part Two
console.log(`Begin Part Two`);
let partTwoResult = 0;
const partTwoTestCases = [{
    input: 14,
    output: 2
  },
  {
    input: 1969,
    output: 966
  },
  {
    input: 100756,
    output: 50346
  }
];

// part two test cases
partTwoTestCases.forEach(function(testCase, index) {
  outputTestResults(index, testCase, getTotalFuel(testCase.input));
});

// do work
data.forEach(function(mass) {
  partTwoResult += getTotalFuel(mass);
});
console.log(`Part Two Answer: ${partTwoResult}`);
Collapse
 
coolshaurya profile image
Shaurya
Collapse
 
jbristow profile image
Jon Bristow

Post it here and I'll add it to the post and on further days posts! Go to you private leaderboard page and copy the code into a reply.

Collapse
 
lindakatcodes profile image
Linda Thompson

Alright, here's the code - 120635-5c140b9a.

We can make it for basically anyone on DEV, or we can focus it towards more beginner-friendly if we'd like - whatever works best for everyone!

Thread Thread
 
jbristow profile image
Jon Bristow

Updated the post to include it! Thank you!

Thread Thread
 
ballpointcarrot profile image
Christopher Kruse

Awesome! Thanks for creating a leaderboard for DEV.

Thread Thread
 
moopet profile image
Ben Sinclair

I'm in.

Collapse
 
jbristow profile image
Jon Bristow • Edited

My kotlin solution.

import java.nio.file.Files
import java.nio.file.Paths
import kotlin.math.max

private const val FILENAME = "src/main/resources/day01.txt"

fun fuelNeeded(mass: Int) = max((mass / 3) - 2, 0)


fun totalFuelNeeded(mass: Int): Int {
    var fuel = fuelNeeded(mass)
    var totalFuel = fuel
    while (fuel != 0) {
        fuel = fuelNeeded(fuel)
        totalFuel += fuel
    }
    return totalFuel
}

fun part1() = Files.readAllLines(Paths.get(FILENAME))
    .sumBy { fuelNeeded(it.toInt()) }
    .toString()

fun part2() = Files.readAllLines(Paths.get(FILENAME))
    .sumBy { totalFuelNeeded(it.toInt()) }
    .toString()

fun main() {
    println("Part 1: ${part1()}")
    println("Part 2: ${part2()}")
}

I'm going to clean this up to avoid imperative style and post it as a reply to this comment.

github.com/jbristow/adventofcode/t...

Collapse
 
jbristow profile image
Jon Bristow

This part 2 solution is much more elegant, and leverages monads.

tailrec fun totalFuelNeeded(mass: Int, fuel: Option<Int> = Option.empty(), totalFuel: Int = 0): Int =
    when (val nextFuel = fuel.fold({ fuelNeeded(mass) }, ::fuelNeeded)) {
        0 -> totalFuel
        else -> totalFuelNeeded(mass, nextFuel.just(), totalFuel + nextFuel)
    }
Collapse
 
ballpointcarrot profile image
Christopher Kruse

I didn't even think about using a max function - that would have been so much cleaner than making two functions!

Collapse
 
jbristow profile image
Jon Bristow

I love cleaning these up into more elegant forms almost as much as I love just getting the solution banged out!

Collapse
 
ballpointcarrot profile image
Christopher Kruse • Edited

Solution in Clojure:

(ns ballpointcarrot.aoc
  (:require [clojure.string :as st]))


(defn fuel-cost
  [value]
  (- (Math/floor (/ value 3)) 2))

(defn adj-fuel-cost
  [value]
  (let [fuel (fuel-cost value)]
    (if (pos? fuel)
      fuel
      0)))

(defn p2019-01
  "Calculate Fuel requirements"
  [input]
  (apply +
         (map #(fuel-cost (Integer/parseInt %)) (st/split-lines input))))

(defn recursive-fuel-cost
  [value]
  (loop [remains value
         total 0]
    (if (= remains 0)
      total
      (let [fuel (adj-fuel-cost remains)]
        (recur fuel (+ total fuel))))))

(defn p2019-01-part2
  "Calculate fuel requirements, including weight of fuel"
  [input]
  (apply +
         (map #(recursive-fuel-cost (Integer/parseInt %)) (st/split-lines input))))

Collapse
 
neilgall profile image
Neil Gall

Yay, Advent of Code is back. I'm not going to stick to one programming language this year, in fact I'm going to try to use lots, even some I've not used before. I'm not quite hardcore enough to do the different-language-every-day challenge however. Not this year anyway.

Day 1 in Haskell:

import Test.Hspec

load :: String -> [Int]
load = map read . lines

moduleFuel :: Int -> Int
moduleFuel mass = (mass `div` 3) - 2

fuelFuel :: Int -> Int
fuelFuel fuel = fuel + fuel' (moduleFuel fuel)
  where
    fuel' f = if f <= 0 then 0 else fuelFuel f 

part1 :: [Int] -> Int
part1 = sum . map moduleFuel

part2 :: [Int] -> Int
part2 = sum . map (fuelFuel . moduleFuel)

testModuleFuel = do
  moduleFuel 12 `shouldBe` 2
  moduleFuel 14 `shouldBe` 2
  moduleFuel 1969 `shouldBe` 654
  moduleFuel 100756 `shouldBe` 33583

testFuelFuel = do
  fuelFuel 2 `shouldBe` 2
  fuelFuel 654 `shouldBe` 966
  fuelFuel 33583 `shouldBe` 50346

test = do
  testModuleFuel
  testFuelFuel

main = do
  test
  input <- fmap load $ readFile "input.txt"
  putStrLn $ "Part 1 : " ++ (show $ part1 input)
  putStrLn $ "Part 2 : " ++ (show $ part2 input)

Once it was solved I did it again in Python just for fun:

from typing import Sequence

def load(file):
    with open(file, "rt") as f:
        return list(int(line) for line in f.readlines())

def moduleFuel(mass: int) -> int:
    return (mass // 3) - 2

def fuelFuel(mass: int) -> int:
    f = moduleFuel(mass)
    return mass + (0 if f <= 0 else fuelFuel(f))

def testModuleFuel():
    assert(moduleFuel(12) == 2)
    assert(moduleFuel(14) == 2)
    assert(moduleFuel(1969) == 654)
    assert(moduleFuel(100756) == 33583)

def testFuelFuel():
    assert(fuelFuel(2) == 2)
    assert(fuelFuel(654) == 966)
    assert(fuelFuel(33583) == 50346)


def part1(modules: Sequence[int]) -> int:
    return sum(moduleFuel(f) for f in modules)

def part2(modules: Sequence[int]) -> int:
    return sum(fuelFuel(moduleFuel(f)) for f in modules)

if __name__ == "__main__":
    testModuleFuel()
    testFuelFuel()
    input = load("input.txt")
    print(f"Part 1 : {part1(input)}")
    print(f"Part 2 : {part2(input)}")

And in the spirit of what I wrote at the top, I did it once more in Julia, my first ever code in that language. First impressions very positive and interestingly it came out shortest. I love the built-in unit testing.

using Test

load(file::AbstractString) = [parse(Int, line) for line in readlines(file)]

moduleFuel(mass::Int) = div(mass, 3) - 2

function fuelFuel(mass::Int)
  f = moduleFuel(mass)
  mass + if f <= 0 0 else fuelFuel(f) end
end

@testset "ModuleFuel" begin
  @test moduleFuel(12) == 2
  @test moduleFuel(14) == 2
  @test moduleFuel(1969) == 654
  @test moduleFuel(100756) == 33583
end

@testset "FuelFuel" begin
  @test fuelFuel(2) == 2
  @test fuelFuel(654) == 966
  @test fuelFuel(33583) == 50346
end

part1(modules) = sum(moduleFuel(m) for m in modules)

part2(modules) = sum(fuelFuel(moduleFuel(m)) for m in modules)

input = load("input.txt")
println("Part 1 : $(part1(input))")
println("Part 2 : $(part2(input))")
Collapse
 
lindakatcodes profile image
Linda Thompson

Hey folks - for anyone who's joined the leader board so far - I have a question!

So there are two main ways to sort our leader board, and I'm wondering which option would be best for us.

Option 1 - It counts how many people belong to the leader board (n), then the first user to get each star gets N points, the second gets N-1, and the last gets 1.

Option 2 - It counts how many stars you have, and then any ties are broken by the time you got the star. So if everyone gets all of the stars, the ones who get them the fastest are higher up.

I think either way is likely fine, but feel like maybe the star sort option might be best - it seems like it would encourage completion of the challenges more so than simply getting them done fastest, but still includes some speed needed to be higher up. Would love to hear some other thoughts on this, and whichever option seems most popular is what I'll sort it by!

Collapse
 
avalander profile image
Avalander

I like option 2 a bit better. I'd rather see the people that manage to solve the more complicated problems at the top than those who solved the first few problems the fastest.

Collapse
 
savagepixie profile image
SavagePixie

Same here, option 2 sounds better.

Collapse
 
jbristow profile image
Jon Bristow

In the check for 5 digits or less.

Collapse
 
coolshaurya profile image
Shaurya

My solution using Rust.


fn part_a(input: Vec<u32>) {
    let output: u32 = input.iter().map(|val| (val - (val % 3)) / 3 - 2).sum();
    println!("{:?}", output);
}

fn part_b(input: Vec<u32>) {
    let output: u32 = input.iter().map(|val| rec_fuel_calc(*val)).sum();
    println!("{:?}", output);
}

fn rec_fuel_calc(mass: u32) -> u32 {
    let answer = ((mass - (mass % 3)) / 3) - 2;
    if answer < 9 {
        return answer;
    } else {
        return answer + rec_fuel_calc(answer);
    }
}
Collapse
 
rpalo profile image
Ryan Palo

This was a good ramp into the month! Here's my Rust solution. :)

/// Day 1: The Tyranny of the Rocket Equation
/// 
/// Calculate the amount of fuel required to launch your spaceship!

use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;

/// Calculate the fuel required to lift one module, based on its mass
fn fuel_required(mass: &usize) -> usize {
    (mass / 3) - 2
}

/// Calculates the fuel required to lift one module, but factors in the
/// weight of the fuel as well
fn recursive_fuel_required(mass: &usize) -> usize {
    if *mass <= 6 {
        0
    } else {
        let next_mass = (mass / 3) - 2;
        next_mass + recursive_fuel_required(&next_mass)
    }
}

/// Calculate the total fuel requirements for the launch
fn fuel_requirements(module_masses: Vec<usize>) -> usize {
    module_masses.iter().map(recursive_fuel_required).sum()
}


/// Parses the input file, which is a bunch of numbers, one per line
fn parse_input() -> Vec<usize> {
    let buf = BufReader::new(File::open("data/day1.txt").unwrap());
    buf.lines()
        .map(|result| result.unwrap())
        .map(|line| line.parse::<usize>().unwrap())
        .collect()
}

/// Main day 1 code
pub fn run() {
    let data = parse_input();
    let total_requirements = fuel_requirements(data);
    println!("Total fuel required: {}", total_requirements);
}
Collapse
 
jb_ profile image
Joshua Blewitt

This is my solution to Part 1 in Ruby - working on Part 2!

#advent of code day 1
#lets open the input file
target = File.open("input.txt")
#part 1
def calculation (file)
    total = 0
    file.each{|number|
    number = number.to_i / 3 - 2
    total += number 
    }
    puts "Our total is - #{total}"
end

calculation(target)
target.close
Collapse
 
fossheim profile image
Sarah

I joined the leaderboard, but since I'm in a EU timezone and only have time to work on them in the evenings I'm expecting my scores to be quite low πŸ˜…

Here's my solution for day 1 with Python (explained my thinking behind it here):

Part 1:

input = np.array([input])
output = np.sum(np.floor(input / 3) - 2)

Part 2:

for module in input_array:
    new_fuel = mod
    while True:
        new_fuel = np.floor(new_fuel / 3) - 2
        if new_fuel > 0:
            total_fuel += new_fuel
        else:
            break
Collapse
 
rizzu26 profile image
Rizwan

Bit late to the party!

Here is my solution in swift. Incase anyone would like followup code can be found here in Github

func fuel(forMass mass: Int) -> Int {
    return Int((Double(mass) / 3).rounded(.down) - 2)
}

func partOne() {
    var total = 0
    input.components(separatedBy: .newlines).map{ input in
        total = total + fuel(forMass: Int(input) ?? 0)
    }
    print(total)
}

func fuel(forMass mass: Int, includesFuelMass: Bool) -> Int {
    if !includesFuelMass {
        return fuel(forMass: mass)
    }

    var currentFuel = mass
    var total = 0
    while true {
        let fuels = fuel(forMass: currentFuel)
        if fuels < 0 {
            break
        }
        currentFuel = fuels
        total = total + currentFuel
    }
    return total
}

func partTwo() {
    var total = 0
    input.components(separatedBy: .newlines).map{ input in
        total = total + fuel(forMass: Int(input) ?? 0, includesFuelMass: true)
    }
    print(total)
}

partOne()
partTwo()