summaryrefslogtreecommitdiffstats
path: root/src/day04.exs
blob: 990e04fd83c4dbda1ec0b358e8aa17d480db0e2e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
defmodule Day04 do
  def part1(data, word \\ ~c"XMAS") do
    data = data
    |> String.split("\n", trim: true)
    |> Enum.map(&(:array.from_list(String.to_charlist(&1))))

    count_word(0, data, word) +
      count_word(0, data, Enum.reverse(word))
  end

  def count_word(acc, [], _) do
    acc
  end

  def count_word(acc, data, word) do
    count_word(acc + count_word_hd(data, word), tl(data), word)
  end

  def count_word_hd(data, word) do
    0..:array.size(hd(data))-1
    |> Enum.map(&(count_word_hd_col(data, word, &1)))
    |> Enum.sum()
  end

  def count_word_hd_col(data, word, col) do
    [check_word_hori(data, word, col),
     check_word_vert(data, word, col),
     check_word_diag_right(data, word, col),
     check_word_diag_left(data, word, col)]
    |> Enum.count(&(&1))
  end

  def check_word_hori(data, word, col) do
    check_word(data, word, col, &(&1), &(&1 + 1))
  end

  def check_word_vert(data, word, col) do
    check_word(data, word, col, &tl/1, &(&1))
  end

  def check_word_diag_right(data, word, col) do
    check_word(data, word, col, &tl/1, &(&1 + 1))
  end

  def check_word_diag_left(data, word, col) do
    check_word(data, word, col, &tl/1, &(&1 - 1))
  end

  def check_word(data, word, col, data_step_fn, col_step_fn) do
    cond do
      word == [] ->
        true
      data == [] or col < 0 or col >= :array.size(hd(data)) ->
        false
      hd(word) == :array.get(col, hd(data)) ->
        check_word(
          data_step_fn.(data),
          tl(word),
          col_step_fn.(col),
          data_step_fn,
          col_step_fn
        )
      true ->
        false
    end
  end

  def part2(data, word \\ ~c"MAS") do
    data = data
    |> String.split("\n", trim: true)
    |> Enum.map(&(:array.from_list(String.to_charlist(&1))))

    count_cross(0, data, word)
  end

  def count_cross(acc, [], _) do
    acc
  end

  def count_cross(acc, data, word) do
    count_cross(acc + count_cross_hd(data, word), tl(data), word)
  end

  def count_cross_hd(data, word) do
    0..:array.size(hd(data))-1
    |> Enum.count(&(check_cross_hd_col(data, word, &1)))
  end

  def check_cross_hd_col(data, word, col) do
    (check_word_diag_right(data, word, col) and
       check_word_diag_left(data, word, col+2)) or
      (check_word_diag_right(data, Enum.reverse(word), col) and
         check_word_diag_left(data, word, col+2)) or
      (check_word_diag_right(data, word, col) and
         check_word_diag_left(data, Enum.reverse(word), col+2)) or
      (check_word_diag_right(data, Enum.reverse(word), col) and
         check_word_diag_left(data, Enum.reverse(word), col+2))
  end
end

data = IO.read(:stdio, :eof)

{time1 , ans1} = :timer.tc(fn -> Day04.part1(data) end)
IO.puts("Time  : #{time1 / 1000000}")
IO.puts("Answer: #{ans1}")

{time2 , ans2} = :timer.tc(fn -> Day04.part2(data) end)
IO.puts("Time  : #{time2 / 1000000}")
IO.puts("Answer: #{ans2}")