summaryrefslogtreecommitdiffstats
path: root/src/day22.exs
blob: 418631f37e3a8db55664b34aec8ee4f80f34c3d2 (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
defmodule Day22 do
  def part1(data, n \\ 2000) do
    data
    |> String.split("\n", trim: true)
    |> Enum.map(&String.to_integer/1)
    |> Enum.map(&(secret_numbers(&1, n)))
    |> Enum.map(&hd/1)
    |> Enum.sum()
  end

  defp secret_numbers(v, n) do
    for _ <- 1..n, reduce: [v] do
      v ->
        [(v |> hd() |> change_value(6) |> change_value(-5) |> change_value(11)) | v]
    end
  end

  defp change_value(v, shift) do
    v
    |> Bitwise.bsl(shift)
    |> Bitwise.bxor(v)
    |> Integer.mod(16777216)
  end

  def part2(data, n \\ 2000) do
    data
    |> String.split("\n", trim: true)
    |> Enum.map(&String.to_integer/1)
    |> Enum.map(&(secret_numbers(&1, n)))
    |> Enum.map(&(sequences(&1, 10)))
    |> Enum.reduce(%{}, fn s, acc ->
      Map.merge(acc, s, fn _k, v1, v2 -> v1 + v2 end)
    end)
    |> Map.values()
    |> Enum.max()
  end

  defp sequences(vs, m) do
    vs
    |> Enum.map(&Integer.mod(&1, m))
    |> Enum.chunk_every(5, 1, :discard) # performance here could be improved
    |> Enum.map(fn chunk -> {diffs(chunk), hd(chunk)} end)
    |> Map.new()
  end

  defp diffs(vs) do
    vs
    |> Enum.chunk_every(2, 1, :discard)
    |> Enum.map(fn [a, b] -> a - b end)
    |> List.to_tuple()
  end
end

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

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

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