user6796341
user6796341

Reputation:

Ruby - How do I shorten my method

I have a hash here:

VALID_CHOICES = {
  'r' => 'rock',
  'p' => 'paper',
  'sc' => 'scissors',
  'l' => 'lizard',
  'sp' => 'spock'
}

And a method which basically compares here:

def win?(first, second)
  (first == 'sc' && second == 'p') ||
    (first == 'p' && second == 'r') ||
    (first == 'r' && second == 'l') ||
    (first == 'l' && second == 'sp') ||
    (first == 'sp' && second == 'sc') ||
    (first == 'sc' && second == 'l') ||
    (first == 'l' && second == 'p') ||
    (first == 'p' && second == 'sp') ||
    (first == 'sp' && second == 'r') ||
    (first == 'r' && second == 'sc')
end

How can I rewrite my method in very short concise code that means exactly the same thing? Any idea? Is it possible to do it using hashes?

Upvotes: 3

Views: 287

Answers (2)

Stefan
Stefan

Reputation: 114158

In addition to user2864740's comment and Cary Swoveland's explanation, you could also use a hash to map "winning pairs" to their respective verb:

WINS = {
  %w[scissors paper]    => 'cuts',
  %w[paper    rock]     => 'covers',
  %w[rock     lizard]   => 'crushes',
  %w[lizard   spock]    => 'poisons',
  %w[spock    scissors] => 'smashes',
  %w[scissors lizard]   => 'decapitates',
  %w[lizard   paper]    => 'eats',
  %w[paper    spock]    => 'disproves',
  %w[spock    rock]     => 'vaporizes',
  %w[rock     scissors] => 'crushes'
}

It returns the corresponding verb if the key's first item beats the second:

WINS[['paper', 'rock']] #=> "covers"

and nil if it doesn't:

WINS[['rock', 'paper']] #=> nil

In your method:

def win?(first, second)
  WINS.has_key?([first, second])
end

Or to check both sides:

if WINS.has_key?([first, second])
  # first wins
elsif WINS.has_key?([second, first])
  # second wins
else
  # tie
end

Or more verbose:

def result(first, second)
  if verb = WINS[[first, second]]
    "first wins: #{first} #{verb} #{second}" 
  elsif verb = WINS[[second, first]]
    "second wins: #{second} #{verb} #{first}" 
  else
    "tie"
  end
end

result('rock', 'scissors')
#=> "first wins: rock crushes scissors"

result('spock', 'lizard')
#=> "second wins: lizard poisons spock"

result('paper', 'paper')
#=> "tie"

Of course, you can also use your abbreviations (sc, p, r, l, sp) instead of whole words.

Upvotes: 1

PinnyM
PinnyM

Reputation: 35533

You should define clear rules for what each token can win:

WINS = {
  'r' => %w{l sc},
  'p' => %w{r sp},  
  'sc' => %w{p l},
  'l' => %w{p sp},
  'sp' => %w{r sc}
}

Now you can determine wins using a simple lookup:

def win?(first, second)
  WINS[first].include?(second)
end

While there may be several 'clever' ways to avoid an explicit structure like WINS, explicit rules are much more understandable - and therefore, more maintainable. Conciseness in code is considered a positive attribute where it improves the readability of the code. Conciseness to the extreme that causes the code to be difficult to understand is not something to strive for.

Upvotes: 7

Related Questions