diff --git a/docs/2022/R/day01.html b/docs/2022/R/day01.html index 2ba8b0a..c02a861 100644 --- a/docs/2022/R/day01.html +++ b/docs/2022/R/day01.html @@ -67,7 +67,7 @@ - + @@ -209,6 +209,18 @@

Day 1

Day 11 + + + @@ -823,8 +835,8 @@

Part 2

diff --git a/docs/2024/R/day12.html b/docs/2024/R/day12.html index 2861d8d..289367d 100644 --- a/docs/2024/R/day12.html +++ b/docs/2024/R/day12.html @@ -66,7 +66,7 @@ - + @@ -215,6 +215,12 @@

Day 12

Day 12 + + @@ -908,8 +914,8 @@

Part 2

diff --git a/docs/index.html b/docs/index.html index 1532e7e..697fced 100644 --- a/docs/index.html +++ b/docs/index.html @@ -179,6 +179,12 @@

Advent of Code: Worked Solutions

Day 12 + + diff --git a/docs/search.json b/docs/search.json index 5a7c58f..0d77f9c 100644 --- a/docs/search.json +++ b/docs/search.json @@ -138,6 +138,50 @@ "Day 6" ] }, + { + "objectID": "2024/R/day13.html", + "href": "2024/R/day13.html", + "title": "Day 13", + "section": "", + "text": "# Libraries\nlibrary(tidyverse)\nlibrary(unglue)\n\n# Read input from file\ninput <- read_lines(\"../input/day13.txt\", skip_empty_rows = TRUE)", + "crumbs": [ + "2024", + "Day 13" + ] + }, + { + "objectID": "2024/R/day13.html#setup", + "href": "2024/R/day13.html#setup", + "title": "Day 13", + "section": "", + "text": "# Libraries\nlibrary(tidyverse)\nlibrary(unglue)\n\n# Read input from file\ninput <- read_lines(\"../input/day13.txt\", skip_empty_rows = TRUE)", + "crumbs": [ + "2024", + "Day 13" + ] + }, + { + "objectID": "2024/R/day13.html#part-1", + "href": "2024/R/day13.html#part-1", + "title": "Day 13", + "section": "Part 1", + "text": "Part 1\nExtract numerical values from input text:\n\ndf <- input |> \n unglue_data(c(\n \"Button {button}: X+{x=\\\\d+}, Y+{y=\\\\d+}\",\n \"{button}: X={x=\\\\d+}, Y={y=\\\\d+}\"\n )) |> \n mutate(\n machine_id = floor((row_number() - 1) / 3),\n across(c(x, y), parse_number),\n .before = everything()\n ) |> \n pivot_wider(names_from = button, values_from = c(x, y))\n\nDefine a function to convert numeric equation input and output token counts:\n\ncompute_tokens <- function(df) {\n \n # Convert each machine's properties into a system of equations and solve.\n soln <- df |> \n nest(coeff = c(x_A, x_B, y_A, y_B)) |> \n nest(intercept = c(x_Prize, y_Prize)) |> \n mutate(\n coeff = map(coeff, ~ matrix(as.numeric(.x), nrow = 2, byrow = TRUE)),\n intercept = map(intercept, as.numeric),\n soln = map2(\n coeff, \n intercept, \n ~ solve(.x, .y) |> \n set_names(\"A\", \"B\") |> \n as_tibble_row()\n )\n ) |> \n unnest(soln) |> \n select(machine_id, A, B)\n \n \n # Check that the solution is two whole numbers, then sum the token cost\n soln |> \n mutate(\n across(\n c(A, B), \n ~ near(.x, abs(round(.x)), tol = 0.001), \n .names = \"{.col}_valid\"\n ),\n win = A_valid & B_valid,\n tokens = if_else(win, 3 * A + B, 0)\n ) |> \n pull(tokens) |> \n sum()\n}\n\nRun function on puzzle input:\n\ncompute_tokens(df)\n\n[1] 31623", + "crumbs": [ + "2024", + "Day 13" + ] + }, + { + "objectID": "2024/R/day13.html#part-2", + "href": "2024/R/day13.html#part-2", + "title": "Day 13", + "section": "Part 2", + "text": "Part 2\nAdd 10000000000000 to each prize intercept and re-compute:\n\ndf |> \n mutate(across(c(x_Prize, y_Prize), ~ .x + 10000000000000)) |> \n compute_tokens() |> \n format(scientific = FALSE)\n\n[1] \"93209116744825\"", + "crumbs": [ + "2024", + "Day 13" + ] + }, { "objectID": "2024/R/day11.html", "href": "2024/R/day11.html", @@ -908,6 +952,50 @@ "Day 7" ] }, + { + "objectID": "2024/R/day12.html", + "href": "2024/R/day12.html", + "title": "Day 12", + "section": "", + "text": "# Libraries\nlibrary(tidyverse)\nlibrary(igraph)\n\n# Read input from file into a data frame\ninput <- read_table(\"../input/day12.txt\", col_names = \"chr\") |> \n mutate(\n row = row_number(),\n chr = str_split(chr, \"\")\n ) |> \n unnest(chr) |> \n mutate(col = row_number(), .by = row) |> \n mutate(idx = row_number(), .before = everything())", + "crumbs": [ + "2024", + "Day 12" + ] + }, + { + "objectID": "2024/R/day12.html#setup", + "href": "2024/R/day12.html#setup", + "title": "Day 12", + "section": "", + "text": "# Libraries\nlibrary(tidyverse)\nlibrary(igraph)\n\n# Read input from file into a data frame\ninput <- read_table(\"../input/day12.txt\", col_names = \"chr\") |> \n mutate(\n row = row_number(),\n chr = str_split(chr, \"\")\n ) |> \n unnest(chr) |> \n mutate(col = row_number(), .by = row) |> \n mutate(idx = row_number(), .before = everything())", + "crumbs": [ + "2024", + "Day 12" + ] + }, + { + "objectID": "2024/R/day12.html#part-1", + "href": "2024/R/day12.html#part-1", + "title": "Day 12", + "section": "Part 1", + "text": "Part 1\nFormat the input as a graph, with edges connecting neighbors of the same type:\n\n# Flag neighboring characters of the same value that border one other\nedges_wide <- input |> \n mutate(v = case_when(row + 1 == lead(row) ~ lead(idx)), .by = c(chr, col)) |> \n mutate(h = case_when(col + 1 == lead(col) ~ lead(idx)), .by = c(chr, row))\n\nedges_long <- edges_wide |> \n pivot_longer(\n c(v, h), \n names_to = NULL, \n values_to = \"target\", \n values_drop_na = TRUE\n )\n\n# Format neighbors as a list of edges and add to add a graph\ng <- edges_long |> \n transmute(\n edge_id = row_number(),\n src = idx, \n target\n ) |> \n pivot_longer(c(src, target)) |> \n arrange(edge_id, value) |> \n pull(value) |> \n make_graph(n = nrow(input), directed = FALSE)\n\nV(g)$name <- 1:nrow(input)\n\n# Separate out the resulting graph into sub-graphs of innerconnected regions\ndg <- decompose(g)\n\nCompute the perimeter, area, and cost of each subgraph then sum the total:\n\ndg |> \n map_int(\\(subgraph) {\n perim <- sum(4 - degree(subgraph))\n area <- gorder(subgraph)\n perim * area\n }) |> \n sum()\n\n[1] 1433460", + "crumbs": [ + "2024", + "Day 12" + ] + }, + { + "objectID": "2024/R/day12.html#part-2", + "href": "2024/R/day12.html#part-2", + "title": "Day 12", + "section": "Part 2", + "text": "Part 2\nUsed a hint from reddit: the number of corners is equal to the number of sides.\nA plot can have a convex corner or a concave corner.\n\nA cell has a convex corner for each pair of adjacent borders\nA cell has a concave corner if it has two adjacent cells of its same group, but its diagonal cell between the two has a different group.\n\n\n# Get original row/column input and join on the group output from the graph\ngroups <- left_join(\n input,\n imap_dfr(dg, \\(g, grp_idx) tibble(grp = grp_idx, idx = V(g)$name)),\n join_by(idx)\n) |> \n select(idx, grp, row, col)\n\n# For each of a cell's neighbors, flag if they're in the same group\nneighbors <- groups |> \n # Get group number of each adjacent cell (N/S/E/W)\n left_join(transmute(groups, n = grp, row = row + 1, col), join_by(row, col)) |> \n left_join(transmute(groups, w = grp, col = col + 1, row), join_by(row, col)) |> \n left_join(transmute(groups, s = grp, row = row - 1, col), join_by(row, col)) |> \n left_join(transmute(groups, e = grp, col = col - 1, row), join_by(row, col)) |> \n # Get group number of each diagonal cell (NW/NE/SW/SE)\n left_join(transmute(groups, nw = grp, row = row + 1, col = col + 1), join_by(row, col)) |> \n left_join(transmute(groups, ne = grp, row = row + 1, col = col - 1), join_by(row, col)) |> \n left_join(transmute(groups, sw = grp, row = row - 1, col = col + 1), join_by(row, col)) |> \n left_join(transmute(groups, se = grp, row = row - 1, col = col - 1), join_by(row, col)) |> \n select(-c(row, col)) |> \n # Compare group numbers of adjacent/diagonal cells to the current cell\n mutate(across(c(n, w, s, e, nw, ne, sw, se), ~ replace_na(.x == grp, FALSE)))\n\n# Compute total number of concave/convex corners for each cell\ncorners <- neighbors |> \n mutate(\n convex = (!n & !w) + (!s & !w) + (!s & !e) + (!n & !e),\n concave = (n & w & !nw) + (s & w & !sw) + (s & e & !se) + (n & e & !ne)\n )\n\nTotal the number of corners per group and multiply by the group’s area to get the total cost:\n\ncorners |> \n summarize(\n area = n(),\n num_sides = sum(convex + concave), \n .by = grp\n ) |> \n mutate(cost = area * num_sides) |> \n pull(cost) |> \n sum()\n\n[1] 855082", + "crumbs": [ + "2024", + "Day 12" + ] + }, { "objectID": "2024/R/day02.html", "href": "2024/R/day02.html", @@ -1039,93 +1127,5 @@ "2024", "Day 9" ] - }, - { - "objectID": "2024/R/day12.html", - "href": "2024/R/day12.html", - "title": "Day 12", - "section": "", - "text": "# Libraries\nlibrary(tidyverse)\nlibrary(igraph)\n\n# Read input from file into a data frame\ninput <- read_table(\"../input/day12.txt\", col_names = \"chr\") |> \n mutate(\n row = row_number(),\n chr = str_split(chr, \"\")\n ) |> \n unnest(chr) |> \n mutate(col = row_number(), .by = row) |> \n mutate(idx = row_number(), .before = everything())", - "crumbs": [ - "2024", - "Day 12" - ] - }, - { - "objectID": "2024/R/day12.html#setup", - "href": "2024/R/day12.html#setup", - "title": "Day 12", - "section": "", - "text": "# Libraries\nlibrary(tidyverse)\nlibrary(igraph)\n\n# Read input from file into a data frame\ninput <- read_table(\"../input/day12.txt\", col_names = \"chr\") |> \n mutate(\n row = row_number(),\n chr = str_split(chr, \"\")\n ) |> \n unnest(chr) |> \n mutate(col = row_number(), .by = row) |> \n mutate(idx = row_number(), .before = everything())", - "crumbs": [ - "2024", - "Day 12" - ] - }, - { - "objectID": "2024/R/day12.html#part-1", - "href": "2024/R/day12.html#part-1", - "title": "Day 12", - "section": "Part 1", - "text": "Part 1\nFormat the input as a graph, with edges connecting neighbors of the same type:\n\n# Flag neighboring characters of the same value that border one other\nedges_wide <- input |> \n mutate(v = case_when(row + 1 == lead(row) ~ lead(idx)), .by = c(chr, col)) |> \n mutate(h = case_when(col + 1 == lead(col) ~ lead(idx)), .by = c(chr, row))\n\nedges_long <- edges_wide |> \n pivot_longer(\n c(v, h), \n names_to = NULL, \n values_to = \"target\", \n values_drop_na = TRUE\n )\n\n# Format neighbors as a list of edges and add to add a graph\ng <- edges_long |> \n transmute(\n edge_id = row_number(),\n src = idx, \n target\n ) |> \n pivot_longer(c(src, target)) |> \n arrange(edge_id, value) |> \n pull(value) |> \n make_graph(n = nrow(input), directed = FALSE)\n\nV(g)$name <- 1:nrow(input)\n\n# Separate out the resulting graph into sub-graphs of innerconnected regions\ndg <- decompose(g)\n\nCompute the perimeter, area, and cost of each subgraph then sum the total:\n\ndg |> \n map_int(\\(subgraph) {\n perim <- sum(4 - degree(subgraph))\n area <- gorder(subgraph)\n perim * area\n }) |> \n sum()\n\n[1] 1433460", - "crumbs": [ - "2024", - "Day 12" - ] - }, - { - "objectID": "2024/R/day12.html#part-2", - "href": "2024/R/day12.html#part-2", - "title": "Day 12", - "section": "Part 2", - "text": "Part 2\nUsed a hint from reddit: the number of corners is equal to the number of sides.\nA plot can have a convex corner or a concave corner.\n\nA cell has a convex corner for each pair of adjacent borders\nA cell has a concave corner if it has two adjacent cells of its same group, but its diagonal cell between the two has a different group.\n\n\n# Get original row/column input and join on the group output from the graph\ngroups <- left_join(\n input,\n imap_dfr(dg, \\(g, grp_idx) tibble(grp = grp_idx, idx = V(g)$name)),\n join_by(idx)\n) |> \n select(idx, grp, row, col)\n\n# For each of a cell's neighbors, flag if they're in the same group\nneighbors <- groups |> \n # Get group number of each adjacent cell (N/S/E/W)\n left_join(transmute(groups, n = grp, row = row + 1, col), join_by(row, col)) |> \n left_join(transmute(groups, w = grp, col = col + 1, row), join_by(row, col)) |> \n left_join(transmute(groups, s = grp, row = row - 1, col), join_by(row, col)) |> \n left_join(transmute(groups, e = grp, col = col - 1, row), join_by(row, col)) |> \n # Get group number of each diagonal cell (NW/NE/SW/SE)\n left_join(transmute(groups, nw = grp, row = row + 1, col = col + 1), join_by(row, col)) |> \n left_join(transmute(groups, ne = grp, row = row + 1, col = col - 1), join_by(row, col)) |> \n left_join(transmute(groups, sw = grp, row = row - 1, col = col + 1), join_by(row, col)) |> \n left_join(transmute(groups, se = grp, row = row - 1, col = col - 1), join_by(row, col)) |> \n select(-c(row, col)) |> \n # Compare group numbers of adjacent/diagonal cells to the current cell\n mutate(across(c(n, w, s, e, nw, ne, sw, se), ~ replace_na(.x == grp, FALSE)))\n\n# Compute total number of concave/convex corners for each cell\ncorners <- neighbors |> \n mutate(\n convex = (!n & !w) + (!s & !w) + (!s & !e) + (!n & !e),\n concave = (n & w & !nw) + (s & w & !sw) + (s & e & !se) + (n & e & !ne)\n )\n\nTotal the number of corners per group and multiply by the group’s area to get the total cost:\n\ncorners |> \n summarize(\n area = n(),\n num_sides = sum(convex + concave), \n .by = grp\n ) |> \n mutate(cost = area * num_sides) |> \n pull(cost) |> \n sum()\n\n[1] 855082", - "crumbs": [ - "2024", - "Day 12" - ] - }, - { - "objectID": "2024/R/day13.html", - "href": "2024/R/day13.html", - "title": "Day 13", - "section": "", - "text": "# Libraries\nlibrary(tidyverse)\nlibrary(unglue)\n\n# Read input from file\ninput <- read_lines(\"../input/day13.txt\", skip_empty_rows = TRUE)", - "crumbs": [ - "2024", - "Day 13" - ] - }, - { - "objectID": "2024/R/day13.html#setup", - "href": "2024/R/day13.html#setup", - "title": "Day 13", - "section": "", - "text": "# Libraries\nlibrary(tidyverse)\nlibrary(unglue)\n\n# Read input from file\ninput <- read_lines(\"../input/day13.txt\", skip_empty_rows = TRUE)", - "crumbs": [ - "2024", - "Day 13" - ] - }, - { - "objectID": "2024/R/day13.html#part-1", - "href": "2024/R/day13.html#part-1", - "title": "Day 13", - "section": "Part 1", - "text": "Part 1\nExtract numerical values from input text:\n\ndf <- input |> \n unglue_data(c(\n \"Button {button}: X+{x=\\\\d+}, Y+{y=\\\\d+}\",\n \"{button}: X={x=\\\\d+}, Y={y=\\\\d+}\"\n )) |> \n mutate(\n machine_id = floor((row_number() - 1) / 3),\n across(c(x, y), parse_number),\n .before = everything()\n ) |> \n pivot_wider(names_from = button, values_from = c(x, y))\n\nDefine a function to convert numeric equation input and output token counts:\n\ncompute_tokens <- function(df) {\n \n # Convert each machine's properties into a system of equations and solve.\n soln <- df |> \n nest(coeff = c(x_A, x_B, y_A, y_B)) |> \n nest(intercept = c(x_Prize, y_Prize)) |> \n mutate(\n coeff = map(coeff, ~ matrix(as.numeric(.x), nrow = 2, byrow = TRUE)),\n intercept = map(intercept, as.numeric),\n soln = map2(\n coeff, \n intercept, \n ~ solve(.x, .y) |> \n set_names(\"A\", \"B\") |> \n as_tibble_row()\n )\n ) |> \n unnest(soln) |> \n select(machine_id, A, B)\n \n \n # Check that the solution is two whole numbers, then sum the token cost\n soln |> \n mutate(\n across(\n c(A, B), \n ~ near(.x, abs(round(.x)), tol = 0.001), \n .names = \"{.col}_valid\"\n ),\n win = A_valid & B_valid,\n tokens = if_else(win, 3 * A + B, 0)\n ) |> \n pull(tokens) |> \n sum()\n}\n\nRun function on puzzle input:\n\ncompute_tokens(df)\n\n[1] 31623", - "crumbs": [ - "2024", - "Day 13" - ] - }, - { - "objectID": "2024/R/day13.html#part-2", - "href": "2024/R/day13.html#part-2", - "title": "Day 13", - "section": "Part 2", - "text": "Part 2\nAdd 10000000000000 to each prize intercept and re-compute:\n\ndf |> \n mutate(across(c(x_Prize, y_Prize), ~ .x + 10000000000000)) |> \n compute_tokens() |> \n format(scientific = FALSE)\n\n[1] \"93209116744825\"", - "crumbs": [ - "2024", - "Day 13" - ] } ] \ No newline at end of file