Exercise: Minesweeper in 100 lines of Ruby
96 points by damir 11 months ago | 33 comments- waf 11 months agoFun 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 agoAuthor here, thank you for doing this! Really nice. And yes, I was not counting empty lines, and also, no code golfing. :)
- neonsunset 11 months agoThank 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...
- radanskoric 11 months ago
- akdfax 11 months agoVery nice syntax. Ruby is definitely underappreciated at the moment.
- RodgerTheGreat 11 months agoI 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 agoI'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.
- codesnik 11 months ago
- pooriar 11 months agoI'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 agoAuthor here, I'm really glad it helped! Thank you for reading. :)
- radanskoric 11 months ago
- igor47 11 months agoCool! 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
- darthg0d 11 months agoDitto. I did the same with GoLang a couple of years back, but didn't aim for clean/terse code: https://github.com/jedib0t/go-mines
- BogdanOlar 11 months agoSame, in Rust, just as an exercise to get familiar with GUIs. So I ended implementing it twice, once with `iced`: https://github.com/BogdanOlar/iced-minesweep-rs , and with `egui`: https://github.com/BogdanOlar/egui-minesweep-rs , using the common `minefield-rs` library: https://github.com/BogdanOlar/minefield-rs
It's a really fun exercise, and a good way to practice new skills. Highly recommend it.
- BogdanOlar 11 months ago
- darthg0d 11 months ago
- oneeyedpigeon 11 months agoWarning 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 agoAuthor 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 agoradanskoric'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 agoIt'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 agorbenv is another great Ruby version manager.
- Jtsummers 11 months ago
- radanskoric 11 months ago
- aaronpkelly 11 months agoWhy 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 agoThank you again for notifying me about this. I emailed dang and he restored my account right away! :)
- defrost 11 months agoGood 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.
- radanskoric 11 months ago
- wkjagt 11 months agoVery 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 agoThis was fun. I saw a post on Pyxel a couple of days ago, and decided to write mine in Python using it.
- twobitshifter 11 months agodon’t you have to be able to flag the mines?
- RodgerTheGreat 11 months agoFlagging 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 agoIf you're a beginner you do.
- msephton 11 months agoIndeed, it's an important omission.
- RodgerTheGreat 11 months ago
- raymond_goo 11 months agoI raise you 49 lines of typescript
https://stackblitz.com/edit/gridgame-minesweeper?file=index....
- fuzzy_biscuit 11 months agoNot a great analog since the gridgame dependency being pulled in is already over 200 lines.
- fuzzy_biscuit 11 months ago
- henning 11 months agoThe 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 agoSounds 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 agoAnd 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 agoI'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 agoSome 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 agoTypes are why I stopped using Ruby. TypeScript may not be elegant, but it’s got a great type system.
- radanskoric 11 months ago
- stonethrowaway 11 months ago[flagged]
- breckenedge 11 months ago