• Skip to main content
  • Skip to primary sidebar
BMA

BeMyAficionado

Inspire Affection

Advent Of Code 2020 – Day 7 – Handy Haversacks

December 18, 2020 by varunshrivastava 2 Comments

Finally, Directed Weighted Graphs have made their way to the Advent Of Code 2020 on Day 7. If you are unfamiliar with graphs then I highly recommend you to go through the below articles:

  • Undirected Graphs and Graph Processing

This above article will give you enough information to get you started with today’s problem. On this note, let’s start with today’s problem.

We reached the regional office in time, even before time. But now there seems to be a new problem with the luggage processing.

Due to recent aviation regulations, many rules (your puzzle input) are being enforced about bags and their contents; bags must be colour-coded and must contain specific quantities of other colour-coded bags. Apparently, nobody responsible for these regulations considered how long they would take to enforce!

For example, consider the following rules:

light red bags contain 1 bright white bag, 2 muted yellow bags.
dark orange bags contain 3 bright white bags, 4 muted yellow bags.
bright white bags contain 1 shiny gold bag.
muted yellow bags contain 2 shiny gold bags, 9 faded blue bags.
shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags.
dark olive bags contain 3 faded blue bags, 4 dotted black bags.
vibrant plum bags contain 5 faded blue bags, 6 dotted black bags.
faded blue bags contain no other bags.
dotted black bags contain no other bags.

These rules specify the contents of the 9 bag types. In the above example, every faded blue bag is empty, every vibrant plum bag contains 11 bags (5 faded blue bags and 6 dotted black bags) and so on…

So, here’s our problem,

You have a shiny gold bag. How many bags would be valid to carry it?

For example – shiny gold bag is can be carried by muted yellow bag, muted yellow bag can be carried inside dark orange bag. Therefore, shiny bag can be carried inside dark orange bag.

I hope the problem is much clear with the above example.

How many bag colors can eventually contain one shiny gold bag?

Where would you start?

Table of Contents

  • Part One – Find all the bags that can contain one shiny gold bag
    • Weighted Graph
    • Weighted Graph Builder
    • Weighted Graph Processor
  • Part Two – Count total bags inside single shiny gold bag recursively

Part One – Find all the bags that can contain one shiny gold bag

This time I’m going to use JAVA because it’s all going to be Classes and Objects (in short, object-oriented) and what’s better than Java (after version 8 :p) when it comes to object-oriented programming.

When I first saw this question the first thing that came to my mind was a Weighted Graph. So, I started from there. I highly recommend you read the following article if you have not worked with graphs before.

  • Undirected Graphs and Graph Processing

The represent the graph, I’ll use Adjacency List Representation (Talked in detail in the above article).

Weighted Graph

Weighted Graph

This is how my WeightedGraph class looks like:

Weighted Graph Class Structure
Weighted Graph Class Structure

Here’s the code for the same. I’ve used the Bag here as a data structure to hold other bags. Although it suits the context of the problem, but Bag is actually a data structure which is used in Graphs.

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class WeightedGraph {

    int totalEdges = 0;
    private Map<String, Bag<WeightedEdge>> edges;

    public WeightedGraph() {
        edges = new HashMap<>();
    }

    public void addEdge(WeightedEdge edge) {
        if (edges.containsKey(edge.getV1()))
            edges.get(edge.getV1()).add(edge);
        else {
            final Bag<WeightedEdge> bag = new Bag<>();
            bag.add(edge);
            edges.put(edge.getV1(), bag);
        }
        totalEdges += 1;
    }

    public int totalEdges() {
        return totalEdges;
    }

    public List<WeightedEdge> adj(String v) {
        return edges.getOrDefault(v, Bag.empty());
    }

    public int totalVertices() {
        return edges.size();
    }

    public void print() {
        this.edges.forEach((key, val) ->
                System.out.println(key + " -> " + val.stream()
                        .map(WeightedEdge::getV2)
                        .collect(Collectors.joining(" "))));
    }

    public Map<String, List<WeightedEdge>> get() {
        return Collections.unmodifiableMap(edges);
    }

}

I’m not going to bore you here by explaining my code. Go through the code yourself, its pretty explanatory and if you do not understand something then do call out in the comments below.

So moving on…

Now, that we have the weighted graph data structure ready, all we need is a builder that will parse the input and build the graph for us.

Let’s build the WeightedGraphBuilder next.

Weighted Graph Builder

There are two types of lines that we have to parse.

light red bags contain 1 bright white bag, 2 muted yellow bags.
.
.
.
dotted black bags contain no other bags.

Here are the parsing rules that I have used in the code:

  • There is only one bag to the left of the word “contain”.
  • There could be multiple bags after the word “contain”. All bags to the right of ‘contain’ is separated via comma (,).
  • The keyword bag or bags have no role to play in the problem so will filter those.
  • Bag names are separated by whitespaces.
    • The first part contains the count of the bag (weight),
    • the middle part contains the colour of the bag,
    • the last part can be simply ignored as it always ends with either bag or bags.
  • Whenever the right part contains no other bags it means this is our leaf node.

