Exercise: Minesweeper in 100 lines of Ruby

96 points by damir 11 months ago | 33 comments
  • waf 11 months ago
    Fun exercise! I found that the Ruby in this post resembles modern C# (probably because Ruby is a gold standard in readability, and in the last 5 years or so C# has been making big strides in readability). I've recreated the Ruby code mostly line-for-line in C# and posted it here: https://gist.github.com/waf/5c6a04899e8250cb9a89406b978c9bcc

    It mapped from Ruby to C# really well. The only concept I had to add was a `BoardCell` base type for the `Mine` and `Empty` types so we could return either `Mine` or `Empty` in a typesafe way. Everything else matched conceptually 1-1.

    It's 118 lines total, with 18 lines of whitespace, so 100 lines exactly. I figure the original Ruby code was also not counting whitespace so it's a fair comparison. I tried to not use any "code golf tricks" that would minimize line count. It's a full program; it could be copy/pasted into a modern .NET 8 project and run without any additional setup.

    Note, I posted this same comment on the original blog post, but I've posted it here too as it might be interesting for HN folks.

    • radanskoric 11 months ago
      Author here, thank you for doing this! Really nice. And yes, I was not counting empty lines, and also, no code golfing. :)
      • neonsunset 11 months ago
        Thank you for making a C# variant, with K&R braces it's just 102 lines, and 1.5 MB binary (quite a few un-strippable baseline bits like integer formatting) when AOT compiled with 9 preview, we've come a long way...
      • akdfax 11 months ago
        Very nice syntax. Ruby is definitely underappreciated at the moment.
        • RodgerTheGreat 11 months ago
          I had a go at an implementation of my own in Lil, using an imperative style rather than OOP. I tried to loosely follow the organization as shown here, but opted for a very different board representation, and I included some niceties like displaying board legends to aid in choosing the proper coordinates.

          https://github.com/JohnEarnest/Decker/blob/main/examples/lil...

          I'm really not convinced that OOP simplifies or clarifies this type of program.

          • codesnik 11 months ago
            I'd too probably write it in Ruby using classes basically just as modules and not much else. It's totally fine using simple arrays and hashes in Ruby for data structures. For example, unlike in many other languages, board could be just a hash of booleans addressed by "tuples", like @board = {[x1, y1] => true, [x2, y2] => true}, which makes neighbours check trivial, while board will be sufficiently compact.
          • pooriar 11 months ago
            I'm currently implementing my favorite 2 player board game - Raptor - in Ruby, and this article is giving me great ideas for how to structure it better. Thanks so much to the author for writing this up!
            • radanskoric 11 months ago
              Author here, I'm really glad it helped! Thank you for reading. :)
            • igor47 11 months ago
              Cool! Mine sweeper is a really fun easy game to implement. I did a version in Python a few years ago: https://github.com/igor47/sweeper but mine is closer to 500 lines
            • oneeyedpigeon 11 months ago
              Warning to Mac users: you'll need at least ruby 2.7 to run this because it uses a feature called 'argument forwarding'. Looks like the latest ruby installed on macOS 15 is 2.6.

              I eventually got it working with a combination of techniques from [this stackoverflow post](https://stackoverflow.com/questions/8730676/how-can-i-switch...) but it's not left my system in the cleanest of states.

              • radanskoric 11 months ago
                Author here, that's a good point. Most of my readers are Ruby developers and no one doing Ruby regularly uses the macOS Ruby. I'll throw in a simple Dockerfile for easier running.
                • AlchemistCamp 11 months ago
                  radanskoric's sibling comment is correct (but flagged).

                  Most Ruby devs I know are on Macs and none of them are running the ancient version Apple installs. Just like with most other languages, even JavaScript, you'll want to get a current or recent version.

                  Either use a version manager like RVM (popular) or ASDF (my favorite tool since it handles nearly all languages). You can also install Ruby with Homebrew, but I don't recommend using Homebrew for managing programming languages since you may want different versions on different projects.

                  • Jtsummers 11 months ago
                    It's not flagged, it's dead. Their comments and submissions are all auto-killed. My guess is it's because their account had one comment in 2015, no activity until 2022, and then a submission. Everything from that submission in February 2022 on is dead (not [flagged][dead], just [dead]).

                    radanskoric: You should reach out to the mods (contact link at the bottom of almost every page here) and ask to have your account un-banned.

                    • dialsMavis 11 months ago
                      rbenv is another great Ruby version manager.
                  • aaronpkelly 11 months ago
                    Why are all of radanskoric's replies in this story marked as [dead]? He is replying to people here and all his comments won't show for people who don't have showdead enabled

                    And when I check his profile, almost every comment he's made is also marked as [dead] - but I don't see a good reason why?

                    • radanskoric 11 months ago
                      Thank you again for notifying me about this. I emailed dang and he restored my account right away! :)
                      • defrost 11 months ago
                        Good question, you might want to email dang hn@ycombinator.com and ask - it goes back to 2022 and might be related to excessive self promotion, something else, or even an accidental click.
                      • wkjagt 11 months ago
                        Very fun stuff, and nice post.

                        Reminds me of my 2048 game clone in Ruby in about 100 lines (https://github.com/wkjagt/2048)

                        And I just saw that I did this - oh god - 9 years ago. Time goes way too fast.

                        • abhishekbasu 11 months ago
                          This was fun. I saw a post on Pyxel a couple of days ago, and decided to write mine in Python using it.

                          https://github.com/abhishekbasu/minesweeper

                          • twobitshifter 11 months ago
                            don’t you have to be able to flag the mines?
                            • RodgerTheGreat 11 months ago
                              Flagging mines is a nice convenience/usability feature, but if you're aiming for minimalism, the game can be considered complete when mine locations are the only un-revealed tiles.
                              • IncreasePosts 11 months ago
                                If you're a beginner you do.
                                • msephton 11 months ago
                                  Indeed, it's an important omission.
                                • raymond_goo 11 months ago
                                  • fuzzy_biscuit 11 months ago
                                    Not a great analog since the gridgame dependency being pulled in is already over 200 lines.
                                  • henning 11 months ago
                                    The stupid thing about object-oriented programming and modern software is that code like this would never make it through code review on an "agile" team.

                                    - "Oh, why are you doing procedural case statements? that's a SOLID anti-pattern, please refactor to polymorphism"

                                    - "Oh, why is the Ascii Renderer class meddling in cell mine logic? `cell.neighbour_mines.zero? ? "_" : cell.neighbour_mines` should be its own method in a `Mine` class that is properly tested"

                                    You're never allowed to just write code that solves a problem. It never ends. Your team has unlimited authority to slow you down with completely useless "feedback" that does not help anyone.

                                    • breckenedge 11 months ago
                                      Sounds like a pretty dysfunctional team, not due to code review or agile.

                                      When I’ve found myself on teams like this, as a junior engineer I just did what was asked to get along and get experience. As a senior engineer, I often looked for a new job. As a staff engineer, I push back with as much politeness as I can.

                                      Many engineers out there have traded getting it done for perfection. That’s not good judgement.

                                      • episteme 11 months ago
                                        And those would be valid comments within a production system maintained by a team. If you think code written as a play exercise and as part of professional software environments should be the same then you need to start listening to your team.
                                        • wagthedog 11 months ago
                                          I'm a senior, worked with Ruby for a long time. There's nothing wrong with this code, it's just Ruby, using some of its new features (which all look pretty organic, style-wise). It's been obvious to me for a while: good languages have a fair balance of paradigms. While Ruby is more OOP than others (everything is an object) it also has some fairly nice functional features that are, in the context of the language, are actually fairly easy to comprehend even by junior devs.

                                          I'm beginning to think that the true reason for Ruby losing popularity is because some find it somewhat difficult to wrap their heads around it, but I don't know why -- some people maybe have a completely different mental model and processes in their heads? For me, from day one I got my hands on the language it seemed perfectly natural. Now I program in several languages with various features and properties (compiled/interpreted, strong/weak types, procedural vs OOP vs functional) -- all I can say is it is joy to write programs in Ruby. About the only things I dislike about it are the following: 1) they could've come up with better ways to signal code-block boundaries (keyword `end` is used with way too many different constructions) and, recently, 2) Type implementation where types are declared in separate files, which is probably sub-optimal. For (2), however, I do understand why it is the way it is: they needed something that would not break an already more or less complex argument definition rules (named arguments, default values for named arguments, etc.) and also something that was a layer on top of the language and not so much part of the language itself -- so maybe it's better this way?

                                          Btw, anyone used types in Ruby? Any tips? I'm still avoiding this, wondering if it'd be a boilerplate and a constant annoyance and waste of time. But does anyone think there is a legitimate way to use it and benefit from it in Ruby (language being interpreted and not compiled)?

                                          • radanskoric 11 months ago
                                            Some time ago I was wondering the same thing and did a little experiment: https://radanskoric.com/experiments/experiment-gradual-typin...

                                            My take on typing and Ruby is that it makes sense in specific parts of the code where you expect to get high benefit from it.

                                            Overall, I don't like using types for the whole project. The reason is that you end up losing some specific Ruby benefits, like being able to express the solution in a few very concise lines of dynamically typed code. Ruby is not designed to be a type friendly language and that shows when trying to use types. It works but it doesn't feel like pure Ruby anymore. It's definitely a compromise. You lose a bit of ruby expressiveness to get some type safety.

                                            I want to emphasise that I am actually very fond of types. For example, I've been learning Rust and I really enjoy using Rust for very different reasons than I enjoy using Ruby.

                                            I think that tools are best used in the way they were intended to be used. And for Ruby that's without types. For Rust it's definitely with types. I enjoy both very much.

                                            In Ruby I'll use types in cases where I would actually prefer to implement the part in another language (like Rust) but using typed Ruby is going to be more maintainable than introducing a whole new language just for one section of the code.

                                            • teaearlgraycold 11 months ago
                                              Types are why I stopped using Ruby. TypeScript may not be elegant, but it’s got a great type system.
                                            • stonethrowaway 11 months ago
                                              [flagged]