aboutsummaryrefslogtreecommitdiff
path: root/aoc2017-gleam/src/aoc_2017/day_8.gleam
blob: 2f9d0dc35bacc931244a7a19c12b382a809898c5 (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import gleam/dict
import gleam/int
import gleam/list
import gleam/option.{None, Some}
import gleam/string

const max_register = "__MAX"

pub type Instruction {
  Instruction(register: String, op: Operation, condition: Condition)
}

pub type Operation {
  Inc(by: Int)
  Dec(by: Int)
}

pub type Condition {
  Equal(register: String, value: Int)
  NotEqual(register: String, value: Int)
  LessThan(register: String, value: Int)
  GreaterThan(register: String, value: Int)
  LessThanOrEq(register: String, value: Int)
  GreaterThanOrEq(register: String, value: Int)
}

type Registers =
  dict.Dict(String, Int)

pub fn parse(input: String) {
  input
  |> string.split("\n")
  |> list.map(parse_instruction)
}

fn parse_instruction(str: String) -> Instruction {
  case string.split(str, " ") {
    [name, op, by, "if", cond_name, cond_type, cond_by] ->
      Instruction(name, to_op(op, by), to_cond(cond_name, cond_type, cond_by))
    _ -> panic as { "couldn't parse: " <> str }
  }
}

pub fn pt_1(input: List(Instruction)) {
  let registers = dict.new() |> dict.insert(max_register, 0)

  input
  |> list.fold(registers, next_instruction)
  |> dict.delete(max_register)
  |> dict.values()
  |> list.reduce(int.max)
}

fn next_instruction(regs: Registers, inst: Instruction) {
  case to_compare_fn(inst.condition)(fetch(inst.condition.register, regs)) {
    True -> {
      let updated_regs = dict.update(regs, inst.register, to_update_fn(inst.op))
      let assert Ok(max) = updated_regs |> dict.values |> list.reduce(int.max)
      dict.insert(updated_regs, max_register, max)
    }
    False -> regs
  }
}

pub fn pt_2(input: List(Instruction)) {
  let registers = dict.new() |> dict.insert(max_register, 0)

  input
  |> list.fold(registers, next_instruction)
  |> dict.get(max_register)
}

fn int(str: String) -> Int {
  let assert Ok(n) = int.parse(str)
  n
}

fn to_op(raw_op: String, raw_by: String) -> Operation {
  case raw_op {
    "inc" -> Inc(int(raw_by))
    "dec" -> Dec(int(raw_by))
    _ -> panic as { "bad op: " <> raw_op }
  }
}

fn to_cond(name: String, raw_type: String, raw_by: String) -> Condition {
  case raw_type {
    "==" -> Equal(name, int(raw_by))
    "!=" -> NotEqual(name, int(raw_by))
    ">" -> GreaterThan(name, int(raw_by))
    "<" -> LessThan(name, int(raw_by))
    ">=" -> GreaterThanOrEq(name, int(raw_by))
    "<=" -> LessThanOrEq(name, int(raw_by))
    _ -> panic as { "bad condition: " <> raw_type }
  }
}

fn to_compare_fn(condition: Condition) -> fn(Int) -> Bool {
  case condition {
    Equal(value: v, ..) -> fn(a) { a == v }
    NotEqual(value: v, ..) -> fn(a) { a != v }
    GreaterThan(value: v, ..) -> fn(a) { a > v }
    LessThan(value: v, ..) -> fn(a) { a < v }
    GreaterThanOrEq(value: v, ..) -> fn(a) { a >= v }
    LessThanOrEq(value: v, ..) -> fn(a) { a <= v }
  }
}

fn to_update_fn(op: Operation) {
  case op {
    Inc(n) -> fn(x) {
      case x {
        Some(i) -> i + n
        None -> n
      }
    }
    Dec(n) -> fn(x) {
      case x {
        Some(i) -> i - n
        None -> -n
      }
    }
  }
}

fn fetch(name: String, registers: Registers) -> Int {
  case dict.get(registers, name) {
    Ok(n) -> n
    Error(_) -> 0
  }
}