Ruby: Undefined method error on method called AFTER definition - ruby

I am not sure what's happening here...but say I do this:
def who_wins?(choice1, choice2)
if (choice1 == 'R' && choice2 == 'S') || (choice1 == 'S' && choice2 == 'P') || (choice1 == 'P' && choice2 == 'R')
return choice1
elsif choice1 == choice2
return "tie"
else
raise NoSuchStrategyError
end
end
won_wins?('R', 'P')
It gives me the following error:
NoMethodError: undefined method `won_wins?' for main:Object
at top level in my-file.rb at line 25
Why would it do that, even though I am calling the method AFTER the definition?

You typed won_wins? and not who_wins?

Related

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?
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.
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.

Rock paper scissors in Ruby

I have to implement a small part of a Rock, Paper, Scissors game for an assignment. Here is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError ; end
def self.winner(player1, player2)
# YOUR CODE HERE
puts "player1 = " + player1[1]
if (player1[1] || player2[1] != 'R') || (player1[1] || player[2] != 'P') || (player1[1] || player2[1] != 'S')
raise NoSuchStrategyError.new("Strategy must be one of 'R','S','P'")
else
if player1[1] == player2[1]
return player1
elsif (player1[1] == 'R' && player2[1] == 'P') || (player1[1] == 'P' && player2[1] == 'S') || (player1[1] == 'S' && player2[1] == 'R')
return player2
else
return player1
end
end
end
def self.tournament_winner(tournament)
# YOUR CODE HERE
end
end
I am using rspec to test out the code and it breaks in both of the below cases. I am assuming that it has to do with where I check to make sure that the input is valid, and even though the input is valid, for whatever reason, the way I am doing the line if (player1[1] || player2[1] != 'R') || (player1[1] || player[2] != 'P') || (player1[1] || player2[1] != 'S') is making the input fail on every case. How can I fix that line so that if the input is valid, then it doesn't raise an error?
Failures:
1) RockPaperScissors game rock breaks scissors [10 points]
Failure/Error: raise NoSuchStrategyError.new("Strategy must be one of 'R','S','P'")
RockPaperScissors::NoSuchStrategyError:
Strategy must be one of 'R','S','P'
# ./lib/rock_paper_scissors.rb:11:in `winner'
# ./spec/rock_paper_scissors_spec.rb:10:in `block (3 levels) in <top (required)>'
EDIT: player1 and player2 are arrays with format ["playername", "move"] with the move being either R,S or P for rock, scissors or paper

Ruby if else statement refactoring

The current code works:
def launched_city(country, city, city_link)
return 'current' if country == 'Malaysia' && ('Kuala Lumpur' == city_link)
return 'current' if country == 'Philippines' && ('Manila' == city_link)
if country == 'Australia'
return 'current' if city == 'Melbourne' && ('Melbourne' == city_link)
return 'current' if city == 'Sydney' && ('Sydney' == city_link)
return 'current' if city == 'Perth' && ('Perth' == city_link)
end
nil
end
but I think it's ugly. Any help?
I tried with case block. It failed with the case statement because I need to check the second statement. I also tried with if elsif else block. It's the same in this case.
COUNTRY_LINKS = { 'Malaysia'=>['Kuala Lumpur'],
'Philippines'=>['Manila'],
'Australia'=>['Melbourne', 'Sydney', 'Perth'] }
def launched_city(country, city, city_link)
if COUNTRY_LINKS.has_key?(country) && COUNTRY_LINKS[country].include? city_link) &&
(country != 'Australia' || city == city_link)
'current'
end
end

