bugsmith
- 21 Posts
- 38 Comments
bugsmith@programming.devOPMto
Programming@programming.dev•Adding Live Reload to a Static Site Generator Written in GoEnglish
1·6 months agoI’d say fsnotify is the least interesting part
bugsmith@programming.devMto
Programming@programming.dev•Programming.dev instance: Sponsors needed
2·7 months agoIt’s Lemmy.
bugsmith@programming.devMto
Programming@programming.dev•Programming.dev instance: Sponsors needed
1·7 months agodeleted by creator
bugsmith@programming.devMto
Programming@programming.dev•Why blocked instances are still visible
3·2 years agoThis should have been posted in programming.dev/c/meta. I’m leaving it up here as the question has been answered.
bugsmith@programming.devOPto
Advent Of Code@programming.dev•[2024 Day 06] Any Gleamings in the house?
1·2 years agoThanks - appreciate the feedback!
Gleam
Late as usual. This one challenged me. Functional programming is a lot of fun, but it’s kicking my ass.
import gleam/dict import gleam/io import gleam/list import gleam/option.{None, Some} import gleam/result import gleam/set.{type Set} import gleam/string import simplifile pub type Point = #(Int, Int) pub type Grid(a) = dict.Dict(Point, a) pub type Direction { North East South West } pub type Loops { DoesLoop DoesNotLoop } pub type Guard { Guard(position: Point, direction: Direction) } fn get_guard(grid: Grid(String)) -> Guard { let pos = dict.filter(grid, fn(_pos, char) { char == "^" }) let assert Ok(pos) = case dict.size(pos) { 1 -> list.first(dict.keys(pos)) 0 -> panic as "No guard found in input!" _ -> panic as "More than one guard found in input!" } Guard(pos, North) } fn move_guard(guard: Guard) -> Guard { let new_pos = case guard.direction { North -> #(-1, 0) East -> #(0, 1) South -> #(1, 0) West -> #(0, -1) } Guard( #(guard.position.0 + new_pos.0, guard.position.1 + new_pos.1), guard.direction, ) } fn turn_guard(guard: Guard) -> Guard { let new_dir = case guard.direction { North -> East East -> South South -> West West -> North } Guard(guard.position, new_dir) } fn get_obstacles(grid: Grid(String)) -> List(Point) { dict.filter(grid, fn(_pos, char) { char == "#" }) |> dict.keys() } fn recurse_grid( grid: Grid(String), guard: Guard, obstacles: List(#(Int, Int)), visited: Set(#(#(Int, Int), Direction)), ) -> #(Set(#(#(Int, Int), Direction)), Loops) { let new_guard = move_guard(guard) let position = new_guard.position let dir = new_guard.direction case dict.has_key(grid, position) { False -> #(visited, DoesNotLoop) True -> { case set.contains(visited, #(position, dir)) { True -> { #(visited, DoesLoop) } False -> { case list.contains(obstacles, position) { True -> recurse_grid(grid, turn_guard(guard), obstacles, visited) False -> recurse_grid( grid, new_guard, obstacles, set.insert(visited, #(position, dir)), ) } } } } } } fn get_grid_input(filename: String) -> Grid(String) { let lines = filename |> simplifile.read() |> result.unwrap("") |> string.trim() |> string.split("\n") use grid, row, row_idx <- list.index_fold(lines, dict.new()) use grid, col, col_idx <- list.index_fold(string.to_graphemes(row), grid) dict.insert(grid, #(row_idx, col_idx), col) } fn part_one( grid: Grid(String), ) -> #(#(Set(#(#(Int, Int), Direction)), Loops), Int) { let guard = get_guard(grid) let obstacles = get_obstacles(grid) let visited = set.new() |> set.insert(#(guard.position, guard.direction)) let visited = recurse_grid(grid, guard, obstacles, visited) let visited_without_dir = set.fold(visited.0, set.new(), fn(acc, x) { set.insert(acc, x.0) }) #(visited, visited_without_dir |> set.size()) } fn check_loop(grid: Grid(String), blocker: Point) -> Loops { let blocked_grid = dict.upsert(grid, blocker, fn(x) { case x { Some("^") -> "^" Some(_) -> "#" None -> "#" } }) let visited = part_one(blocked_grid).0 visited.1 } fn part_two(grid: Grid(String), visited: Set(#(#(Int, Int), Direction))) { let visited = set.fold(visited, set.new(), fn(acc, x) { set.insert(acc, x.0) }) use counter, position <- set.fold(visited, 0) case check_loop(grid, position) { DoesLoop -> counter + 1 DoesNotLoop -> counter } } pub fn main() { let input = "input.in" let p1 = input |> get_grid_input() |> part_one let visited = p1.0.0 io.debug(p1.1) input |> get_grid_input |> part_two(visited) |> io.debug() }
Gleam
Struggled with the second part as I am still very new to this very cool language, but got there after scrolling for some inspiration.
import gleam/int import gleam/io import gleam/list import gleam/regex import gleam/result import gleam/string import simplifile pub fn main() { let assert Ok(data) = simplifile.read("input.in") part_one(data) |> io.debug part_two(data) |> io.debug } fn part_one(data) { let assert Ok(multiplication_pattern) = regex.from_string("mul\\(\\d{1,3},\\d{1,3}\\)") let assert Ok(digit_pattern) = regex.from_string("\\d{1,3},\\d{1,3}") let multiplications = regex.scan(multiplication_pattern, data) |> list.flat_map(fn(reg) { regex.scan(digit_pattern, reg.content) |> list.map(fn(digits) { digits.content |> string.split(",") |> list.map(fn(x) { x |> int.parse |> result.unwrap(0) }) |> list.reduce(fn(a, b) { a * b }) |> result.unwrap(0) }) }) |> list.reduce(fn(a, b) { a + b }) |> result.unwrap(0) } fn part_two(data) { let data = "do()" <> string.replace(data, "\n", "") <> "don't()" let assert Ok(pattern) = regex.from_string("do\\(\\).*?don't\\(\\)") regex.scan(pattern, data) |> list.map(fn(input) { input.content |> part_one }) |> list.reduce(fn(a, b) { a + b }) }
Elixir
defmodule Day02 do defp part1(reports) do reports |> Enum.map(fn report -> levels = report |> String.split() |> Enum.map(&String.to_integer/1) cond do sequence_is_safe?(levels) -> :safe true -> :unsafe end end) |> Enum.count(fn x -> x == :safe end) end defp part2(reports) do reports |> Enum.map(fn report -> levels = report |> String.split() |> Enum.map(&String.to_integer/1) sequences = 0..(length(levels) - 1) |> Enum.map(fn i -> List.delete_at(levels, i) end) cond do sequence_is_safe?(levels) -> :safe Enum.any?(sequences, &sequence_is_safe?/1) -> :safe true -> :unsafe end end) |> Enum.count(fn x -> x == :safe end) end defp all_gaps_within_max_diff?(numbers) do numbers |> Enum.chunk_every(2, 1, :discard) |> Enum.all?(fn [a, b] -> abs(b - a) <= 3 end) end defp is_strictly_increasing?(numbers) do numbers |> Enum.chunk_every(2, 1, :discard) |> Enum.all?(fn [a, b] -> a < b end) end defp is_strictly_decreasing?(numbers) do numbers |> Enum.chunk_every(2, 1, :discard) |> Enum.all?(fn [a, b] -> a > b end) end defp sequence_is_safe?(numbers) do (is_strictly_increasing?(numbers) or is_strictly_decreasing?(numbers)) and all_gaps_within_max_diff?(numbers) end def run(data) do reports = data |> String.split("\n", trim: true) p1 = part1(reports) p2 = part2(reports) IO.puts(p1) IO.puts(p2) end end data = File.read!("input.in") Day02.run(data)
I’m late to the party, as usual. Damned timezones. This year I’m going to tackle with a small handful of languages, but primarily Elixir and Gleam. This is my first time trying this languages in earnest, so expect some terrible, inefficient and totally unidiomatic code!
Here’s day one:Elixir
part_one = File.read!("input.in") |> String.split("\n", trim: true) |> Enum.map(fn line -> line |> String.split() |> Enum.map(&String.to_integer/1) end) |> Enum.reduce({[], []}, fn [first, second], {list1, list2} -> {[first | list1], [second | list2]} end) |> then(fn {list1, list2} -> {Enum.sort(list1), Enum.sort(list2)} end) |> then(fn {list1, list2} -> Enum.zip(list1, list2) |> Enum.map(fn {x, y} -> abs(x - y) end) end) |> Enum.sum() part_two = File.read!("input.in") |> String.split("\n", trim: true) |> Enum.map(fn line -> line |> String.split() |> Enum.map(&String.to_integer/1) end) |> Enum.reduce({[], []}, fn [first, second], {list1, list2} -> {[first | list1], [second | list2]} end) |> then(fn {list1, list2} -> Enum.map(list1, fn line -> line * Enum.count(list2, fn x -> x === line end) end) |> Enum.sum() end) IO.inspect(part_one) IO.inspect(part_two)
bugsmith@programming.devOPMto
Programming@programming.dev•Good software development habits
1·2 years agoTotally agree. Like most “rules”, it just needs treating with nuance and context.
I really like Nushell. I would not run it as a daily driver currently, as it mostly doesn’t win me over from Fish, feature-wise, but I love having it available for anything CLI date pipeline work I need to do.
Love this. Always interesting to see novel ways of querying data in the terminal, and I agree that jq’s syntax is difficult to remember.
I actually prefer nu(shell) for this though. On the lobste.rs thread for this blog, a user shared this:
| get license.key -i | uniq --count | rename license This outputs the following: ╭───┬──────────────┬───────╮ │ # │ license │ count │ ├───┼──────────────┼───────┤ │ 0 │ bsd-3-clause │ 23 │ │ 1 │ apache-2.0 │ 5 │ │ 2 │ │ 2 │ ╰───┴──────────────┴───────╯
Thanks. I didn’t know about these advanced libraries, and had not heard of C++ modules either. Appreciate the explanation.
I don’t code in C++ (although I’m somewhat familiar with the syntax). My understanding is the header files should only contain prototypes / signatures, not actual implementations. But that doesn’t seem to be the case here. Have I misunderstood, or is that part of the joke?
Honestly, for any large scale project in Python, Pydantic makes it bearable. We use Python heavily at work (and I’d argue we shouldn’t be for the projects we’re working on…), and Pydantic is the one library we’re using that I wouldn’t be without. Precisely because it allows us to inject some of these static typing concepts and keeps us honest, and our code understandable.
Yes! The concepts are intertwined. I think the key take away, for me, is to lean heavily into your type system and allow that to do some of the heavy lifting. Accept that something like a
usernameis not a string, but a subtype of a string (this has to be true if any validation is required, otherwise you’d just accept any valid string).
It’s one of my favourites. Something I revisit every couple of years.
bugsmith@programming.devOPMto
Programming@programming.dev•The Hacker News Top 40 books of 2023
1·3 years agoThat one has been on my list for a while. Are you finding yourself able to easily apply what is taught to your day-to-day?
bugsmith@programming.devto
Gaming@beehaw.org•Sonic X Shadow Generations - Announce Trailer
3·3 years agoDifferent strokes for different folks, I guess. I enjoyed Heroes for what it was.
I agree that Sonic Battle was one of, if not the best entries for character building. And SB is, in fact, my all-time favourite Sonic game. Breaks me that I may never see a sequel / reboot, and get to relive Emerl’s story.












I’m glad you enjoyed it. I appreciate the kind words.
Feel free to do so!