Ruby // Getting Variables to Communicate Across Classes // Why nil? - ruby

I have the following two classes I fabricated for simplicity. I would like to take the information given in the first class, and use it in other classes throughout the program. However, I can not seem to get the variable to retain it's value given by the user.
class Input
attr_accessor :input
def initialize
#input = input
end
def call
get_input
# Changer.new.change(#input)
output
end
def get_input
puts "please write a number"
#input = gets.chomp.to_s
end
def output
p Changer.new.change(#input)
end
end
class Changer < Input
def change(input)
if #input == "one"
#input = "1"
elsif #input == "two"
#input = "2"
elsif #input == nil
"it's nil"
else
"something else"
end
end
end
Input.new.call
I have tried a few variations on the above classes, some with inheritance, some without, initializing, or not, etc. They all seem to output 'nil'. Please advise. Thank you for your time.

When the change method in Changer runs, #input is an instance variable specific to that Changer instance, and it is nil.
You want your change method to work on the input argument being provided to it, rather than #input.
def change(input)
if input == "one"
"1"
elsif input == "two"
"2"
elsif input == nil
"it's nil"
else
"something else"
end
end
Or better yet:
def change(input)
case input
when "one"
"1"
when "two"
"2"
when nil
"it's nil"
else
"something else"
end
end

Related

RUBY Exit minesweeper game loop

I can't figure out how to exit the game loop. I'm having a hard tried making a lose? function, I tried doing like lose?(x) that would return true when x==1 but that didn't get it to exit the run method. Here's my code for the Game class.
class Minesweeper
attr_accessor :board
def initialize
#board = Board.new
end
def run
puts "Welcome to minesweeper!"
x = nil
play_turn until win? || lose?(x)
end
def play_turn
board.render
pos, command = get_input
debugger
if !explode?(pos, command)
board.set_input(pos,command)
else
puts "You lose!"
lose?(1)
end
end
def explode?(pos, cmd)
board.grid[pos[0]][pos[1]].bomb && cmd == "reveal"
end
def get_input
pos = nil
command = nil
until pos && check_pos(pos)
puts "What position?"
pos = parse_pos(gets.chomp)
end
until command && check_command(command)
puts
puts "What would you like to do (e.g. reveal, flag... ~ else?)"
command = gets.chomp
puts
end
[pos, command]
end
#Some code here (check_pos, etc)
def lose?(x)
return true if x == 1
false
end
def win?
# board.all? {}
end
end
I had explode? in the Board class before, but for the sake of being able to end the game, moved it here. Any help is greatly appreciated!
If we expand your run method, a little, it becomes more obvious what the issue is:
def run
puts "Welcome to minesweeper!"
x = nil
until(win? || lose(x)) do
play_turn
end
end
In this method, you're creating a new variable x which is scoped to this method and has a value of nil. This variable is never set in the scope of that method, so is always nil, meaning that the check win? || lose(x) could also be written win? || lose(nil), and lose will never return true.
If you return a value from your play_turn method, you can then use that. Note that the result of the last thing executed in the method is what is returned:
def play_turn
board.render
pos, command = get_input
debugger
if !explode?(pos, command)
board.set_input(pos,command)
true
else
puts "You lose!"
false
end
end
which means your run method can then check the result:
def run
puts "Welcome to minesweeper!"
# we now know that play_turn returns true if the turn was not a loser
# (i.e. it should continue to the next loop) and false if the player
# lost, so we can use that returned value in our loop.
while(play_turn) do
if win?
puts "looks like you won!"
# if the player wins, we want to exit the loop as well. This is done
# using break
break
end
end
end
Note that this also means that you don't need a separate lose method

Can't use include? with an instance variable in a method

I am trying to use 'gets' to get input and then check to see if the instance variable includes the input inside of a method but I can't get it work. Here is the code:
class Game
attr_accessor :available_moves
def initialize
#available_moves = ["0","1","2","3","4","5","6","7","8"]
end
def play_game
puts ("Welcome")
while true
puts "Player 1, choose a square"
player1_choice = gets
# v this is what I can't get to work v
if #available_moves.include?(player1_choice)
puts "yes"
break
else
puts "no"
end
end
end
end
game1 = Game.new
game1.play_game
No matter what I try, the 'else' condition is met and "no" is printed.
When the user inputs text with gets they press Enter, which sends a newline. You need to remove the newline with gets.chomp:
class Game
attr_accessor :available_moves
def initialize
#available_moves = ["0","1","2","3","4","5","6","7","8"]
end
def play_game
puts ("Welcome")
while true
puts "Player 1, choose a square"
# Note the .chomp here to remove the newline that the user inputs
player1_choice = gets.chomp
# v this is what I can't get to work v
if #available_moves.include?(player1_choice)
puts "yes"
break
else
puts "no"
end
end
end
end
game1 = Game.new
game1.play_game
Now you get:
game1.play_game
Welcome
Player 1, choose a square
1
yes
=> nil
See How does gets and gets.chomp in ruby work? for a more in-depth explanation.

How do I extract certain information from an API and return as a list in Ruby?

I'm trying to extract just the "dba" values in the hash from the API and return them as a numbered list.
Here are my 3 separate files/classes. In the 'menu' method I can return all of the hashes, but I just want the "dba" or "restaurant name" instead.
I know I have to put an .each.with_index method in there somewhere, but I can't seem to figure it out.
Any thoughts?
class OpenDataAPI
def self.get_data
response = HTTParty.get('https://data.cityofnewyork.us/resource/43nn-pn8j.json')
response[0..24].each do |hash|
RestaurantInspections.new(hash)
end
end
end
class CLI
def begin
OpenDataAPI.get_data
puts "Welcome to the Restaurant Inspection CLI!"
menu
end
def menu
input = nil
while input != "exit"
puts "Type 1 for a list of Restaurants, type Exit to exit the program"
input = gets.strip.downcase
case input
when "1"
puts OpenDataAPI.get_data
#puts RestaurantInspections.all.each.with_index do |key, index|
#puts "#{index + 1}: #{key}"
#end
when "exit"
break
else
puts "Invalid entry - Please type either 1 or Exit"
end
end
end
class RestaurantInspections
attr_reader :restaurant_name, :type_of_food, :violation_description, :grade
##all = []
def initialize(api_hash)
#restaurant_name = api_hash["dba"]
#type_of_food = api_hash["cuisine_description"]
#violation_description = api_hash["violation_description"]
#grade = api_hash["grade"]
##all << self
end
def self.all
##all
end
end
You're almost there, just change this method to this:
def menu
input = nil
while input != "exit"
puts "Type 1 for a list of Restaurants, type Exit to exit the program"
input = gets.strip.downcase
case input
when "1"
RestaurantInspections.all.each.with_index do |restaurant, index|
puts "#{index + 1}: #{restaurant.restaurant_name}"
end
when "exit"
break
else
puts "Invalid entry - Please type either 1 or Exit"
end
end
end

Ruby: Trouble with scope and class / instance methods

I'm having some trouble understanding scope in ruby.
Here is a link to a repo if you'd like to download/run what I'm talking about to see for yourself:
https://github.com/minervadreaming/killshit
I have several .rb files present - specifically, I'm having an issue calling a class method from an instance. Seems like I'm not properly following scope, but I'm not sure how or why.
Here is a snippet from room.rb:
module KillShit
class Room
attr_reader :player, :monster, :game
def initialize(player, monster, game)
#player = player
#monster = monster
#game = game
end
def action
outline
Player.describe(player)
vs
Monster.describe(monster)
outline
#rolling a d20 to see who takes a turn
turn = rand(1..100)
if turn <= 20
monster_attack
else
puts "What would you like to do?"
puts "1. Attack!"
puts "2. Defend!"
puts "3. Run away!"
#Give the player magic if they're at least level 2
if player.maglevel >= 1
puts "4. Cast spell"
else
end
prompt; action = gets.chomp
if action == "1"
attack(player, monster)
elsif action == "2"
defend
elsif action == "3"
flee
elsif action == "4" && player.maglevel >= 1
magic
else
action
end
end
end
def magic
puts "What magic would you like to cast?"
if player.maglevel == 1
puts "1. Heal"
puts "2. Fireball"
puts "3. Tremor"
prompt; magic = gets.chomp
if magic == "1"
Spells.heal(player)
elsif magic == "2"
Spells.fireball(player, monster)
elsif magic == "3"
Spells.tremor(player, monster)
else
magic
end
elsif player.maglevel == 2
puts "1. Greater Heal"
puts "2. Firestorm"
puts "3. Earthquake"
prompt; magic = gets.chomp
if magic == "1"
Spells.greaterheal(player)
elsif magic == "2"
Spells.firestorm(player, monster)
elsif magic == "3"
Spells.earthquake(player, monster)
else
magic
end
else
end
end
end
end
As you can see, when the player chooses to cast a spell it calls out to a Spells class, which I have in spells.rb. Here is a snippet from that code:
require_relative 'room'
require_relative 'player'
require_relative 'monster'
module KillShit
class Spells
attr_accessor :player, :monster
#Checking if user has enough MP to cast spell
def self.mp_check(req, player)
req_mp = req
if player.mp < req_mp
puts "#{player.name} doesn't have enough MP!"
action
else
end
end
def self.heal(player)
req_mp = 3
Spells.mp_check(req_mp, player)
player.mp -= req_mp
amt = rand(3..10)
player.hp += amt
puts "#{player.name} has been healed by #{amt} HP!"
Room.action
end
end
end
The problem is in the "Room.action" call at the end of the self.heal method (or just "action" as was my first attempt, as can be seen in the self.mp_check method). When it is hit, the following error is received:
spells.rb:27:in `heal': undefined method `action' for KillShit::Room:Class (NoMethodError)
I thought that it might've been because I needed to define "action" as a class method with "self." but that isn't doing it. I also thought that it's because I'm trying to call "action" from the Class itself as opposed to the instance of the class with which I am working, and so I should call .action but I can't figure out how to do so.
Any ideas? What should I be reading up on to better understand the concept behind what's happening here?
Thanks!
You Should call the method Room#action like:
KillShit::Room.action
Just after a brief look - you did not defined class but instance method action.
Class methods need to by referenced with keyword self or class name directly, so you would need fix the definition this way:
module KillShit
class Room
…
def self.action
…
end
…
end
end
Updated answer:
module KillShit
class Room
def self.action
puts 'action in Room !'
end
end
class Spells
def self.heal(player)
Room.action
end
end
end
KillShit::Spells.heal('Wallace')
# action in Room !

Undefined local variable or method 'user_response'

I've read my code up and down for about 30 mins now. I can't for the life of me see where user_response is undefined. I'm very new to coding so I don't know how much of the code would be appropriate to paste in here. I figure that launch and get_action are essential but the rest couldn't hurt?
error => rb:32:in `launch!': undefined local variable or method `user_response' for
<Guide:0x007fb019984718> (NameError)
class Guide
class Config
##actions = ['list', 'find', 'add', 'quit']
def self.actions
##actions
end
end
def initialize(path=nil)
# locate the restaurant text file at path
Restaurant.filepath = path
if Restaurant.file_usable?
puts "Found restaurant file."
# or IF a new text file can't be created, create a new file
elsif Restaurant.create_file
puts "Created restaurant file."
# exit if create fails
else
puts "Exiting"
exit!
end
end
def launch! #! means that it is a strong powerful method!
introduction
# action loop
result = nil
until result == :quit
action = get_action
result = do_action(user_response)
end
conclusion
end
def get_action
action = nil
# Keep asking for user input until we get a valid action
until Guide::Config.actions.include?(action)
puts "Actions: " + Guide::Config.actions.join(", ") if action
print "> "
user_response = gets.chomp
action = user_response.downcase.strip
end
return action
end
def do_action(action)
case action
when 'list'
puts "Listing..."
when 'find'
puts "Finding..."
when 'add'
puts "Adding..."
when 'quit'
return :quit
else puts " I don't understand that command."
end
end
def introduction
puts "<<< Welcome to the Food Finder >>>"
puts "This is an interactive guide to help you find the food you crave."
end
def conclusion
puts "<<< Goodbye and Bon Appetit! >>>>"
end
end
I think you want to do this :
def launch! #! means that it is a strong powerful method!
introduction
# action loop
result = nil
until result == :quit
result = do_action(get_action)
end
conclusion
end
The only time you define a variable called user_response is in your get_action method.
The way you define it there makes it a local variable and it will not be accessible from anywhere but inside the get_action method.

Resources