aboutsummaryrefslogtreecommitdiff
path: root/aoc2017-gleam/src/aoc_2017/day_16.gleam
blob: 9a5110e4ee8825b0c2c0215d35acf57f05a3575c (plain)
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
import gleam/dict.{type Dict}
import gleam/int
import gleam/list
import gleam/string

pub type DanceMove {
  Spin(moving: Int)
  Exchange(first: Int, second: Int)
  Partner(first: String, second: String)
}

const initial_lineup = "abcdefghijklmnop"

const dancer_count = 16

const end_state = 1_000_000_000

pub fn parse(input: String) {
  string.split(input, ",")
}

fn do_spin(dancers: List(String), moving: Int) {
  let #(front, back) = list.split(dancers, dancer_count - moving)
  list.append(back, front)
}

fn do_exchange(dancers: List(String), first: Int, second: Int) {
  let indexed = list.index_map(dancers, fn(d, i) { #(i, d) })

  let assert Ok(first_dancer) = list.key_find(indexed, first)
  let assert Ok(second_dancer) = list.key_find(indexed, second)

  indexed
  |> list.key_set(first, second_dancer)
  |> list.key_set(second, first_dancer)
  |> list.map(fn(tup) { tup.1 })
}

fn do_partner(dancers: List(String), first: String, second: String) {
  use dancer <- list.map(dancers)
  case dancer {
    d if d == first -> second
    d if d == second -> first
    d -> d
  }
}

pub fn pt_1(input: List(String)) {
  initial_lineup
  |> string.to_graphemes()
  |> next_move(input)
  |> string.concat()
}

fn next_move(dancers, raw_moves) {
  case raw_moves {
    [] -> dancers
    ["s" <> size, ..rest] -> dancers |> do_spin(int(size)) |> next_move(rest)
    ["x" <> swap, ..rest] -> {
      let assert Ok(#(first, second)) = string.split_once(swap, "/")
      dancers |> do_exchange(int(first), int(second)) |> next_move(rest)
    }
    ["p" <> swap, ..rest] -> {
      let assert Ok(#(first, second)) = string.split_once(swap, "/")
      dancers |> do_partner(first, second) |> next_move(rest)
    }
    _ -> panic as "bad dance move"
  }
}

pub fn pt_2(input: List(String)) {
  initial_lineup
  |> string.to_graphemes()
  |> find_cycle(caching_in: dict.new(), cycle: 0, dancing_to: input)
}

fn find_cycle(
  moving_to dance_position: List(String),
  caching_in cache: Dict(String, Int),
  cycle cycle: Int,
  dancing_to dance_moves: List(String),
) {
  let dance_hash = string.concat(dance_position)
  case dict.get(cache, dance_hash) {
    Ok(c) -> {
      let offset = end_state % { cycle - c } - c
      let assert [#(final, _)] =
        dict.filter(cache, fn(_, v) { v == offset }) |> dict.to_list()
      final
    }
    _err ->
      find_cycle(
        moving_to: next_move(dance_position, dance_moves),
        caching_in: dict.insert(cache, dance_hash, cycle),
        cycle: cycle + 1,
        dancing_to: dance_moves,
      )
  }
}

fn int(n) {
  let assert Ok(n) = int.parse(n)
  n
}