import gleam/int import gleam/list import gleam/result import gleam/string const starting_position = 50 pub type Movement { Left(Int) Right(Int) } pub fn part1(input: List(String)) -> Int { let movements = input |> list.filter_map(parse_movement) let #(zero_count, _final_pos) = list.fold(movements, #(0, starting_position), fn(acc, movement) { let #(count, pos) = acc let new_pos = apply_movement(pos, movement) let new_count = case new_pos { 0 -> count + 1 _ -> count } #(new_count, new_pos) }) zero_count } pub fn part2(input: List(String)) -> Int { let movements = input |> list.filter_map(parse_movement) let #(zero_count, _final_pos) = list.fold(movements, #(0, starting_position), fn(acc, movement) { let #(count, pos) = acc let new_pos = apply_movement(pos, movement) let zeroes = zeroes_hit(pos, movement) #(count + zeroes, new_pos) }) zero_count } pub fn parse_movement(input: String) -> Result(Movement, Nil) { case string.first(input) { Ok("L") -> input |> string.drop_start(1) |> int.parse |> result.map(Left) Ok("R") -> input |> string.drop_start(1) |> int.parse |> result.map(Right) _ -> Error(Nil) } } fn apply_movement(position: Int, movement: Movement) -> Int { let new_pos = case movement { Left(n) -> position - n Right(n) -> position + n } // Wrap around 0-99 (100 positions) { { new_pos % 100 } + 100 } % 100 } fn zeroes_hit(position: Int, movement: Movement) -> Int { let #(steps, going_right) = case movement { Left(n) -> #(n, False) Right(n) -> #(n, True) } case going_right { True -> { // Right: hit 0 when (position + k) % 100 == 0 let first_hit = { 100 - position } % 100 case first_hit { 0 -> steps / 100 f if f <= steps -> { steps - f } / 100 + 1 _ -> 0 } } False -> { // Left: hit 0 when (position - k) % 100 == 0, i.e., k == position case position { 0 -> steps / 100 p if p <= steps -> { steps - p } / 100 + 1 _ -> 0 } } } }