Each report has varying number of levels, so I used readLines to import the whole thing in as a data frame, and then used separate_wider_delim to split the character vector out into columns. I don’t know the full length of rows, so i chose a large value and then dropped columns containing no data. Then convert to a numeric matrix.
The Red-Nosed reactor safety systems can only tolerate levels that are A) either all increasing or all decreasing, or B) Any two adjacent levels differ by at least one and at most three.
okay, so we need a difference between column j+1 and j. Easiest way would probably be to do a loop.
The same rules apply as before, except if removing a single level from an unsafe report would make it safe, the report instead counts as safe.
I was stumped, so I’m trying a tactic idea from Angel Martinez.
Check between each level for whether or not it was a safe transition. Then count the number of unsafe levels per row.
Toggle the code
angels.if_safe.fun <-function(i, j){ diff <-abs(exa[i,j] - exa[i,j-1]) flip <- (sign(exa[i,j]) !=sign(exa[i,j-1])) status <- dplyr::case_when(diff ==0~"unsafe", diff >3~"unsafe", flip ==TRUE~"unsafe",.default ="safe")return(status)}angels.if_safe.fun(3, 5)# set an indicator of the number of unsafe levelsis.safe <-rep("safe", NROW(exa))for(i in1:NROW(exa)){ J <-length(na.omit(exa[i,])) # number of non-NA entries err <-0for(j in2:J){# run the function to check for an unsafe level.if(angels.if_safe.fun(i, j) =="unsafe"){# if j is unsafe, and not at J, delete it and put j+1 where j is at exa[i, j:J-1] <- exa[i, j+1:J] exa[i, J] <-NA err <-1 }# re-run safe check function on the same j. If another error then flag as unsafeif(angels.if_safe.fun(i, j) =="unsafe"& err==1){ is.safe[i] <-"unsafe" } }}(safe.if.remove.1<- n.unsafe<2)
Yea.. this isn’t working b/c when j=J, there is no j+1.
Session info
Toggle
─ Session info ───────────────────────────────────────────────────────────────
setting value
version R version 4.4.1 (2024-06-14)
os macOS Sonoma 14.6.1
system aarch64, darwin20
ui X11
language (EN)
collate en_US.UTF-8
ctype en_US.UTF-8
tz America/Los_Angeles
date 2024-12-13
pandoc 3.1.11 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown)
─ Packages ───────────────────────────────────────────────────────────────────
package * version date (UTC) lib source
aochelpers * 0.1.0.9000 2024-12-02 [1] Github (EllaKaye/aochelpers@d4ccd91)
[1] /Users/rdonatello/Library/R/arm64/4.4/library
[2] /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library
──────────────────────────────────────────────────────────────────────────────
Source Code
---title: "2024: Day 02 Red-Nosed Reports"date: 2024-12-02categories: - loops - tidyr - NAs - one_stardraft: false---## Setup[The original challenge](https://adventofcode.com/2024/day/02)# Input dataEach report has varying number of levels, so I used readLines to import the whole thing in as a data frame, and then used `separate_wider_delim` to split the character vector out into columns. I don't know the full length of rows, so i chose a large value and then dropped columns containing no data. Then convert to a numeric matrix. ```{r}library(aochelpers)input <-readLines("~/GitHub/AdventOfCode/2024/day/2/input") |>data.frame()head(input)input2 <- tidyr::separate_wider_delim(input, cols =everything(), delim =" ",names =paste0("rpt", 1:8), too_few ="align_start")input.num <-apply(input2, 2, as.numeric)# apply(input2, 2, function(x)mean(is.na(x))) # identify blank columns```# TLDR; Solutions## Part 1 ⭐::: {.callout-danger}### ❓ How many reports are safe?:::```{r}input.diffmat <-matrix(NA, nrow =NROW(input.num), ncol =NCOL(input.num)-1)for(i in1:NROW(input.num)){for(j in2:NCOL(input.num)){ input.diffmat[i, j-1] <- input.num[i, j]-input.num[i, j-1] }}flag.size <- input.diffmat ==0|abs(input.diffmat) >3# adjusted here for missing valuesunsafe <-rowSums(flag.size, na.rm =TRUE) >0for(i in1:NROW(input.diffmat)){if(unsafe[i] ==FALSE){# adjusted for missing values unsafe[i] <-length(rle(input.diffmat[i,which(!is.na(input.diffmat[i,]))]>0)$lengths)>1 }}sum(!unsafe)```## Part 2 ⭐⭐::: {.callout-danger}### ❓ :::# Walkthrough / Explainer## Part 1First place to look for the chief is the Red-Nosed Reindeer nuclear fusion/fission plant. But they need help with some data crunching. :::{.callout-exa icon=true}**Example Data**One line per report, one column per level. Example data has 6 reports with 5 levels each. ```{r}exa <-c("7 6 4 2 1", "1 2 7 8 9", "9 7 6 2 1", "1 3 2 4 5", "8 6 4 4 1", "1 3 6 7 9") |>lines_to_matrix(split =' ')class(exa) <-"numeric"exa```:::> The Red-Nosed reactor safety systems can only tolerate levels that are A) either all increasing or all decreasing, or B) Any two adjacent levels differ by at least one and at most three.okay, so we need a difference between column j+1 and j. Easiest way would probably be to do a loop. ```{r}exa.diffmat <-matrix(NA, nrow =NROW(exa), ncol =NCOL(exa)-1)for(i in1:NROW(exa)){for(j in2:NCOL(exa)){ exa.diffmat[i, j-1] <- exa[i, j]-exa[i, j-1] }}exa.diffmat```Now to flag the unsafe rows based on size of difference. I want the result to be TRUE/FALSE for later indexing. ```{r}flag.size <- exa.diffmat ==0|abs(exa.diffmat) >3unsafe <-apply(flag.size, 1, sum)|>as.logical()```To flag the sign changes, use `rle()` Run Length Encoding. If the length of the `lengths` output is greater than 1, then there was a sign change. ```{r}rle(exa.diffmat[6,]>0)rle(exa.diffmat[6,]>0)$lengthslength(rle(exa.diffmat[6,]>0)$lengths)>1```Only run this on the rows that haven't already been flagged as unsafe```{r}for(i in1:NROW(exa.diffmat)){if(unsafe[i] ==FALSE){ unsafe[i] <-length(rle(exa.diffmat[i,]>0)$lengths)>1 }}unsafe!unsafe```number of safe reports```{r}sum(!unsafe)```Matches. ## Part 2> The same rules apply as before, except if removing a single level from an unsafe report would make it safe, the report instead counts as safe.I was stumped, so I'm trying a tactic idea from [Angel Martinez](https://github.com/Angelmartinez-20/Advent_of_Code/tree/main). Check between each level for whether or not it was a safe transition. Then count the number of unsafe levels per row. ```{r, eval=FALSE}angels.if_safe.fun <- function(i, j){ diff <- abs(exa[i,j] - exa[i,j-1]) flip <- (sign(exa[i,j]) != sign(exa[i,j-1])) status <- dplyr::case_when(diff == 0 ~ "unsafe", diff > 3 ~ "unsafe", flip == TRUE ~ "unsafe", .default = "safe") return(status)}angels.if_safe.fun(3, 5)# set an indicator of the number of unsafe levelsis.safe <- rep("safe", NROW(exa))for(i in 1:NROW(exa)){ J <- length(na.omit(exa[i,])) # number of non-NA entries err <- 0 for(j in 2:J){ # run the function to check for an unsafe level. if(angels.if_safe.fun(i, j) == "unsafe"){ # if j is unsafe, and not at J, delete it and put j+1 where j is at exa[i, j:J-1] <- exa[i, j+1:J] exa[i, J] <- NA err <- 1 } # re-run safe check function on the same j. If another error then flag as unsafe if(angels.if_safe.fun(i, j) == "unsafe" & err==1){ is.safe[i] <- "unsafe" } }}(safe.if.remove.1 <- n.unsafe<2)```Yea.. this isn't working b/c when j=J, there is no j+1. ##### Session info {.appendix}<details><summary>Toggle</summary>```{r}#| echo: falsesessioninfo::session_info(pkgs ="attached")```</details>