undefined method `keys' for nil:NilClass in tic tac toe game RUBY

I'm trying to build a simple 2 player tic tac toe game in Ruby.
Here is the code:
class Morpion
def init
create_grid
get_player
show_grid
end
def get_player
puts "Let play some Tic Tac Toe"
puts ""
#player1 ='X'
#player2='O'
puts ""
puts "Where would you like to move? (check out the grid below and type any number 1-9 to place your symbol): "
puts " 1 | 2 | 3 "
puts "---+---+---"
puts " 4 | 5 | 6 "
puts "---+---+---"
puts " 7 | 8 | 9 "
end
def create_grid
#grid = {
'1' => ' ',
'2' => ' ',
'3' => ' ',
'4' => ' ',
'5' => ' ',
'6' => ' ',
'7' => ' ',
'8' => ' ',
'9' => ' '
}
end
def show_grid
puts ""
puts "#{#grid['1']}|#{#grid['2']}|#{#grid['3']}"
puts "-----"
puts "#{#grid['4']}|#{#grid['5']}|#{#grid['6']}"
puts "-----"
puts "#{#grid['7']}|#{#grid['8']}|#{#grid['9']}"
puts ""
end
def play
number_turns=1
while number_turns < 10
number_turns.odd? ? player_turn(#player1) : player_turn(#player2)
game_checker
if game_checker
break
end
number_turns+=1
end
end
def player_turn(player)
puts player == 'X' ? "It's X's turn!" : "It's O's turn!"
puts ""
cell = gets.chomp
unless #grid.keys.include?(cell) #check if the user entered a number corresponding to the grid
puts ""
puts "it has to be a number from 1 to 9"
player_turn(player)
end
if #grid[cell] == ' ' #check if cell in grid is empty for user input
#grid[cell] = player
else
puts ""
puts "That cell is occupied. Choose again!"
player_turn(player)
end
show_grid
end
def game_checker
end_game = false
if #grid['1'] != ' ' && #grid['5'] != ' ' && #grid['9'] != ' '
if (#grid['1'] == #grid['2'] && #grid['1'] == #grid['3'])
end_game = true
victory = #grid['1']
elsif (#grid['4'] == #grid['5'] && #grid['4'] ==#grid['6'])
end_game = true
victory = #grid['4']
elsif (#grid['7'] == #grid['8'] && #grid['7'] == #grid['9'])
end_game = true
victory = #grid['7']
elsif (#grid['1'] == #grid['4'] && #grid['1'] == #grid['7'])
end_game = true
victory = #grid['1']
elsif (#grid['2'] == #grid['5'] && #grid['2'] == #grid['8'])
end_game= true
victory = #grid['2']
elsif (#grid['3'] == #grid['6'] && #grid['3'] == #grid['9'])
end_game = true
victory = #grid['3']
elsif (#grid['1'] == #grid['5'] && #grid['1'] == #grid['9'])
end_game = true
victory = #grid['1']
elsif (#grid['3'] == #grid['5'] && #grid['3'] == #grid['7'])
end_game = true
victory = #grid['3']
else
end_game = false
end
end
if end_game
puts "the winner of this game is #{victory}"
return true
end
end
end
m=Morpion.new
m.play
So my issue is this:
1. I am asking a player to add his symbol (X or O) in the grid that ranges from 1 to 9 (because there is 9 cells)
if I enter 1 for example, which is the upper left cell I get this error:
(eval):187: undefined method `keys' for nil:NilClass (NoMethodError)
from (eval):168:in `play'
from (eval):245
If you want to run this program I suggest using THIS LINK
EDIT:
The problem as #Paul and #August stated was that I used an incorrect constructor method init instead of using the correct one: initialize.
Now my program works. Thanks to them.
You initialize the #grid hash in a method called init. Ruby won't call this method when you construct a new instance of Game. You should instead rename the init method to initialize, which will be called automatically by Ruby.
The problem is that your #grid variable is never being created; it is nil. Hence the error message, you're attempting to invoke a method on a nil object.
The cause of your woes is because you've misnamed the constructor method. In Ruby, constructors are named initialize, however you named it init. Rename it to the correct name, and it should work.

Paper, Scissor, Rock game in Ruby. When an array changes data, it changes the data of another array (¿?)

This is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError
end
def self.winner(player1, player2)
if ((player1[1] == 'R') && (player2[1] == 'S') ||
(player1[1] == 'S') && (player2[1] == 'P') ||
(player1[1] == 'P') && (player2[1] == 'R'))
return player1
elsif ((player1[1] == 'R') && (player2[1] == 'P') ||
(player1[1] == 'S') && (player2[1] == 'R') ||
(player1[1] == 'P') && (player2[1] == 'S'))
return player2
elsif ((player1[1] == 'R') && (player2[1] == 'R') ||
(player1[1] == 'S') && (player2[1] == 'S') ||
(player1[1] == 'P') && (player2[1] == 'P'))
return player1
end
end
def self.tournament_winner(tournament)
player1 = Array.new
player2 = Array.new
nextround = Array.new
while tournament.length != 1 do
tournament.each_with_index {|item, index|
if (index%2!=0)
player2[0] = item[0]
player2[1] = item[1]
elsif (index%2 ==0)
player1[0] = item[0]
player1[1] = item[1]
else
puts 'bananas'
end
if (index%2!=0)
nextround[(index-1)/2] = winner(player1, player2)
end
}
tournament=nextround
end
return tournament
end
end
RockPaperScissors.tournament_winner([["num1", "R"], ["num2", "P"], ["num3", "P"], ["num4", "R"]])
Well, the last line is an execution launch. This code makes a tournament of rock, paper scissors. It takes as input an array of arrays with each character and its attack, and it has to return the array with the champion and its attack.
The tournament is num1 vs num2 (num2 wins), and num3 vs num4 (num3 wins). Then the final is Num2 vs Num3, and in this stablemate wins the first guy in the array (Num2).
It seems overcomplicated because the code has to work with any number of characters, as long as their number is base2 (2, 4, 8, 16 characters..., etc).
My problem is next (debug the code and you will see). When it changes the value of the array 'Player1' or 'Player2', it also changes the value in the array 'nextround', even if it is not in that line!
That is not suppose to happen!
By the way, I am learning Ruby so it may be a really stupid failure.
why does this need to be true?
"It seems overcomplicated because the code has to work with any number of characters, as long as their number is base2 (2, 4, 8, 16 characters..., etc)."
Rather having player1 and player2 be arrays, I would rewrite them to be instances of a class Player. Then write methods in the Player class so you can call player1.hand and it returns 'S' || 'R' || 'P'
that way you can store how many wins a player has on the player object,
things I'd look into learning more about
case/when statements
the special initialize method
attrs_accessor (used for making data accessible across classes)
modules
also i've seen it done and i may be wrong on this, but generally you don't put classes inside classes.

Resources