DEV Community

Cover image for Advent of code - Day 7
Quentin Ménoret
Quentin Ménoret

Posted on

Advent of code - Day 7

Are you participating in the Advent of code this year?

If you don't know what the advent of code is, it's a website where you'll find a daily challenge (every day it gets harder). It's a really fun event, you should participate!

I try to solve the exercises using either JavaScript or TypeScript and will share my solutions daily (with one day delay so no one can cheat!). I only share the solution for the second part.

For day #7, I created a sort a tree like structure, but flat (I'm lazy), represented by a Record. Basically, for each color, you know which colors you can contains.

Once you have such a structure, finding the answer is just a matter of a small recursion:

const tree = input.reduce((tree, line) => {
  const color = /(^.*) bags contain/.exec(line)[1];
  tree[color] = [];

  const matches = line.matchAll(/,? (\d+) ([^,.]*) bags?/g);
  for (const match of matches) {
    for (let i = 0; i < parseInt(match[1]); i++) {
      tree[color].push(match[2]);
    }
  }
  return tree;
}, {});

const depth = (color) => {
  if (tree[color] === []) return 1;
  return tree[color].reduce((acc, color) => acc + depth(color), 1);
};

console.log(depth("shiny gold") - 1);
Enter fullscreen mode Exit fullscreen mode

Feel free to share your solution in the comments!


Photo by Markus Spiske on Unsplash

Top comments (5)

Collapse
 
oguimbal profile image
Olivier Guimbal

For once, the JS version is compact :) - where's part 2, though ? 😊

A flat structure is indeed simpler to manage, given that we have to traverse the graph both ways (part 1 is basically "count all my parents" and part 2 "count all my descendents")

Haskell version (of both part 1 & 2)

norm = filter isAlphaNum

ruleOf r =
  let [color, bagStr] = splitOn " bags contain " r
   in splitOn ", " bagStr
        & filter (/= "no other bags.")
        & map (unwords . init . words)
        & map norm
        & map (break isAlpha)
        & map (\(a, b) -> (norm color, (read a :: Int, b)))

allContainers rules color =
  let containers = map fst $ filter ((== norm color) . snd . snd) rules
   in color : concatMap (allContainers rules) containers

allContained rules color =
  let contained = map snd $ filter ((== norm color) . fst) rules
   in 1 + sum (map (\c -> fst c * (allContained rules . snd) c) contained)

ans = do
  rules <- concatMap ruleOf . lines <$> readFile "./data.txt"
  return (
        Set.size . Set.fromList . filter (/= "shiny gold") $ allContainers rules "shiny gold",
        allContained rules "shiny gold" - 1
    )
Enter fullscreen mode Exit fullscreen mode
Collapse
 
qmenoret profile image
Quentin Ménoret

This is part 2!

Actually I always traversed in the same order. I just bruteforced in part 1, trying all possible start point 😂 This is my part 1 solution:

const tree = input.reduce((tree, line) => {
  const color = /(^.*) bags contain/.exec(line)[1];
  tree[color] = [];

  const matches = line.matchAll(/,? \d+ ([^,.]*) bags?/g);
  for (const match of matches) {
    tree[color].push(match[1]);
  }
  return tree;
}, {});

const canContainGold = (color) => {
  if (tree[color].includes("shiny gold")) return true;
  return tree[color].some((color) => canContainGold(color));
};

const filtered = Object.keys(tree).filter((color) => {
  return canContainGold(color);
});

console.log(filtered.length);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
oguimbal profile image
Olivier Guimbal

oh yes right :)

Collapse
 
qmenoret profile image
Quentin Ménoret • Edited

Damn, it's been a long time I haven't written a single line of Haskell...
I thought doing it with Haskell but... Pretty sure I would have given up already!
Good job!

Collapse
 
oguimbal profile image
Olivier Guimbal

Advent of code is quite a good occasion to practice or learn. So I figured "given that you like hurting yourself, why not doing it in Haskell ?" (I dont use it often)