Advent of Code 2020-07 with R & Neo4j

colinfay

Colin Fay

Posted on December 15, 2020

Advent of Code 2020-07 with R & Neo4j

Solving Advent of Code 2020-07 with R & Neo4j.

[Disclaimer] Obviously, this post contains a big spoiler about Advent of Code.

Instructions

Find the complete instructions at:https://adventofcode.com/2020/day/7.

R & Neo4j solution

As today’s options was a graph challenge, I decided to play a little bit with R and Neo4J.

Part one

library(tidyverse)


## ── Attaching packages ───────────── tidyverse 1.3.0 ──

## ✓ ggplot2 3.3.0 ✓ purrr 0.3.4
## ✓ tibble 3.0.1 ✓ dplyr 1.0.2
## ✓ tidyr 1.0.3 ✓ stringr 1.4.0
## ✓ readr 1.3.1 ✓ forcats 0.5.0

## ── Conflicts ──────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()

# Reading the data
input <- read.delim(
  "2020-07-aoc.txt", 
  header = FALSE, 
  stringsAsFactors = FALSE
) %>% as_tibble()

clean <- input %>%
  # Extracting the origin
  mutate(
    origin = gsub("(.*) bags .*", "\\1", V1), 
    contain = gsub(".* contain (.*) bags*\\.", "\\1", V1), 
    contain = map(contain, ~ (strsplit(.x, ","))[[1]])
  ) %>%
  unnest(contain) %>%
  # Extracting what they can contain
  mutate(
    n_contain = gsub(".*([0-9]+).*", "\\1", contain),
    n_contain = case_when(
      n_contain == "no other" ~ "0", 
      TRUE ~ n_contain
    ) %>% as.character(),
    contain = gsub(" bags*", "", contain),
    contain = gsub("[0-9]*", "", contain),
    contain = stringr::str_trim(contain)
  ) %>%
  # Keeping only the needed elements
  select(
    origin, contain, n_contain
  )
clean

## # A tibble: 1,395 x 3
## origin contain n_contain
## <chr> <chr> <chr>    
## 1 clear maroon dull lavender 1        
## 2 wavy turquoise vibrant magenta 4        
## 3 wavy turquoise light violet 4        
## 4 wavy turquoise bright gold 5        
## 5 wavy turquoise faded black 2        
## 6 wavy beige plaid magenta 3        
## 7 wavy beige wavy lime 3        
## 8 wavy beige clear turquoise 2        
## 9 wavy beige muted cyan 3        
## 10 mirrored black plaid red 1        
## # … with 1,385 more rows


# Launching the container

system("docker run --rm --name neo4j --env NEO4J_AUTH=neo4j/password --publish=7687:7687 --publish=7474:7474 -v $(pwd):/var/lib/neo4j/import -d neo4j:3.4")

library(neo4r)
# Connecting to the Neo4j API
con <- neo4j_api$new(
  url = "http://localhost:7474", 
  user = "neo4j", 
  password = "password"
)

# Waiting for the container to be ready
Sys.sleep(10)
while (try(con$ping()) != 200){
  Sys.sleep(10)
}


# Writing CSV for Neo4J
data.frame(
  nodes = unique(clean$origin, clean$contain)
) %>%
  write_csv("aoc07_nodes.csv")
clean %>%
  write_csv("aoc07_relationships.csv")


# Adding constraint of uniqueness on names
"CREATE CONSTRAINT ON (origin:origin) ASSERT origin.name IS UNIQUE;" %>%
  call_neo4j(con, include_stats = TRUE)

## No data returned.

## # A tibble: 12 x 2
## type value
## <chr> <dbl>
## 1 contains_updates 1
## 2 nodes_created 0
## 3 nodes_deleted 0
## 4 properties_set 0
## 5 relationships_created 0
## 6 relationship_deleted 0
## 7 labels_added 0
## 8 labels_removed 0
## 9 indexes_added 0
## 10 indexes_removed 0
## 11 constraints_added 1
## 12 constraints_removed 0

"CREATE CONSTRAINT ON (contain:contain) ASSERT contain.name IS UNIQUE;" %>%
  call_neo4j(con, include_stats = TRUE)

## No data returned.

## # A tibble: 12 x 2
## type value
## <chr> <dbl>
## 1 contains_updates 1
## 2 nodes_created 0
## 3 nodes_deleted 0
## 4 properties_set 0
## 5 relationships_created 0
## 6 relationship_deleted 0
## 7 labels_added 0
## 8 labels_removed 0
## 9 indexes_added 0
## 10 indexes_removed 0
## 11 constraints_added 1
## 12 constraints_removed 0


# Load the nodes
'LOAD CSV WITH HEADERS FROM "file:///aoc07_nodes.csv" AS csvLine
MERGE (:bag {name : csvLine.nodes} ) ' %>%
  call_neo4j(con, include_stats = TRUE)

## No data returned.

## # A tibble: 12 x 2
## type value
## <chr> <dbl>
## 1 contains_updates 1
## 2 nodes_created 594
## 3 nodes_deleted 0
## 4 properties_set 594
## 5 relationships_created 0
## 6 relationship_deleted 0
## 7 labels_added 594
## 8 labels_removed 0
## 9 indexes_added 0
## 10 indexes_removed 0
## 11 constraints_added 0
## 12 constraints_removed 0


# Load the relationships
'LOAD CSV WITH HEADERS FROM "file:///aoc07_relationships.csv" AS csvLine
MATCH (c:bag {name : csvLine.contain})
MATCH (o:bag {name : csvLine.origin})
MERGE (o) -[:CAN_CONTAIN {n_contain: csvLine.n_contain}]->(c);' %>%
  call_neo4j(con, include_stats = TRUE)

## No data returned.

## # A tibble: 12 x 2
## type value
## <chr> <dbl>
## 1 contains_updates 1
## 2 nodes_created 0
## 3 nodes_deleted 0
## 4 properties_set 1385
## 5 relationships_created 1385
## 6 relationship_deleted 0
## 7 labels_added 0
## 8 labels_removed 0
## 9 indexes_added 0
## 10 indexes_removed 0
## 11 constraints_added 0
## 12 constraints_removed 0


# Count all the bags that can eventually contain a shiny gold ones
'MATCH (b:bag)-[:CAN_CONTAIN*1..]-> (:bag {name:"shiny gold"}) RETURN count( DISTINCT b) AS count' %>%
  call_neo4j(con)

## $count
## # A tibble: 1 x 1
## value
## <int>
## 1 126
## 
## attr(,"class")
## [1] "neo" "list"
Enter fullscreen mode Exit fullscreen mode

Part two

'MATCH (:bag {name:"shiny gold"})-[r:CAN_CONTAIN*1..]-> (:bag) WITH reduce(tot = 1, i IN r| tot * toInteger(i.n_contain)) AS total RETURN sum(total) AS total' %>%
  call_neo4j(con)

## $total
## # A tibble: 1 x 1
## value
## <int>
## 1 220149
## 
## attr(,"class")
## [1] "neo" "list"


# Removing the container
system("docker kill neo4j")

unlink("aoc07_nodes.csv")
unlink("aoc07_relationships.csv")
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
colinfay
Colin Fay

Posted on December 15, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Advent of Code 2020-07 with R & Neo4j
rstats Advent of Code 2020-07 with R & Neo4j

December 15, 2020