bugsmith

joined 2 years ago
MODERATOR OF
[–] bugsmith@programming.dev 1 points 2 weeks ago

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

[–] bugsmith@programming.dev 1 points 1 month ago

I'd say fsnotify is the least interesting part

[–] bugsmith@programming.dev 50 points 2 months ago (3 children)

Spectacle OCR is fantastic news. That is really going to simplify one of my current workflows.

[–] bugsmith@programming.dev 2 points 2 months ago
[–] bugsmith@programming.dev 5 points 8 months ago

I have used and enjoyed lawnchair for the past year. It's quite minimal and I've found it very stable.

 

I'm running behind as usual. I'm still rather new to Gleam and would love some feedback on how I handled the Day 06 puzzle:

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()
}
[–] bugsmith@programming.dev 1 points 2 years ago

Another vote here for Fastmail. I also like Posteo, Mailbox and mxroute, but these are not as fully featured - which may be perfect for you if you're after email only. What I really like about Fastmail is that on top of being a customer-focused business (rather than a customer is the product business), they offer a really snappy web interface with excellent search - and they are extremely compliant with email standards, building everything on JMAP.

I do not like Proton or Tutanota. I have used both, including using Proton as my main email account for the past two years. I do believe they are probably the best when it comes to encryption and privacy standards, but for me it's at far too much cost. Encrypted email is almost pointless - the moment you email someone who isn't using a Proton (or PGP encryption), then the encryption is lost. Or even if they just forward an email to someone outside your chain. I would argue that if you need to send a message to someone with enough sensitivity to require this level of encryption, email is the wrong choice of protocol.

For all that Proton offer, it results in broken email standard compliance, awful search capability and reliance on bridge software or being limited to their WebUI and apps. And it's a shame, because I really like the company and their mission.

[–] bugsmith@programming.dev 1 points 2 years ago

I particularly enjoyed a recent company meeting that spent considerable time talking about the importance of flow state. It had an awkward pregnant pause when someone (usually very quiet) unmuted to ask, "is the policy to increase the number of days we must spend in our open-plan office kind of undermining this?". Literally all of our directors just shifted on their seats hoping another would answer that.

Eventually, HR director stated "Not at all, that's what headphones are for!"

Which was particularly delightful, as our tech director had only 20 minutes before stated how he would like to discourage people sitting in the office in silos with their headphones on.

 

Sony has released a new PlayStation 5 controller called the Access Controller, which is designed to be customizable for disabled gamers. It allows users to configure different buttons, triggers and sticks to suit their individual needs. The kit aims to help people who struggle with thumbsticks, pressing buttons, or holding a controller. Feedback from disabled gamers was incorporated into the design. While a step forward, some find issues like the lack of a right stick limits gameplay in certain genres. Overall though, the product and others like Microsoft's Adaptive Controller are helping make gaming more inclusive for disabled players.

view more: next ›