day01.gleam

 1//// Authored-by: Claude Opus 4.5 via Crush
 2
 3import gleam/int
 4import gleam/list
 5import gleam/result
 6import gleam/string
 7
 8pub type Rotation {
 9  Left(Int)
10  Right(Int)
11}
12
13fn parse_rotation(s: String) -> Result(Rotation, Nil) {
14  let s = string.trim(s)
15  case string.first(s) {
16    Ok("L") -> s |> string.drop_start(1) |> int.parse |> result.map(Left)
17    Ok("R") -> s |> string.drop_start(1) |> int.parse |> result.map(Right)
18    _ -> Error(Nil)
19  }
20}
21
22fn apply_rotation(position: Int, rotation: Rotation) -> Int {
23  let new_pos = case rotation {
24    Left(n) -> position - n
25    Right(n) -> position + n
26  }
27  // Wrap around 0-99 (100 positions)
28  { { new_pos % 100 } + 100 } % 100
29}
30
31pub fn part1(input: List(String)) -> Int {
32  let rotations = list.filter_map(input, parse_rotation)
33  let #(zero_count, _final_pos) =
34    list.fold(rotations, #(0, 50), fn(acc, rotation) {
35      let #(count, pos) = acc
36      let new_pos = apply_rotation(pos, rotation)
37      let new_count = case new_pos {
38        0 -> count + 1
39        _ -> count
40      }
41      #(new_count, new_pos)
42    })
43  zero_count
44}
45
46fn count_zeros_crossed(position: Int, rotation: Rotation) -> Int {
47  let #(steps, going_right) = case rotation {
48    Left(n) -> #(n, False)
49    Right(n) -> #(n, True)
50  }
51
52  case going_right {
53    True -> {
54      // Right: hit 0 when (position + k) % 100 == 0
55      let first_hit = { 100 - position } % 100
56      case first_hit {
57        0 -> steps / 100
58        f if f <= steps -> { steps - f } / 100 + 1
59        _ -> 0
60      }
61    }
62    False -> {
63      // Left: hit 0 when (position - k) % 100 == 0, i.e., k == position
64      case position {
65        0 -> steps / 100
66        p if p <= steps -> { steps - p } / 100 + 1
67        _ -> 0
68      }
69    }
70  }
71}
72
73pub fn part2(input: List(String)) -> Int {
74  let rotations = list.filter_map(input, parse_rotation)
75  let #(zero_count, _final_pos) =
76    list.fold(rotations, #(0, 50), fn(acc, rotation) {
77      let #(count, pos) = acc
78      let zeros = count_zeros_crossed(pos, rotation)
79      let new_pos = apply_rotation(pos, rotation)
80      #(count + zeros, new_pos)
81    })
82  zero_count
83}