From 11d5af98db53849cd551a8eea5fd4c450a0e3b9d Mon Sep 17 00:00:00 2001 From: Ruidy Date: Wed, 4 Dec 2024 17:48:19 +0100 Subject: [PATCH] fix: test are passing for part 2 --- lib/advent_code2024/solutions/day04/day4.ex | 110 +++++++++++--------- 1 file changed, 59 insertions(+), 51 deletions(-) diff --git a/lib/advent_code2024/solutions/day04/day4.ex b/lib/advent_code2024/solutions/day04/day4.ex index 39d1bee..d8a0ec2 100644 --- a/lib/advent_code2024/solutions/day04/day4.ex +++ b/lib/advent_code2024/solutions/day04/day4.ex @@ -62,7 +62,7 @@ defmodule AdventCode2024.Solutions.Day04 do end defp within_bounds?(grid, row, col) do - row >= 0 && row < length(grid) && col >= 0 && col < length(Enum.at(grid, 0)) + row >= 0 and row < length(grid) and col >= 0 and col < length(Enum.at(grid, 0)) end @doc """ @@ -85,68 +85,76 @@ defmodule AdventCode2024.Solutions.Day04 do def solve_part2(_input_file) do end - def count_x_mas(grid) when is_list(grid) do - # Convert string rows to character grid - char_grid = Enum.map(grid, &String.graphemes/1) - rows = length(char_grid) - cols = length(Enum.at(char_grid, 0)) + def count_x_mas(grid) do + processed_grid = Enum.map(grid, &String.graphemes/1) - # Check each possible center position - for row <- 1..(rows - 2), - col <- 1..(cols - 2), - reduce: 0 do - acc -> - if is_x_mas_pattern?(char_grid, {row, col}) do - acc + 1 + processed_grid + |> Enum.with_index() + |> Enum.reduce(0, fn {row, row_idx}, acc -> + row + |> Enum.with_index() + |> Enum.reduce(acc, fn {cell, col_idx}, acc_inner -> + if cell == "A" do # Start from the center A + acc_inner + check_x_patterns(processed_grid, {row_idx, col_idx}) else - acc + acc_inner end - end + end) + end) end - defp is_x_mas_pattern?(grid, {row, col}) do - # Get the center character first - center = get_char(grid, row, col) - if center != "A", do: false, - else: check_diagonals(grid, row, col) - end - - defp check_diagonals(grid, row, col) do - # Define the four diagonal directions from center - diagonals = [ - [{-1, -1}, {1, 1}], # top-left to bottom-right - [{-1, 1}, {1, -1}], # top-right to bottom-left - ] - - # For each diagonal pair, check if either forms a valid MAS pattern - Enum.any?(diagonals, fn [d1, d2] -> - chars1 = get_diagonal_chars(grid, row, col, d1) - chars2 = get_diagonal_chars(grid, row, col, d2) - - case {chars1, chars2} do - {[c1, "A", c3], [c4, "A", c6]} when not is_nil(c1) and not is_nil(c3) and not is_nil(c4) and not is_nil(c6) -> - (is_mas?(c1, c3) and is_mas?(c4, c6)) or - (is_mas?(c3, c1) and is_mas?(c6, c4)) - _ -> false + defp check_x_patterns(grid, {row, col}) do + # Check both 1-step and 2-step patterns + [1, 2] + |> Enum.reduce(0, fn step, acc -> + if check_x_pattern(grid, {row, col}, step) do + acc + 1 + else + acc end end) end - defp get_diagonal_chars(grid, row, col, {dr, dc}) do - [ - get_char(grid, row - dr, col - dc), - get_char(grid, row, col), - get_char(grid, row + dr, col + dc) - ] + defp check_x_pattern(grid, {row, col}, step) do + # Check if we can form an X pattern with MAS on both diagonals + check_diagonal(grid, {row, col}, {-step, -step}, {step, step}) and # top-left to bottom-right + check_diagonal(grid, {row, col}, {-step, step}, {step, -step}) # top-right to bottom-left end - defp get_char(grid, row, col) do - if within_bounds?(grid, row, col) do - Enum.at(Enum.at(grid, row), col) - end + defp check_diagonal(grid, {center_row, center_col}, {top_dr, top_dc}, {bottom_dr, bottom_dc}) do + # Check top part and bottom part + top_row = center_row + top_dr + top_col = center_col + top_dc + bottom_row = center_row + bottom_dr + bottom_col = center_col + bottom_dc + + within_bounds?(grid, top_row, top_col) and + within_bounds?(grid, bottom_row, bottom_col) and + ( + # Check normal orientation (M at top, S at bottom) + (Enum.at(Enum.at(grid, top_row), top_col) == "M" and + Enum.at(Enum.at(grid, bottom_row), bottom_col) == "S") or + # Check upside down orientation (S at top, M at bottom) + (Enum.at(Enum.at(grid, top_row), top_col) == "S" and + Enum.at(Enum.at(grid, bottom_row), bottom_col) == "M") + ) end - defp is_mas?(first, last) do - first == "M" and last == "S" + @doc """ + Reads a grid from a file and counts the occurrences of a word using `count_word/2`. + + ## Parameters + - file_path: The path to the file containing the grid. + - word: The word to be searched for. + + ## Returns + - The number of times the word is found in the grid. + """ + def count_word_from_file(file_path, word) do + file_path + |> File.stream!() + |> Enum.map(&String.trim/1) + |> Enum.map(&String.graphemes/1) + |> count_word_occurrences(word) end end