Ruby Golf: The Results!

Share this article

Back in November, I wrote a piece about Ruby Golf. At the end of the article I set a 5 holes as a challenge for readers to have a go at completing using the minumum number of bytes. There was a terrific response that saw a number of impressive ‘shots’.

The deadline passed at the end of 2011 and since then I’ve been going through all the entries to find the winners of each hole. There were some great solutions that showed some insightful uses of Ruby. It’s fair to say, though, that most of them looked like mind-boggling gobbledygook at first glance … although I think that is par for the course in code golf!

As well as using this post to anounce the winners, I thought it might be useful to go through some of the tricks that were used in each of the winning entries. So, if you feel like you’re stuck in a rough patch of code or a mental sand bunker, then read on – there’s plenty to learn here. Fore!

Example Hole

As part of the article, I presented this as an example of Ruby Golf:

Write a method that would find the sum of all multiples of a given number up to a given value. For example sum(5,24) would calculate the sum of all the multiples of 5 up to 24 (ie 5 + 10 + 15 + 20). Here was my method:

def sum(n,t)
  n*(1..t/n).to_a.inject(&:+)
end

Needless to say that my handicap could certainly be better and there were numerous improvements that the readers were only too keen to point out.

Kyle Putnam spotted that the to_a method isn’t needed as inject is a method of the Enumerable class which ranges inherit from, saving 5 bytes:

def sum(n,t)
  n*(1..t/n).inject(&:+)
end

Rein Henrichs also pointed out that you don’t need the & symbol in Ruby 1.9, and you can also use the reduce method, instead of inject (it has the same numnber of letters, but possibly has more meaning):

def sum(n,t)
  n*(1..t/n).reduce(:+)
end

But then Kyle Dean hit an amazing hole in 1 by employing a bit of elementary mathematics. There is already a formula for summing numbers, which can be used instead of iterating over a range:

def sum(n,t)
  k=t/n;n*k*(k+1)/2
end

This is a great example of eschewing fancy methods and obscure tricks and instead just relying on good old fashioned Mathematics to get the job done.

Hole 1: Fizz Buzz

Given a number the function returns “Fizz” if it is a multiple of 3, “Buzz” if it is a multiple of 5 and “FizzBuzz” if it is a multiple of 15. If the number is not a multiple of 3 or 5 then the number is returned as a string.

This was a fiercly contested hole, but Marcin Mańk drove straight down the fairway with this effort that weighs in at 41 bytes.

def fizzbuzz n
    n%3<1&&f=:Fizz;n%5&lt;1?"#{f}Buzz":"#{f||n}"
  end

This entry uses the modulo operator to check if n is a multiple of 3. If it is, then n%3 will actually be 0, but since we are only dealing with integers, you can save a byte by testing if it is less than 1 (n%3<1) rather than equal to 0 (n%3==0).

The first statement uses the && operator to set f=:Fizz (note also the use of a symbol here, as it uses one less byte than a string) if the n%3<1 evaluates to true (ie if n is a multiple of 3). If n%3<1 is false (ie not a multiple of 3) then the second part is not reached, so the variable f is not set to anything.

After the ;, which signifies the end of a line of code, a similar test is then done to see if n is a multiple of 5. The ternary operator is then used to display one result if this is true and another result if it is false. If n is a multiple of 5 then the following string is returned:

"#{f}Buzz"

What is clever about this is that string interpolation is used to insert the value of f in front of the word Buzz. If f has been set to :Fizz (ie if n was also a multiple of 3 and therfore a multiple of 15), then it is automatically converted to a string and inserted in front of the word Buzz. If f has not been set (ie if n is not a multiple of 3, but still a multiple of 5) then #{f} is simply ignored and the string “Buzz” is all that is returned.

If n is not a multiple of 5 then the following string is returned:

"#{f||n}"

This is a smart use of the || operator to give two possible options. If f has been set, then ‘Fizz’ will be displayed (as a string rather than symbol because interpolation is again being used). The second part of the condition will only be reached if the first part returns false (ie f has not been set) and will then display the number entered (again, as a string due to interpolation).