Cool!!! So with all these rules laid out, let’s write our graph builder.

Here’s how the class structure looks like:

Weighted Graph Builder
Weighted Graph Builder Class Structure

And here’s the code for the same.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class WeightedGraphBuilder {
    /**
     * Sample Input
     * ----------------
     * light red bags contain 1 bright white bag, 2 muted yellow bags.
     * dark orange bags contain 3 bright white bags, 4 muted yellow bags.
     * bright white bags contain 1 shiny gold bag.
     * muted yellow bags contain 2 shiny gold bags, 9 faded blue bags.
     * shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags.
     * dark olive bags contain 3 faded blue bags, 4 dotted black bags.
     * vibrant plum bags contain 5 faded blue bags, 6 dotted black bags.
     * faded blue bags contain no other bags.
     * dotted black bags contain no other bags.
     *
     * @param filePath
     * @return
     */
    public static final String CONTAIN_REGEX = " contain ";
    public static final int ZERO = 0;
    public static final String BAG_REGEX = "bag";
    public static final String COMMA_REGEX = ",";

    public WeightedGraph buildFromFileInput(Path filePath) {
        WeightedGraph wg = new WeightedGraph();
        try {
            Files.readAllLines(filePath).forEach(line ->
                    buildEdge(parseV1(line), line.split(CONTAIN_REGEX)[1].split(COMMA_REGEX))
                    .forEach(wg::addEdge));
        } catch (IOException e) {
            // never do this in production code
            e.printStackTrace();
        }

        return wg;
    }

    private String parseV1(String line) {
        // Line: light red bags contain 1 bright white bag, 2 muted yellow bags.
        // After Split:     light red bags
        // After Substring: light red
        return line.split(CONTAIN_REGEX)[ZERO]
                .substring(ZERO, line.split(CONTAIN_REGEX)[ZERO]
                        .indexOf(BAG_REGEX))
                .trim();
    }

    private List<WeightedEdge> buildEdge(String from, String[] edgesParts) {
        return Arrays.stream(edgesParts)
                .map(vw -> new WeightedEdge(from, vw.trim()))
                .collect(Collectors.toList());
    }
}

If you are wondering how I’m so sure if the above code works great. Well, I’ve got the test cases for that in place. And you should have them too. I will not share the test case here but if you are interested in the entire code at one place then you can check out my GitHub (@vslala) for the same.

Great!!! with that let’s move to the next part of the puzzle and that is to process the graph for the required information.

Weighted Graph Processor

Processors are always different from the graph because you can process the same graph in multiple ways for different information. Therefore, keeping the processing logic outside of the graph always helps to make you code extendable.

This is the class which needs a little bit of explanation I guess, as it involves the main logic for solving the problem, the depth-first search on the weighted graph. So, before jumping into the logic part directly, let me show you the structure of the class. I think that way it would be easier to understand what’s going on in the code at a very high-level.

Weighted Graph  Processor Class Structure
Weighted Graph Processor Class Structure

Here’s the code for the same.

import lombok.RequiredArgsConstructor;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

@RequiredArgsConstructor
public class WeightedGraphProcessor {

    private final WeightedGraph graph;
    private final AtomicBoolean isBagFound = new AtomicBoolean(false);
    private final Set<String> part1 =  new HashSet<>();

    public int findAllBagsContainingBag(String bagColor) {
        Set<String> vertices = graph.get().keySet();
        vertices.forEach(vertex -> {
            isBagFound.set(false);
            dfs(vertex, graph, bagColor);
        });

        return part1.size();
    }

    private void dfs(String vertex, WeightedGraph graph, String bagColor) {
        if  (isBagFound.get() || vertex.equals(bagColor)) {
            isBagFound.set(true);
            return;
        }

        if (vertex.isEmpty()) return;

        graph.adj(vertex).forEach(edge -> {
            dfs(edge.other(vertex), graph, bagColor);
            if (isBagFound.get())
                part1.add(vertex);
        });
    }

    public int countAllBagsInsideABag(String bagColor) {
        return countTotalBags(bagColor);
    }

}

So as always let’s take the actual example to understand the logic of the code.

light red bags contain 1 bright white bag, 2 muted yellow bags.
dark orange bags contain 3 bright white bags, 4 muted yellow bags.
bright white bags contain 1 shiny gold bag.
muted yellow bags contain 2 shiny gold bags, 9 faded blue bags.
shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags.
dark olive bags contain 3 faded blue bags, 4 dotted black bags.
vibrant plum bags contain 5 faded blue bags, 6 dotted black bags.
faded blue bags contain no other bags.
dotted black bags contain no other bags.

In the above example, if we plot the directed graph from left to right bags, this is what we get till shiny gold. So, the depth -first search will be done from each vertex (bag) till we reach an empty bag or shiny gold.

And while doing that we will keep track of the edges that led to the shiny gold bag and that will be our answer.

Directed Graph on Example Input
Directed Graph

Let’s say you start from Light Red, you will visit Bright White to Shiny Gold, then you will go from Muted Yellow to Shiny Gold. This way you know that Light Red, Bright White and Muted Yellow can contain shiny gold bag.

