Crespyl's recent activity

  1. Comment on KDE Plasma 6 is (mega) released in ~tech

    Crespyl
    Link Parent
    Thanks for all the hard work! I've been a KDE user since Xandros was a thing, and it's been a great experience, thanks to people like you making it possible!

    Thanks for all the hard work! I've been a KDE user since Xandros was a thing, and it's been a great experience, thanks to people like you making it possible!

  2. Comment on Collapse comments? in ~tildes

    Crespyl
    Link
    On a slight tangent, having a Hacker News-style "next" button to skip to the next sibling comment might be a helpful way to navigate precisely past long/already read comments.

    On a slight tangent, having a Hacker News-style "next" button to skip to the next sibling comment might be a helpful way to navigate precisely past long/already read comments.

    8 votes
  3. Comment on Monday, April 8, 2024 total solar eclipse: where and when in ~space

    Crespyl
    Link Parent
    That's a nice tool! I also like the timeanddate one, which (at least on mobile) lets you zoom in the map a little more, nice if you're trying to make travel plans. The last one went right next to...

    That's a nice tool! I also like the timeanddate one, which (at least on mobile) lets you zoom in the map a little more, nice if you're trying to make travel plans.

    The last one went right next to where I was living at the time, and this time around, despite having moved across the country, the totality goes just south of my city. I'm looking forward to seeing the full thing this year.

    3 votes
  4. Comment on Conservative government would require websites to verify age to watch porn: Pierre Poilievre in ~tech

    Crespyl
    Link Parent
    The problem is that the NAT makes every user on the LAN share the same WAN IP address/connection to the ISP. The ISP doesn't really have a good way to tell how many people are connected to that...

    The problem is that the NAT makes every user on the LAN share the same WAN IP address/connection to the ISP. The ISP doesn't really have a good way to tell how many people are connected to that router/modem, let alone who's who.

    Tracking sessions through NAT (at the ISP level) like that would require some kind of new packet tagging protocol, and it's really hard to do that without upgrading all the infrastructure and middleware boxes in between.

    I could maybe see it working if everyone switches entirely to IPv6, but that seems like one of those "perpetually five years away" sorts of things.

    1 vote
  5. Comment on Downtime due to sign up spam in ~tech

    Crespyl
    Link Parent
    Bitcoin was in part inspired by Hashcash, a very similar PoW system from the 90's designed to combat email spam. In principle the idea of requiring a small-but-measurable cost (either of actual...

    Bitcoin was in part inspired by Hashcash, a very similar PoW system from the 90's designed to combat email spam.

    In principle the idea of requiring a small-but-measurable cost (either of actual money like a USPS stamp, or electricity/compute as in PoW) seems like a good anti-spam measure for a lot of systems, but I'm not sure how well it scales in a world full of crypto ASICs and GPU compute.

    16 votes
  6. Comment on The Playdate handheld console is now in stock and available in twenty-two additional countries in ~games

    Crespyl
    Link Parent
    YOYOZO is fun, thanks for making it! I also really liked Bloom (from another team), and Lucas Pope is working on Mars After Midnight which will hopefully release soon. Another game in development...

    YOYOZO is fun, thanks for making it!

    I also really liked Bloom (from another team), and Lucas Pope is working on Mars After Midnight which will hopefully release soon. Another game in development is Diora, which also looks really cool.

    Bennet Foddy also has a game on there, Zipper.

    1 vote
  7. Comment on doukutsu-rs, an open-source re-implementation of the Cave Story engine in ~games

    Crespyl
    Link Parent
    Neat! Cave Story is one of my favorite games, I remember playing the freeware version when I was a penniless youngster looking for cheap thrills, and at the time it absolutely blew me away with...

    Neat!

    Cave Story is one of my favorite games, I remember playing the freeware version when I was a penniless youngster looking for cheap thrills, and at the time it absolutely blew me away with its heart and scope. It still holds up as a great classic, and I'm always happy to see new ways for people to share and enjoy the game.

    I couldn't quickly find a list of what's fixed/changed/added, although it looks like it supports replays (and maybe a second player??).

    8 votes
  8. Comment on Single-player games to play with my partner in ~games

    Crespyl
    Link Parent
    Myst has been remastered a number of times, these days it runs in full realtime 3D and looks great. The sequel, Riven, (my favorite) has never been remastered (due to losing the original assets in...

    Myst has been remastered a number of times, these days it runs in full realtime 3D and looks great.

    The sequel, Riven, (my favorite) has never been remastered (due to losing the original assets in a fire, IIRC), but should still run ok. If it doesn't run as is, I think it's supported by ScumVM. Also, it's getting a ground up remake that should be out in the not-too-distant future!

    Obduction isn't directly connected to the Myst games, so you don't need to have played those to make sense of the story, and the latest game is actually Firmament, which is also standalone (and fairly short by comparison).

    3 votes
  9. Comment on A secret tunnel in a NYC synagogue leads to a brawl between police and worshippers in ~humanities

    Crespyl
    Link Parent
    The URL bar in your screenshot still reads greenhomestyle.com, maybe you have an extension somewhere causing problems?

    The URL bar in your screenshot still reads greenhomestyle.com, maybe you have an extension somewhere causing problems?

    2 votes
  10. Comment on Why this math professor objects to diversity statements in ~life

  11. Comment on A secret tunnel in a NYC synagogue leads to a brawl between police and worshippers in ~humanities

    Crespyl
    Link Parent
    Just as another data point, I also see the same behavior, the original link is to laundry tips. Weird.

    Just as another data point, I also see the same behavior, the original link is to laundry tips.

    Weird.

    2 votes
  12. Comment on Sourcehut [open source competitor to Github] is being DDoSed in ~comp

    Crespyl
    Link Parent
    Interestingly, Godot was also under attack (via HN) recently, don't know if it's back up yet.

    Interestingly, Godot was also under attack (via HN) recently, don't know if it's back up yet.

    13 votes
  13. Comment on Windows 10 end of life could prompt torrent of e-waste as 240 million devices set for scrapheap in ~tech

    Crespyl
    Link Parent
    Does that just align the contents of the taskbar to the bottom left corner, or actually move the whole bar to be vertical along the full height of the left side of the screen? The former is a nice...

    Does that just align the contents of the taskbar to the bottom left corner, or actually move the whole bar to be vertical along the full height of the left side of the screen?

    The former is a nice concession to people who don't care for the new center-bottom layout, the latter is a useful feature for widescreen displays, and my own preferred layout. At least at launch Win11 didn't allow for vertical taskbars at all, if that's changed then that's some good news.

    3 votes
  14. Comment on What games have you been playing, and what's your opinion on them? in ~games

    Crespyl
    Link
    I finally finished The Last of Us: Part 1 on my Steam Deck. I'd already seen the HBO adaptation, so I knew where the story was headed, but actually playing through it was definitely a very...

    I finally finished The Last of Us: Part 1 on my Steam Deck.

    I'd already seen the HBO adaptation, so I knew where the story was headed, but actually playing through it was definitely a very worthwhile experience. Particularly near the end, the game changes things up enough to kind of force you to reasses the amount of violence you're doing (/teaching this child to do) from a different angle.

    There's that one bit right near the end where the game essentially forces you to take a certain action that hits way harder than it did in the TV adaptation, which makes for another good example of how games can deliver certain story beats through interactivity in a way unique among other forms of storytelling.

    Really enjoyed it overall, and I'm looking forward to seeing Part 2 come to PC as well, hopefully I won't have to wait too long.

    1 vote
  15. Comment on Game recommendations, specifically in ~games

    Crespyl
    Link Parent
    It's not quite as much logistical, more execution focused, but Jusant had Death Stranding vibes to me when I played it. It's a climbing game that requires a bit of looking around to plan your next...

    It's not quite as much logistical, more execution focused, but Jusant had Death Stranding vibes to me when I played it. It's a climbing game that requires a bit of looking around to plan your next stretch of climbing, and careful movements with both hands to progress. The story's a bit thin, but gameplay was rewarding and chill, and the visuals were quite nice.

    It's made by Don'tNod, the team behind the Life is Strange games.

    2 votes
  16. Comment on Day 9: Mirage Maintenance in ~comp.advent_of_code

    Crespyl
    Link
    This was easier than I expected for a saturday puzzle, but pleasant to implement. Part 1 - Ruby def differences(nums) diffs = [] nums[1..].each_with_index do |n,i| diffs.append(n - nums[i]) end...

    This was easier than I expected for a saturday puzzle, but pleasant to implement.

    Part 1 - Ruby
    def differences(nums)
      diffs = []
      nums[1..].each_with_index do |n,i|
        diffs.append(n - nums[i])
      end
      diffs
    end
    
    def predict(nums)
      if nums.all? { |n| n == 0 }
        0
      else
        diffs = differences(nums)
        nums.last + predict(diffs)
      end
    end
    
    def compute_p1(input)
      histories = input.lines.map { |l| l.split(" ").map(&:to_i) }
      predictions = histories.map { |h| predict(h) }
      predictions.sum
    end
    
    Part 2
    def predict_back(nums)
      if nums.all? { |n| n == 0 }
        0
      else
        diffs = differences(nums)
        nums.first - predict_back(diffs)
      end
    end
    
    def compute_p2(input)
      histories = input.lines.map { |l| l.split(" ").map(&:to_i) }
      predictions = histories.map { |h| predict_back(h) }
      predictions.sum
    end
    
    1 vote
  17. Comment on Game recommendations, specifically in ~games

    Crespyl
    Link Parent
    I just recently came upon Smushi Come Home and have been enjoying it. Climbing seems to be a little more restrictive than in BotW, say, but you can jump, climb, glide and generally explore at your...

    I just recently came upon Smushi Come Home and have been enjoying it. Climbing seems to be a little more restrictive than in BotW, say, but you can jump, climb, glide and generally explore at your own pace in a series of cozy little areas, full of little guys to talk to and things to do.

    I'll also second the recommendation for Lil Gator Game.

    1 vote
  18. Comment on Day 4: Scratchcards in ~comp.advent_of_code

    Crespyl
    Link
    This is one of those ones where understanding the instructions was the most complicated part. My Part 2 solution is slow, taking about 10 seconds to run on my machine, but that's good enough for...

    This is one of those ones where understanding the instructions was the most complicated part.

    My Part 2 solution is slow, taking about 10 seconds to run on my machine, but that's good enough for me.

    Part 1 - Ruby
    def parse_card(line)
      id = line.match(/Card\s+(\d+):.+/)[1].to_i
      numbers = line.split(":")[1].split("|").map { |s| s.split(" ").map(&:to_i) }
      {id: id,
       winning: numbers.first,
       have: numbers.last}
    end
    
    def score_card(card)
      matches = card[:winning].filter { |n| card[:have].include?(n) }
      return 0 if matches.empty?
      matches[1..].reduce(1) { |score, n| score * 2 }
    end
    
    def compute_p1(input)
      input
        .lines
        .map { |l| parse_card(l) }
        .map { |c| score_card(c) }
        .sum
    end
    
    Part 2
    def card_matches(card)
      card[:winning].filter { |n| card[:have].include?(n) }
    end
    
    def compute_p2(input)
      cards = input
                .lines
                .map { |l| parse_card(l) }
      copies_table = {}
      cards.each { |c| copies_table[c[:id]]=1 }
      cards.each_with_index do |card,idx|
        copies_table[idx+1].times do
          matches = card_matches(card).count
          1.upto(matches) do |i|
            copies_table[idx+i+1] += 1
          end
        end
      end
      copies_table.values.sum
    end
    
    1 vote
  19. Comment on Day 3: Gear Ratios in ~comp.advent_of_code

    Crespyl
    Link
    This seems tricky for the third day, but it's really just another parsing problem at its core. I was able to re-use a utility class from previous years, which helps a lot with handling grids like...

    This seems tricky for the third day, but it's really just another parsing problem at its core. I was able to re-use a utility class from previous years, which helps a lot with handling grids like this (they come up frequently in AoC problems).

    Part 1 - Ruby
    def compute_p1(input)
      symbols = "*#/+%$&=@-".chars
      g = Grid.new(input)
      # find each symbol
      g.coords_where { |c| symbols.include?(c) }
       .map { |x,y| get_neighboring_numbers(g, x, y) }
       .flatten
       .sum
    end
    
    def read_number(grid, start_x, start_y)
      str = grid.get(start_x,start_y).clone
      return nil unless str.match?(/[[:digit:]]/)
      x, y = start_x-1, start_y
      while grid.get(x,y) != nil && grid.get(x,y).match?(/[[:digit:]]/)
        str.insert(0, grid.get(x,y))
        x -= 1
      end
      x = start_x+1
      while grid.get(x,y) != nil && grid.get(x,y).match?(/[[:digit:]]/)
        str.concat(grid.get(x,y))
        x += 1
      end
      str.to_i
    end
    
    def get_neighboring_numbers(grid, start_x, start_y)
        [
          [-1, -1], [0, -1], [+1, -1],
          [-1,  0],          [+1,  0],
          [-1, +1], [0, +1], [+1, +1]
        ].map { |dx, dy| [start_x+dx, start_y+dy] }
         .filter { |x, y| grid.get(x,y).match?(/[[:digit:]]/) }
         .map { |x, y| read_number(grid, x, y) }
         .uniq # assuming there are no two identical numbers on the same symbol
    end
    
    class Grid
      attr_accessor :grid
      attr_accessor :width
      attr_accessor :height
      attr_accessor :default
    
      def initialize(input, default: nil)
        @grid = input.lines
                  .map(&:strip)
                  .map(&:chars)
        @width = @grid[0].size
        @height = @grid.size
        @default = default
      end
    
      def in_bounds?(x, y)
        x >= 0 && x < @width && y >= 0 && y < @height
      end
    
      def get(x,y)
        if x < 0 || x >= @width || y < 0 || y >= @height
          @default
        else
          @grid[y][x]
        end
      end
    
      def set(x,y,val)
        if x < 0 || x >= @width || y < 0 || y >= @height
          raise "Tried to write out of bounds"
        else
          @grid[y][x] = val
        end
      end
    
      def all_coords
        (0...width).to_a.product((0...height).to_a)
      end
    
      def coords_where
        all_coords.filter { |x, y| yield(@grid[y][x]) }
      end
    
      def each_index
        all_coords.each do |x,y|
          yield(x,y)
        end
      end
    
      def update
        each_index do |x, y|
          @grid[y][x] = yield(x, y, @grid[y][x])
        end
      end
    
      def ==(other)
        return false if other.class != Grid
        return other.grid == @grid
      end
    
      def all?(value)
        return @grid.flatten.all?(value)
      end
    
      def neighbors(x,y)
        [
          [-1, -1], [0, -1], [+1, -1],
          [-1,  0],          [+1, 0],
          [-1, +1], [0, +1], [+1, +1]
        ].map { |dx, dy| get(x+dx, y+dy) }
      end
    
      def to_s
        s = ""
        height.times do |y|
          width.times do |x|
            s << get(x,y) || default.to_s
          end
          s << "\n"
        end
        return s
      end
    
      def count(value)
        if block_given?
          @grid.flatten.count(&block)
        else
          @grid.flatten.count(value)
        end
      end
    end
    
    Part 2

    Part 2 becomes easy when we already have everything in a searchable grid format.

    def compute_p2(input)
      g = Grid.new(input)
      g.coords_where { |c| c == "*" }
       .map { |x,y| get_neighboring_numbers(g, x, y) }
       .filter { |numbers| numbers.length == 2 }
       .map { |numbers| numbers.inject(&:*) }
       .sum
    end
    
    2 votes
  20. Comment on Day 2: Cube Conundrum in ~comp.advent_of_code

    Crespyl
    Link
    Didn't start this one until the afternoon. Day 2 - Ruby #!/usr/bin/env ruby require 'benchmark' require 'minitest' require 'pry-byebug' TEST_STR = "\ Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue;...

    Didn't start this one until the afternoon.

    Day 2 - Ruby
    #!/usr/bin/env ruby
    
    require 'benchmark'
    require 'minitest'
    require 'pry-byebug'
    
    TEST_STR = "\
    Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
    Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
    Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
    Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
    Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green"
    
    class Test < MiniTest::Test
      def test_p1
        assert_equal(8, compute_p1(TEST_STR))
      end
    
      def test_p2
        assert_equal(2286, compute_p2(TEST_STR))
      end
    end
    
    def parse_game(line)
      m = line.match(/Game (\d+): (.+;?)/)
      id = m[1].to_i
      draws = m[2]
                .split(";")
                .map { |cubes| cubes.split(",")
                            .map { |s| s.strip.split(" ") }}
                .map { |cubes|
                  cubes.reduce({}) { |h,a| h[a[1].to_sym] = a[0].to_i; h }}
      {id: id, draws: draws}
    end
    
    def is_possible?(game, limits)
      game[:draws].each do |draw|
        return false unless draw.all? do |color, count|
          count <= limits[color]
        end
      end
      return true
    end
    
    def minimum_cubes_power(game)
      game[:draws].reduce(Hash.new(0)) { |maximums,draw|
        draw.each { |color, count|
          if maximums[color] < count
            maximums[color] = count
          end
        }
        maximums
      }.reduce(1) { |product,kv| kv.last * product }
    end
    
    def compute_p1(input)
      limits = {red: 12, green: 13, blue: 14}
      input
        .lines
        .map { |l| parse_game(l) }
        .filter { |g| is_possible?(g, limits) }
        .map { |g| g[:id] }
        .sum
    end
    
    def compute_p2(input)
      input.lines
        .map { |l| parse_game(l) }
        .map { |g| minimum_cubes_power(g) }
        .sum
    end
    
    if MiniTest.run
      puts 'Test case OK, running...'
    
      @input = File.read(ARGV[0] || 'input')
      do_p2 = defined?(compute_p2)
    
      Benchmark.bm do |bm|
        bm.report('Part 1:') { @p1 = compute_p1(@input) }
        bm.report('Part 2:') { @p2 = compute_p2(@input) } if do_p2
      end
    
      puts "\nResults:"
      puts 'Part 1: %i' % @p1
      puts 'Part 2: %i' % @p2 if do_p2
    
    else
      puts 'Test case ERR'
    end
    
    1 vote