A brilliant solution that built on the entry by Cyrus. Can it be beaten?

Hole 2: Caesar Cipher

Implement a Caesar Shift Cipher

Example:

caesar("hello",3) => "khoor"

You should also be able to produce negative shifts.

I love codes and ciphers, so was particularly interested in the solution to this one. The best came in at a tiny 29 bytes, courtesy of Josh Cheek:

def caesar(s,n)
    s.gsub(/./){|c|(c.ord+n).chr}
  end

This was a remarkably clean and efficient method that simply used the gsub method to substitute every letter using the regular expression /./ and a block that shifts each character forward n places. The foibles of Ruby dicatate that to add to a string you must first use the ord method to convert it to to it’s integer representation and then use the chr method to change it back to a string.

Hole 3: Rock,Paper,Scissors Game

Write a simple method that ‘plays’ this game, where the player enters their ‘move’ as an argument to the method. If the player enters an invalid option then the result should be ‘lose’. The computer should choose its move at random. The output gives the computer’s ‘move’ and the result as a comma-separated string.

This was my favorite hole and also had the most entries. The winning entry is a thing of beauty and I can’t get over how J–L has condensed the ubiquitous children’s game down into a measley 84 characters:

def play(i)
  m=%w(Rock Paper Scissors);m

[/c]+?,+%w(Draw Win Lose)[((m.index(i)||c-1)-c)%3]
end

It starts by using the shortcut notation of creating an array of the three ‘moves’ and assigning it to a variable. Assignment is usually something to avoid in programming golf as it wastes a couple of bytes. But in this case it is necessary as the array is going to be used again later.

The next statement then just builds up the string that will be returned by the method. It starts by selecting the computers move from the array of moves (m) at random, The computers ‘move’ is calculated using the rand method, but a neat trick is used to assign the random number to a variable, c, while at the same time using it to reference the index of the moves array. This means that c can be used again later:

  m

Next a comma is concatenated to the string. The comma is created using the shortcut ?, which is one byte less than the more obvious string literal ','.

+?,

Last of all the result of the game – win lose or draw – is concatenated to the end. The result is determined by the following bit of code:

+%w(Draw Win Lose)[((m.index(i)||c-1)-c)%3]

m.index(i) finds the index of the player’s move. If this is not ‘Rock’, ‘Paper’ or ‘Scissors’ then it is replaced by 1 less than the computers move (which was set as c earlier in the method). This will guarantee that the player will lose, as you will see soon. Next comes a clever little algorithm that calculates if you have won, drawn or lost using modulo 3 arithmetic. If you subtract the index of the computers move from the index of your own move, then the answer is different depending on the result of the game:

eg 
You play Rock (index = 0). Computer plays Rock(index = 0). This is a draw:
0 - 0 = 0 = 0 mod 3
You play Rock (index = 0). Computer plays Scissors(index = 2). This is a win:
0 - 2 = - 2 = 1 mod 3
You play Rock (index = 0). Computer plays Paper(index = 1). This is a loss:
0 - 1 = - 1 = 2 mod 3

The result is different for depending on the outcome of the game (win, lost or draw). These results are the same for the other other 6 possible results (try it) and means that if you construct the array as %w(draw win lose) then the index matches the result of subtracting the moves.

Hole 4: String Counter

Write a method that when given a string and substring, returns the number of times the substring occurs in that string (ignoring case).

This appeared to be a relatively straightforward solution that was fairly easy to solve using the scan method. Marcin Mańk was the first to use this method, but made the schoolboy error of using the length method instead of size, which is 2 bytes shorter. Christian Guenther was quick to point this out and therefore he wins the prize for this solution in 20 bytes:

def count(s,x) 
    s.scan(/#{x}/i).size
  end

This method works by changing the required substring into a regular expression using interpolation. Each occurence of the string is then placed into an array that is returned by the scan method. To find how many times the string appeared it is then simply a case of finding the size of this array, so the size method can be chained on to the end.

Hole 5: Swingers Function

Write a function that replaces ‘putting your keys in a tin’. The argument to the function is an array of arrays that contain two objects. The function returns a new array where the pairs of objects have been mixed up. An object should not end up with it’s original ‘partner’.

Example:

swingers([["Homer","Marge"],["Micky","Minnie"],["Fred","Wilma"],["Peter","Lois"],["George","Judy"]])
=> [["Homer","Wilma"],["Micky","Lois"],["Fred","Judy"],["Peter","Marge"],["George","Minnie"]]

Josh Cheek had the lowest entry, with 30 bytes:

def swingers(s)
      f,l=s.transpose;f.zip l.rotate
    end

This solution uses mass assignment to assign the result of transposing the array of couples to two variables x and y. What this effectively does is put all the men in x and all the women in y. The zip function is then used to merge the two arrays back togehter, but the rotate method is applied to the y array (of women) first. This effectively shifts every woman along one place. This means that the first man gets put with the next woman along in the array.

This fulfills the requirement that was specified, although there was an implicit requirement that the mixing up should be random. This is easily achieved by applying the shuffle method to the original array. This was actually done early on by Remi, who is awarded the prize for this hole:

def swingers(a)
  x,y=a.shuffle.transpose;x.zip y.rotate
end

Overall

After such tough competition, I’m pleased to anounce the overall winner with the lowest overall score for all five holes … and winner of the inagural Ruby Blazer … is… Josh Cheek! Congratulations to the RubySource Masters Champion of 2011!

If you were one of the indiviual winners mentioned above, then there is a Sitepoint book waiting for you. You simply need to get in touch by leaving a comment below. Congratulations to all those winners, as well as everybody who took part. Remember, it is the process of trying to get a solution that helps to improve your coding skills and, just like in real golf, practice makes perfect. I certainly learned a lot of Ruby by reading through all of the solutions. It was fascinating to see the multitude of different approaches used to reach the same solution. I also liked the fact that a number of the entries built on previous attempts – the equivalent of taking the same route to the green and then sinking a long putt to win the hole! But are these the best possible answer? We can never be 100% sure a solution is a small as possible, so can anybody beat them? Have you learnt any cool new Ruby tricks from these entries or do you have any others that haven’t been mentioned? Let us know in the comments.

But the best thing about Ruby Golf is that at the end of a gruelling round of programming, you can still make a trip to the 19th hole at the end of the day!

Darren JonesDarren Jones
View Author

Darren loves building web apps and coding in JavaScript, Haskell and Ruby. He is the author of Learn to Code using JavaScript, JavaScript: Novice to Ninja and Jump Start Sinatra.He is also the creator of Nanny State, a tiny alternative to React. He can be found on Twitter @daz4126.

Share this article
Read Next
7 Easy Ways to Make a Magento 2 Website Faster
7 Easy Ways to Make a Magento 2 Website Faster
Konstantin Gerasimov
Powerful React Form Builders to Consider in 2024
Powerful React Form Builders to Consider in 2024
Femi Akinyemi
Quick Tip: How to Animate Text Gradients and Patterns in CSS
Quick Tip: How to Animate Text Gradients and Patterns in CSS
Ralph Mason
Sending Email Using Node.js
Sending Email Using Node.js
Craig Buckler
Creating a Navbar in React
Creating a Navbar in React
Vidura Senevirathne
A Complete Guide to CSS Logical Properties, with Cheat Sheet
A Complete Guide to CSS Logical Properties, with Cheat Sheet
Ralph Mason
Using JSON Web Tokens with Node.js
Using JSON Web Tokens with Node.js
Lakindu Hewawasam
How to Build a Simple Web Server with Node.js
How to Build a Simple Web Server with Node.js
Chameera Dulanga
Building a Digital Fortress: How to Strengthen DNS Against DDoS Attacks?
Building a Digital Fortress: How to Strengthen DNS Against DDoS Attacks?
Beloslava Petrova
Crafting Interactive Scatter Plots with Plotly
Crafting Interactive Scatter Plots with Plotly
Binara Prabhanga
GenAI: How to Reduce Cost with Prompt Compression Techniques
GenAI: How to Reduce Cost with Prompt Compression Techniques
Suvoraj Biswas
How to Use jQuery’s ajax() Function for Asynchronous HTTP Requests
How to Use jQuery’s ajax() Function for Asynchronous HTTP Requests
Aurelio De RosaMaria Antonietta Perna
Quick Tip: How to Align Column Rows with CSS Subgrid
Quick Tip: How to Align Column Rows with CSS Subgrid
Ralph Mason
15 Top Web Design Tools & Resources To Try in 2024
15 Top Web Design Tools & Resources To Try in 2024
SitePoint Sponsors
7 Simple Rules for Better Data Visualization
7 Simple Rules for Better Data Visualization
Mariia Merkulova
Cloudways Autonomous: Fully-Managed Scalable WordPress Hosting
Cloudways Autonomous: Fully-Managed Scalable WordPress Hosting
SitePoint Team
Best Programming Language for AI
Best Programming Language for AI
Lucero del Alba
Quick Tip: How to Add Gradient Effects and Patterns to Text
Quick Tip: How to Add Gradient Effects and Patterns to Text
Ralph Mason
Logging Made Easy: A Beginner’s Guide to Winston in Node.js
Logging Made Easy: A Beginner’s Guide to Winston in Node.js
Vultr
How to Optimize Website Content for Featured Snippets
How to Optimize Website Content for Featured Snippets
Dipen Visavadiya
Psychology and UX: Decoding the Science Behind User Clicks
Psychology and UX: Decoding the Science Behind User Clicks
Tanya Kumari
Build a Full-stack App with Node.js and htmx
Build a Full-stack App with Node.js and htmx
James Hibbard
Digital Transformation with AI: The Benefits and Challenges
Digital Transformation with AI: The Benefits and Challenges
Priyanka Prajapat
Quick Tip: Creating a Date Picker in React
Quick Tip: Creating a Date Picker in React
Dianne Pena
How to Create Interactive Animations Using React Spring
How to Create Interactive Animations Using React Spring
Yemi Ojedapo
10 Reasons to Love Google Docs
10 Reasons to Love Google Docs
Joshua KrausZain Zaidi
How to Use Magento 2 for International Ecommerce Success
How to Use Magento 2 for International Ecommerce Success
Mitul Patel
5 Exciting New JavaScript Features in 2024
5 Exciting New JavaScript Features in 2024
Olivia GibsonDarren Jones
Tools and Strategies for Efficient Web Project Management
Tools and Strategies for Efficient Web Project Management
Juliet Ofoegbu
Choosing the Best WordPress CRM Plugin for Your Business
Choosing the Best WordPress CRM Plugin for Your Business
Neve Wilkinson
ChatGPT Plugins for Marketing Success
ChatGPT Plugins for Marketing Success
Neil Jordan
Managing Static Files in Django: A Comprehensive Guide
Managing Static Files in Django: A Comprehensive Guide
Kabaki Antony
The Ultimate Guide to Choosing the Best React Website Builder
The Ultimate Guide to Choosing the Best React Website Builder
Dianne Pena
Exploring the Creative Power of CSS Filters and Blending
Exploring the Creative Power of CSS Filters and Blending
Joan Ayebola
How to Use WebSockets in Node.js to Create Real-time Apps
How to Use WebSockets in Node.js to Create Real-time Apps
Craig Buckler
Best Node.js Framework Choices for Modern App Development
Best Node.js Framework Choices for Modern App Development
Dianne Pena
SaaS Boilerplates: What They Are, And 10 of the Best
SaaS Boilerplates: What They Are, And 10 of the Best
Zain Zaidi
Understanding Cookies and Sessions in React
Understanding Cookies and Sessions in React
Blessing Ene Anyebe
Enhanced Internationalization (i18n) in Next.js 14
Enhanced Internationalization (i18n) in Next.js 14
Emmanuel Onyeyaforo
Essential React Native Performance Tips and Tricks
Essential React Native Performance Tips and Tricks
Shaik Mukthahar
How to Use Server-sent Events in Node.js
How to Use Server-sent Events in Node.js
Craig Buckler
Five Simple Ways to Boost a WooCommerce Site’s Performance
Five Simple Ways to Boost a WooCommerce Site’s Performance
Palash Ghosh
Elevate Your Online Store with Top WooCommerce Plugins
Elevate Your Online Store with Top WooCommerce Plugins
Dianne Pena
Unleash Your Website’s Potential: Top 5 SEO Tools of 2024
Unleash Your Website’s Potential: Top 5 SEO Tools of 2024
Dianne Pena
How to Build a Chat Interface using Gradio & Vultr Cloud GPU
How to Build a Chat Interface using Gradio & Vultr Cloud GPU
Vultr
Enhance Your React Apps with ShadCn Utilities and Components
Enhance Your React Apps with ShadCn Utilities and Components
David Jaja
10 Best Create React App Alternatives for Different Use Cases
10 Best Create React App Alternatives for Different Use Cases
Zain Zaidi
Control Lazy Load, Infinite Scroll and Animations in React
Control Lazy Load, Infinite Scroll and Animations in React
Blessing Ene Anyebe
Building a Research Assistant Tool with AI and JavaScript
Building a Research Assistant Tool with AI and JavaScript
Mahmud Adeleye
Understanding React useEffect
Understanding React useEffect
Dianne Pena
Web Design Trends to Watch in 2024
Web Design Trends to Watch in 2024
Juliet Ofoegbu
Building a 3D Card Flip Animation with CSS Houdini
Building a 3D Card Flip Animation with CSS Houdini
Fred Zugs
How to Use ChatGPT in an Unavailable Country
How to Use ChatGPT in an Unavailable Country
Dianne Pena
An Introduction to Node.js Multithreading
An Introduction to Node.js Multithreading
Craig Buckler
How to Boost WordPress Security and Protect Your SEO Ranking
How to Boost WordPress Security and Protect Your SEO Ranking
Jaya Iyer
Understanding How ChatGPT Maintains Context
Understanding How ChatGPT Maintains Context
Dianne Pena
Building Interactive Data Visualizations with D3.js and React
Building Interactive Data Visualizations with D3.js and React
Oluwabusayo Jacobs
JavaScript vs Python: Which One Should You Learn First?
JavaScript vs Python: Which One Should You Learn First?
Olivia GibsonDarren Jones
13 Best Books, Courses and Communities for Learning React
13 Best Books, Courses and Communities for Learning React
Zain Zaidi
5 jQuery.each() Function Examples
5 jQuery.each() Function Examples
Florian RapplJames Hibbard
Implementing User Authentication in React Apps with Appwrite
Implementing User Authentication in React Apps with Appwrite
Yemi Ojedapo
AI-Powered Search Engine With Milvus Vector Database on Vultr
AI-Powered Search Engine With Milvus Vector Database on Vultr
Vultr
Understanding Signals in Django
Understanding Signals in Django
Kabaki Antony
Why React Icons May Be the Only Icon Library You Need
Why React Icons May Be the Only Icon Library You Need
Zain Zaidi
View Transitions in Astro
View Transitions in Astro
Tamas Piros
Getting Started with Content Collections in Astro
Getting Started with Content Collections in Astro
Tamas Piros
What Does the Java Virtual Machine Do All Day?
What Does the Java Virtual Machine Do All Day?
Peter Kessler
Become a Freelance Web Developer on Fiverr: Ultimate Guide
Become a Freelance Web Developer on Fiverr: Ultimate Guide
Mayank Singh
Layouts in Astro
Layouts in Astro
Tamas Piros
.NET 8: Blazor Render Modes Explained
.NET 8: Blazor Render Modes Explained
Peter De Tender
Mastering Node CSV
Mastering Node CSV
Dianne Pena
A Beginner’s Guide to SvelteKit
A Beginner’s Guide to SvelteKit
Erik KückelheimSimon Holthausen
Brighten Up Your Astro Site with KwesForms and Rive
Brighten Up Your Astro Site with KwesForms and Rive
Paul Scanlon
Which Programming Language Should I Learn First in 2024?
Which Programming Language Should I Learn First in 2024?
Joel Falconer
Managing PHP Versions with Laravel Herd
Managing PHP Versions with Laravel Herd
Dianne Pena
Accelerating the Cloud: The Final Steps
Accelerating the Cloud: The Final Steps
Dave Neary
An Alphebetized List of MIME Types
An Alphebetized List of MIME Types
Dianne Pena
The Best PHP Frameworks for 2024
The Best PHP Frameworks for 2024
Claudio Ribeiro
11 Best WordPress Themes for Developers & Designers in 2024
11 Best WordPress Themes for Developers & Designers in 2024
SitePoint Sponsors
Top 10 Best WordPress AI Plugins of 2024
Top 10 Best WordPress AI Plugins of 2024
Dianne Pena
20+ Tools for Node.js Development in 2024
20+ Tools for Node.js Development in 2024
Dianne Pena
The Best Figma Plugins to Enhance Your Design Workflow in 2024
The Best Figma Plugins to Enhance Your Design Workflow in 2024
Dianne Pena
Harnessing the Power of Zenserp for Advanced Search Engine Parsing
Harnessing the Power of Zenserp for Advanced Search Engine Parsing
Christopher Collins
Build Your Own AI Tools in Python Using the OpenAI API
Build Your Own AI Tools in Python Using the OpenAI API
Zain Zaidi
The Best React Chart Libraries for Data Visualization in 2024
The Best React Chart Libraries for Data Visualization in 2024
Dianne Pena
7 Free AI Logo Generators to Get Started
7 Free AI Logo Generators to Get Started
Zain Zaidi
Turn Your Vue App into an Offline-ready Progressive Web App
Turn Your Vue App into an Offline-ready Progressive Web App
Imran Alam
Clean Architecture: Theming with Tailwind and CSS Variables
Clean Architecture: Theming with Tailwind and CSS Variables
Emmanuel Onyeyaforo
How to Analyze Large Text Datasets with LangChain and Python
How to Analyze Large Text Datasets with LangChain and Python
Matt Nikonorov
6 Techniques for Conditional Rendering in React, with Examples
6 Techniques for Conditional Rendering in React, with Examples
Yemi Ojedapo
Introducing STRICH: Barcode Scanning for Web Apps
Introducing STRICH: Barcode Scanning for Web Apps
Alex Suzuki
Using Nodemon and Watch in Node.js for Live Restarts
Using Nodemon and Watch in Node.js for Live Restarts
Craig Buckler
Task Automation and Debugging with AI-Powered Tools
Task Automation and Debugging with AI-Powered Tools
Timi Omoyeni
Quick Tip: Understanding React Tooltip
Quick Tip: Understanding React Tooltip
Dianne Pena
12 Outstanding AI Tools that Enhance Efficiency & Productivity
12 Outstanding AI Tools that Enhance Efficiency & Productivity
Ilija Sekulov
React Performance Optimization
React Performance Optimization
Blessing Ene Anyebe
Introducing Chatbots and Large Language Models (LLMs)
Introducing Chatbots and Large Language Models (LLMs)
Timi Omoyeni
Migrate to Ampere on OCI with Heterogeneous Kubernetes Clusters
Migrate to Ampere on OCI with Heterogeneous Kubernetes Clusters
Ampere Computing
Scale Your React App with Storybook and Chromatic
Scale Your React App with Storybook and Chromatic
Daine Mawer
10 Tips for Implementing Webflow On-page SEO
10 Tips for Implementing Webflow On-page SEO
Milan Vracar
Get the freshest news and resources for developers, designers and digital creators in your inbox each week