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

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.

Related

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

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

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

How do I write an rspec Ruby test for this method play_turn?

class Game
def start
#player1 = Player.new("don")
#player2 = Player.new("tum")
end
def player_turn
if #turn.even?
puts "this is #{#player2.name}'s turn"
else
puts "this is #{#player1.name}'s turn"
end
end
end
I think first you are going to have to define the instance variable #turn, and how it is incremented. Additionally I would recommend changing the Game#start to #initialize, the test below assumes this.
Then you can check what is output to stdout.
RSpec.describe Game do
describe "#player_turn" do
context 'when the turn is even' do
let(:game) { Game.new }
it "tells you it is player 2's turn" do
expect do
game.player_turn
end.to output("this is tum's turn\n").to_stdout
end
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 !

Outputting correct object attribute based on user input from an array of objects in Ruby

I have created an array of objects containing information on the Oscars using a text file with all the category names, winners and nominees (winners appear in nominees list as well). I now want to be able to ask a user. Of which category would you like to know the winner? Once the question is asked it would return the answer. I can only get it to work on the last object of the array(best visual effects returns gravity). Can someone explain why this is happening?
class AwardCategory
attr_accessor :winner, :name, :nominees
def initialize(name)
#name = name
#nominees = []
end
end
class Nominee
attr_accessor :name
def initialize(name)
#name = name
end
end
file = File.open('oscar_noms.txt', 'r')
oscars = []
begin
while true do
award_category = AwardCategory.new(file.readline.downcase)
award_category.winner = file.readline.downcase
nominee = Nominee.new(file.readline.downcase)
award_category.nominees << nominee
next_nominee = Nominee.new(file.readline.downcase)
until next_nominee.name == "\n"
award_category.nominees << next_nominee
next_nominee = Nominee.new(file.readline.downcase)
end
oscars << award_category
end
rescue EOFError => e
puts 'rescued'
end
#puts oscars.inspect
#Read input here
puts "What category do you want to know the winner for?"
answer = gets
oscars.each
if answer.downcase == award_category.name
puts award_category.winner
else
puts "That is not a category"
end
That piece of code
puts "What category do you want to know the winner for?"
answer = gets
oscars.each
if answer.downcase == award_category.name
puts award_category.winner
else
puts "That is not a category"
end
Now with correct indentation
puts "What category do you want to know the winner for?"
answer = gets
oscars.each
if answer.downcase == award_category.name
puts award_category.winner
else
puts "That is not a category"
end
Note that the part below oscars.each is not indented, because each needs a do/end block which it will execute once for every element. What you probably want is this
puts "What category do you want to know the winner for?"
answer = gets
oscars.each do |award_category|
if answer.downcase == award_category.name
puts award_category.winner
else
puts "That is not a category"
end
end
Although I suggest you leave off the else, because you will get the message "That is not a category" for every answer that did not match. Also, you should use gets.chomp to remove the newline from the user input and do downcase outside of the each loop. As a last note, some of your variables are poorly named. For example, why should a list of award categories be named oscars? It should be award_categories instead.

Resources