Wait, I have one more diagram for you. The Sequence Diagram.

Sequence Diagram
Sequence Diagram

After this sequence diagram, I think most of the things will be clear and if there is anything that you want me to explain then please do comment below.

For Part One, I will leave it here.

By the way, the answer was correct. You can call method findAllBagsContainingBag("shiny gold") to get the answer.

Let’s start with the Part Two.

Part Two – Count total bags inside single shiny gold bag recursively

This is the extension of the existing problem. And this will be easy now because we made the Directed Weighted Graph in the first part itself.

In this part, we have to count all the bags inside shiny gold bag recursively. So, it’s a depth-first search that will start from the “shiny gold” bag and keep on adding the weights to the sum. This is similar to the formula they have explained in the example i.e. 1 + 1*7 + 2 + 2*11 = 32 bags!

Here’s the code that we need to write to find the total count of the bags inside “shiny gold”.

public int countAllBagsInsideABag(String bagColor) {
    return countTotalBags(bagColor);
}

private int countTotalBags(String bagColor) {
    if (graph.adj(bagColor).isEmpty())
        return 0;
    else
        return graph.adj(bagColor)
                .stream()
                .mapToInt(edge -> (int) (edge.getW() * (1 + countTotalBags(edge.other(bagColor)))))
                .sum();
}

This code calls the countTotalBags recursively and adds the count to the count returned from the function. At the end you get the total bags count.

Here’s the sequence diagram for the above code.

Sequence  Diagram for counting total bags inside Shiny Gold Bag.
Sequence Diagram for counting total bags inside Shiny Gold Bag.

This article grew a lot bigger than I expected. But I hope you understand the concept and hopefully learned something new through this problem.

Let me know how you find it in the comments below.

Related

Filed Under: Programming Tagged With: aoc-2020, day-7, directed-graphs, graphs, java, oops, problem-solving

Primary Sidebar

Subscribe to Blog via Email

Do you enjoy the content? Feel free to leave your email with me to receive new content straight to your inbox. I'm an engineer, you can trust me :)

Join 874 other subscribers

Latest Podcasts

Recent Posts

  • Is The Cosmos a Vast Computation?
  • Building Semantic Search for E-commerce Using Product Embeddings and OpenSearch
  • Leader Election with ZooKeeper: Simplifying Distributed Systems Management
  • AWS Serverless Event Driven Data Ingestion from Multiple and Diverse Sources
  • A Step-by-Step Guide to Deploy a Static Website with CloudFront and S3 Using CDK Behind A Custom Domain

Recent Comments

  • Varun Shrivastava on Deploy Lambda Function and API Gateway With Terraform
  • Vaibhav Shrivastava on Deploy Lambda Function and API Gateway With Terraform
  • Varun Shrivastava on Should Girls Wear Short Clothes?
  • D on Should Girls Wear Short Clothes?
  • disqus_X5PikVsRAg on Basic Calculator Leetcode Problem Using Object-Oriented Programming In Java

Categories

  • Blogging
  • Cooking
  • Fashion
  • Finance & Money
  • Programming
  • Reviews
  • Software Quality Assurance
  • Technology
  • Travelling
  • Tutorials
  • Web Hosting
  • Wordpress N SEO

Archives

  • November 2024
  • September 2024
  • July 2024
  • April 2024
  • February 2024
  • November 2023
  • June 2023
  • May 2023
  • April 2023
  • August 2022
  • May 2022
  • April 2022
  • February 2022
  • January 2022
  • November 2021
  • September 2021
  • August 2021
  • June 2021
  • May 2021
  • April 2021
  • February 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • April 2020
  • February 2020
  • December 2019
  • November 2019
  • October 2019
  • August 2019
  • July 2019
  • June 2019
  • May 2019
  • April 2019
  • March 2019
  • January 2019
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • March 2018
  • February 2018
  • January 2018
  • December 2017
  • November 2017
  • October 2017
  • September 2017
  • August 2017
  • July 2017
  • June 2017
  • May 2017
  • April 2017
  • March 2017
  • February 2017
  • January 2017
  • December 2016
  • November 2016
  • October 2016
  • September 2016
  • August 2016
  • July 2016
  • June 2016
  • May 2016

Tags

Affordable Hosting (4) algorithms (4) amazon (3) aoc-2020 (7) believe in yourself (4) best (4) database (4) earn money blogging (5) education (4) elementary sorting algorithms (4) experience (3) fashion (4) finance (6) Financial Freedom (7) food (7) friends (3) goals (5) google (5) india (10) indian cuisine (5) indian education system (4) java (16) life (16) life changing (4) love (4) make money (3) microservices (9) motivation (4) oops (4) podcast (6) poor education system (4) principles of microservices (5) problem-solving (7) programmer (5) programming (28) python (5) reality (3) seo (6) spring (3) success (10) success factor (4) technology (4) top 5 (7) typescript (3) wordpress (7)

Copyright © 2025 · Be My Aficionado · WordPress · Log in

Go to mobile version