DEV Community

Caleb Weeks
Caleb Weeks

Posted on

Advent of Code #8 (in Gleam)

I'm not particularly proud of my code for today's solution. I could definitely refactor it to reduce a lot of the duplication, but I have a lot of other things going on, so I'm just going to leave it as is. The only other thing I'll mention is that this problem would really benefit from generator functions, list comprehensions, or lazy evaluated ranges, but without those features, I resorted to recursion. I suppose this is an example of where my brain decided that recursion was easier to wrap my head around than fold_until. Anyway, here's the code:

import gleam/int
import gleam/io
import gleam/list
import gleam/string
import gleam/pair
import gleam/set.{type Set}
import gleam/option.{Some, None}
import gleam/dict.{type Dict}
import simplifile as file

const example = "
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  let assert 14 = part1(example)
  let assert 34 = part2(example)
  part1(input) |> int.to_string |> io.println
  part2(input) |> int.to_string |> io.println
}

type Bounds = #(Int, Int)
type Coord = #(Int, Int)
type Map = Dict(String, List(Coord))

fn parse_input(input: String) -> #(Bounds, Map) {
  input
  |> string.trim
  |> string.split("\n")
  |> list.index_fold(#(#(0, 0), dict.new()), fn(acc, line, x) {
    line
    |> string.to_graphemes
    |> list.index_fold(acc, fn(acc, char, y) {
      let #(_, map) = acc
      let map = 
        dict.upsert(map, char, fn(val) {
          case char, val {
            ".", _ -> []
            _, Some(positions) -> list.append(positions, [#(x, y)])
            _, None -> [#(x, y)]
          }
        })
      #(#(x, y), map)
    })
  })
}

fn in_bounds(bounds: Bounds, coord: Coord) -> Bool {
  let #(x, y) = coord
  let #(max_x, max_y) = bounds
  x >= 0 && x <= max_x && y >= 0 && y <= max_y
}

fn part1(input: String) -> Int {
  let #(bounds, map) = parse_input(input)
  map
  |> dict.fold(set.new(), fn(antinodes, _, positions) {
    positions
    |> list.combination_pairs
    |> list.fold(antinodes, fn(antinodes, pair) {
      let #(x1, y1) = pair.first(pair)
      let #(x2, y2) = pair.second(pair)
      let a1 = #(2 * x1 - x2, 2 * y1 - y2)
      let a2 = #(2 * x2 - x1, 2 * y2 - y1)
      case in_bounds(bounds, a1), in_bounds(bounds, a2) {
        True, True -> antinodes |> set.insert(a1) |> set.insert(a2)
        True, False -> antinodes |> set.insert(a1)
        False, True -> antinodes |> set.insert(a2)
        False, False -> antinodes
      }
    })
  })
  |> set.size
}

fn get_antinodes(bounds: Bounds, pair: #(Coord, Coord)) -> Set(Coord) {
  set.new()
  |> set.insert(pair.first(pair))
  |> set.insert(pair.second(pair))
  |> direction_1(bounds, pair)
  |> direction_2(bounds, pair)
}

fn direction_1(set: Set(Coord), bounds: Bounds, pair: #(Coord, Coord)) -> Set(Coord) {
  let #(x1, y1) = pair.first(pair)
  let #(x2, y2) = pair.second(pair)
  let d1 = #(2 * x1 - x2, 2 * y1 - y2)
  case in_bounds(bounds, d1) {
    True -> set |> set.insert(d1) |> direction_1(bounds, #(d1, #(x1, y1)))
    False -> set
  }
}

fn direction_2(set: Set(Coord), bounds: Bounds, pair: #(Coord, Coord)) -> Set(Coord) {
  let #(x1, y1) = pair.first(pair)
  let #(x2, y2) = pair.second(pair)
  let d2 = #(2 * x2 - x1, 2 * y2 - y1)
  case in_bounds(bounds, d2) {
    True -> set |> set.insert(d2) |> direction_2(bounds, #(#(x2, y2), d2))
    False -> set
  }
}

fn part2(input: String) -> Int {
  let #(bounds, map) = parse_input(input)
  map
  |> dict.fold(set.new(), fn(antinodes, _, positions) {
    positions
    |> list.combination_pairs
    |> list.fold(antinodes, fn(antinodes, pair) {
      set.union(antinodes, get_antinodes(bounds, pair))
    })
  })
  |> set.size
}
Enter fullscreen mode Exit fullscreen mode

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay