Having trouble interpolating user input into another method - ruby

Im scraping data from espn.com of all golden state warriors players and want to show each attribute of every player. Such as their height position, salary and college. Once I give the user the ability to pick the player they would like to know more about. I would like puts out that information for the user to see but I see an error in the process.
This is the code I have written.
attr_accessor :player, :continue
def intialize
#continue = true
end
def call
start
#skips over while loop
#while #continue
list_players
menu
show_player if #continue == true && #player != nil
#end
goodbye
end
def start
puts "Welcome to the Golden State Roster 'The Home Of The Splash Brothers'"
end
def goodbye
puts "See you next time for any players updates!"
end
def list_players
Player.all.clear
Scraper.scrape_page
Player.all.each.with_index(1) do |player, index|
puts "#{index}. #{player.name}"
end
end
def menu
input = ""
while input != "exit"
puts "Choose any player you wish by the 'number', type 'list' to reshow list or type 'exit' when finished."
input = gets.strip.downcase
if input.to_i > 0
the_player = #player[input.to_i - 1]
puts "#{the_player.name} plays #{the_player.position} for the Golden State Warriors. He is #{the_player.age} years old and #{the_player.height} tall. He comes in weighing #{the_player.weight}. #{the_player.name} graduated from #{the_player.college} and makes honest living of #{the_player.salary} dollars per year."
"Select another player by typing 'yes' or press 'exit' when you're ready to leave."
binding.pry
elsif input == "exit"
#continue = false
# elsif input.to_i.between?(1, Player.all.length)
# #player = Player.all[input.to_i - 1]
elsif input == "list"
list_players
else
puts "Error, Choose any player you wish by the number or type 'exit' when finished."
end
end
end ```
It breaks at "the_player"
```This is the error
```Welcome to the Golden State Roster 'The Home Of The Splash Brothers'
1. Jordan Bell
2. Andrew Bogut
3. Quinn Cook
4. DeMarcus Cousins
5. Stephen Curry
6. Marcus Derrickson
7. Kevin Durant
8. Jacob Evans
9. Draymond Green
10. Andre Iguodala
11. Jonas Jerebko
12. Damian Jones
13. Damion Lee
14. Shaun Livingston
15. Kevon Looney
16. Alfonzo McKinnie
17. Klay Thompson
Choose any player you wish by the 'number', type 'list' to reshow list or type 'exit' when finished.
4
Traceback (most recent call last):
2: from ./bin/ballers:6:in `<main>'
1: from /home/Thisforbliss/Development/project/lib/project/cli.rb:17:in`call'
/home/Thisforbliss/Development/project/lib/project/cli.rb:45:in `menu': undefined method `[]' for nil:NilClass (NoMethodError)```

Related

stuck on a multiple choice game on ruby

I am just starting with ruby and tried to create a multiple choice game. I can't seem to see where i get something wrong which makes it so that it either repeats the generic line for a room instead of showing the resulting option.
just for info, the options in the hall are either "north", "look" or "quit"
then in the study, options are "look", "look at desk", "south", "quit", "enter combination 2451"
code below:
def hall_begin
#first line you see
puts "you can either look around or move north"
gets.chomp
end
def look_hall
# first option to look around in the hall
puts "You are standing in a hall with a marble floor. You see a door."
hall_begin
end
def onwards_study
# second option to go forwards into the next room from the hall
puts "You are in the study, you can either look around of move back south"
gets.chomp
end
def back_to_hall
# moving back into the hall from the study
puts "You are back in the hall, either look around or go north"
gets.chomp
end
def look_study
# looking around the study to find the desk and safe
puts "You are in a warm and cosy study. You see a safe. You see a desk."
onwards_study
end
def study_desk
# looking on the study desk to find the combination
puts "You see a piece of paper that reads, The combination is 2451."
onwards_study
end
def study_safe
# if you open the safe with combination
puts "You see some diamonds in the safe, pick them up and make your escape"
end
def first_choice
# all the choices whilst in the hall
while true
direction_1 = hall_begin
if direction_1 == "look"
look_hall
elsif direction_1 == "north"
onwards_study
elsif direction_1 == "quit"
break
end
end
end
while true
# start of the game
first_choice
while true
# all choices you face whilst in the study
direction_2 = onwards_study
if direction_2 == "look"
look_study
elsif direction_2 == "south"
back_to_hall
elsif direction_2 == "look at desk"
study_desk
elsif direction_2 == "enter combination 2451"
study_safe
break
elsif direction_2 == "quit"
break
end
break
end
end
This seems to be a beginner level program. In case if you haven't studied the functions yet. This simple code can help you.
won_condition = false
breaking_condition = false
puts "You are standing in the hall, either look around or go north"
while true
if won_condition == true
break
end
if breaking_condition == true
break
end
user_input = gets.chomp
if user_input == 'quit'
break
elsif user_input == "look"
puts "You are standing in a hall with a marble floor. You see a door."
elsif user_input == 'north'
puts "You are in the study, you can either look around or move back south"
while true
study_input = gets.chomp
if study_input == 'look'
puts "You are in a warm and cosy study. You see a safe. You see a desk."
elsif study_input == 'look at desk'
puts "You see a piece of paper that reads, The combination is 2451."
elsif study_input == 'south'
puts "You are standing in the hall, either look around or go north"
break
elsif study_input == '2451'
puts "You see some diamonds in the safe, pick them up and make your escape."
won_condition = true
break
elsif study_input == 'quit'
breaking_condition = true
break
end
end
end
end
As Chandan pointed out, having this many loops is unnecessary. I think what you are after can be achieved by having one main loop that gets the user input and then handles it.
I don't want to make too complicated suggestions to begin with since you are just starting out, but I think keeping track of a "current_room" variable is helpful (which can later transition into coordinates on a 2D room array or something).
Too give you a few examples, this is how something similar could be achieved.
def describe_room(current_room)
if current_room == "hall"
puts "You are standing in the hall, either look around or go north"
elsif current_room == "study"
puts "You are in the study, you can either look around of move back south"
end
end
def examine_room(current_room)
if current_room == "hall"
puts "You are standing in a hall with a marble floor. You see a door."
elsif current_room == "study"
puts "You are in a warm and cosy study. You see a safe. You see a desk."
end
end
def move(current_room, direction)
if current_room == "hall" and direction == "north"
"study"
elsif current_room == "study" and direction == "south"
"hall"
else
puts "You cannot go that way"
current_room
end
end
def hall_commands(command)
# No hall specific commands at this points
puts "Unknown command"
# Return "hall" since we are never moving anywhere else
"hall"
end
def study_commands(command)
if command == "look at desk"
puts "You see a piece of paper that reads, The combination is 2451."
elsif command == "enter combination 2451"
puts "You see some diamonds in the safe, pick them up and make your escape"
return nil # Use explicit return statement to avoid default return at the end of the method
else
puts "Unknown command"
end
# Return "study" as the default
"study"
end
# Starting position
current_room = "hall"
while true
break if current_room == nil
# Start each loop by a brief description of the current room.
describe_room(current_room)
# Get the user input in the main loop
command = gets.chomp
# Check for global commands (i.e. movements, look, etc.) first
# and then move on to check for room specific commands.
if command.in?(["north", "east", "south", "west"])
current_room = move(current_room, command)
elsif command == "look"
examine_room(current_room)
elsif command == "quit"
break
elsif current_room == "hall"
current_room = hall_commands(command)
elsif current_room == "study"
current_room = study_commands(command)
else
puts "Unknown command"
end
end
Basically I have simplified it into one loop as mentioned earlier, then split up the "global commands" that could be used regardless which room your are in, and the "room specific commands" that only apply in certain rooms.
I hope this helps you getting into Ruby. When you feel more comfortable, I would recommend looking into case/when statements as an alternative to if/elsif/else statements and also Arrays to keep track of rooms and positions.

Undefined Method "attack" for Player::Class

So I am trying to make a text based game with classes for an assignment. I have worked for 2 hours and could not find what the problem is.
class Rankuun
attr_accessor :rankuun_damage, :rankuun_health
def initialize
rankuun_health = 200
rankuun_damage = 100
end
def monolouge
puts 'Rankuun: "So, I see that you have lived this long. I am suprised.'
puts "Not a single libing creature has lived for this long inside my dungeon."
puts "But it's time that your endless slaughter of my brethren are halted."
puts "Now face what true fear really is!"
puts "Hoc vanitas est, et non est fere ut serves!"
puts "You see a mystical aura rise around Rankuun, and hear the shouts of agony"
puts "Rankuun has grown twice in size, and has taken the form of some kind of lich"
puts 'Rankuun: WELCOME TO DIE!"'
end
end
class Player
attr_accessor :health, :gold
def initialize
health = 100
money = 200
puts "Health: #{health}"
puts "Gold: #{money}"
end
def attack
puts "You attack the monster!"
hitmiss = 1
if hitmiss == 1
dmg = rand(5..10)
puts "You hit the monster, and do #{dmg} damage!"
monster_health = monster_health - dmg
elsif hitmiss == 2
puts "You missed!"
end
end
def guard
puts "You attempt to defend yourself"
guard = rand(1..2)
if guard == 1
counter = rand(5..10)
puts "You block the damage, and counterstrike for #{counter} damage"
monster_health = monster_health - counter
elsif guard == 2
monster_counter = rand(1..5)
puts "You try to guard, but the enemy hits harder than you expected, and you get dealt #{monster_counter}"
health = health = monster_counter
end
end
def loot
puts "You search the room and find:"
loot_item = rand (2..3)
if loot_item == 2
puts "You find some gold!"
money = money + 50
puts "Health: #{health}"
puts "Gold: #{money}"
elsif loot_item == 3
puts "You find a curious potion that seems to heal you"
health = health + 50
puts "Health: #{health}"
puts "Gold: #{money}"
end
end
def encounter
encounter = rand(1..2)
if encounter == 1
puts "A monster confronts you!"
monster = Monster.new
elsif encounter == 2
puts "There appears to be no monsters in this room"
end
end
end
class Monster
attr_accessor :monster_health, :monster_damage
def initialize
monster_health = 50
monster_damage = 10
end
def monster_attack
puts "The monster attacks you!"
end
end
puts "There has been a saying in your town for as long as you can remember:"
puts "Ne pas entrer dans le Donjon De Rankuun"
puts 'It means: "Do not enter The Dungeon of Rankuun"'
puts "Many adventurers died inside, and the only living creature in there is the man named Rankuun"
puts "He has great power over the Dungeon, reviving the dead and casting black magic"
puts "You have been selected by the village to go into the Dungeon and exterminate Rankuun"
puts "You have been given a sword, a shield, and some gold. Now you must enter:"
puts "T H E D U N G E O N O F R A N K U U N!"
puts ""
puts ""
player = Player.new
player.encounter
room1 = gets.chomp
if room1 == "attack"
player.attack
elsif room1 == "loot"
player.loot
end
It would be great if this problem were solved. Thanks for responding and aiding me in my assignment.
Welcome to the exciting world of object-oriented design. Many adventurers died inside.
I think you may have a small misunderstanding about the difference between classes and instances. If so, I strongly advise you to read about it before continuing.
You created a new instance of Player when you called Player.new. Your first mistake was not putting it in a variable.
Try something like this:
my_player = Player.new
Secondly, you are trying to call encounter on the Player class, while you should call it on the new instance.
my_player.encounter
You do the same thing inside the Monster class with Player.attack.
I could tell you how to solve each of these problems individually, but I think you would benefit more from redesigning some parts of the project to be easier to change in the future. Hopefully, most of the problems will resolve themselves along the way.
Generally speaking, the shorter a method is, the better. When you tell the Player to attack, that is all it should do. Instead, it does all sorts of things, including getting the monster to attack!
It suddenly becomes apparent that the two classes have quite a lot in common: they both attack; they both take damage, and they both die. It's time to make a superclass. (If you are not familiar with how classical inheritance works, you should learn - this truly is the perfect use case for it.)
class Character
attr_accessor :health
def attack damageable, damage
damageable.take_damage damage
end
def take_damage damage
health -= damage # Equivenent to health = health - damage
potential_death
end
def potential_death
if dead?
die
end
end
def dead?
health <= 0 # With random damage, it could be less than 0.
end
def die # overruled by subclass
end
end
The greatest advantage to doing it like this is you only have to write the code in one place, and it will work for everything. If you change your mind about a design decision, you can change it in one place and know that everything will be adjusted.
You can make a subclass similar to this:
class Monster < Character
def die
super # Call the copy of die in Character, in case it contains something important
reward killer
puts "You kill the monster..."
end
def reward rewardable
rewardable.gain_money 30
end
end
class Player < Character
def die
super # Call the copy of die in Character, in case it contains something important
puts "You died..."
game.over
end
end
(These are only examples; they are not as complete as the code you already have.)
Do you see how each method only does one thing? If you apply that principle to everything you write, it will become much easier to reuse bits and pieces.
I hope this has been useful. If you decide to stick with what you have and just fix the errors, just say so in the comments, and I'll help you with that.
Good luck!

Ruby script crashes [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have the following code that I am trying to learn from this Ruby book and it keeps crashing, I have spent hours trying to fix. If anyone has any clue why this is happening please let me know. I am not enjoying Ruby
# Define custom classes ---------------------------------------------------
#Define a class representing the console window
class Screen
def cls #Define a method that clears the display area
puts ("\n" * 25) #Scroll the screen 25 times
puts "\a" #Make a little noise to get the player's attention
end
def pause #Define a method that pauses the display area
STDIN.gets #Execute the STDIN class's gets method to pause script
#execution until the player presses the enter key
end
end
#Define a class representing the Ruby Number Guessing Game
class Game
#This method displays the game's opening screen
def display_greeting
Console_Screen.cls #Clear the display area
#Display welcome message
print "\t\t Welcome to the Ruby Number Guessing Game!" +
"\n\n\n\n\n\n\n\n\n\n\n\n\n\nPress Enter to " +
"continue."
Console_Screen.pause #Pause the game
end
#Define a method to be used to present game instructions
def display_instructions
Console_Screen.cls #Clear the display area
puts "INSTRUCTIONS:\n\n" #Display a heading
#Display the game's instructions
puts "This game randomly generates a number from 1 to 100 and"
puts "challenges you to identify it in as few guesses as possible."
puts "After each guess, the game will analyze your input and provide"
puts "you with feedback. You may take as many turns as you need in"
puts "order to guess the game's secret number.\n\n"
puts "Game will stop if you have guessed 10 times.\n\n\n"
puts "Good luck!\n\n\n\n\n\n\n\n\n"
print "Press Enter to continue."
Console_Screen.pause #Pause the game
end
#Define a method that generates the game's secret number
def generate_number
#Generate and return a random number between 1 and 100
return randomNo = 1 + rand(1000)
end
#Define a method to be used control game play
def play_game
#Call on the generate_number method in order to get a random number
number = generate_number
#Loop until the player inputs a valid answer
loop do
Console_Screen.cls #Clear the display area
if answer == "c"
print "Game count : #{$gameCount}"
end
#Prompt the player to make a guess
print "\nEnter your guess and press the Enter key: "
reply = STDIN.gets #Collect the player's answer
reply.chop! #Remove the end of line character
reply = reply.to_i #Convert the player's guess to an integer
#Validate the player's input only allowing guesses between 1 and 100
if reply < 1 or reply > 1000 then
redo #Redo the current iteration of the loop
end
#Analyze the player's guess to determine if it is correct
if reply == number then #The player's guess was correct
Console_Screen.cls #Clear the display area
print "You have guessed the number! Press enter to continue."
Console_Screen.pause #Pause the game
break #Exit loop
elsif reply < number then #The player's guess was too low
Console_Screen.cls #Clear the display area
print "Your guess is too low! ( valid range: 1 - 1000) Press Enter to continue."
Console_Screen.pause #Pause the game
elsif reply > number then #The player's guess was too high
Console_Screen.cls #Clear the display area
print "Your guess is too high! ( valid range: 1 - 1000) Press Enter to continue."
Console_Screen.pause #Pause the game
end
$noOfGuesses +=1
break if $noOfGuesses > 10
end
end
#This method displays the information about the Ruby Number Guessing Game
def display_credits
Console_Screen.cls #Clear the display area
#Thank the player and display game information
puts "\t\tThank you playing the Ruby Number Guessing Game.\n\n\n\n"
puts "\n\t\t\t Developed by Jerry Lee Ford, Jr.\n\n"
puts "\t\t\t\t Copyright 2010\n\n"
puts "\t\t\tURL: http://www.tech-publishing.com\n\n\n\n\n\n\n\n\n\n"
end
end
# Main Script Logic -------------------------------------------------------
Console_Screen = Screen.new #Instantiate a new Screen object
SQ = Game.new #Instantiate a new Game object
#Execute the Game class's display_greeting method
SQ.display_greeting
answer = ""
$gameCount = 0
$noOfGuesses = 0
$totalNoOfGuesses = 0
$avgNoOfGuesses = 0
#Loop until the player enters y or n and do not accept any other input
loop do
Console_Screen.cls #Clear the display area
#Prompt the player for permission to start the game
print "Are you ready to play the Ruby Number Guessing Game? (y/n): "
answer = STDIN.gets #Collect the player's response
answer.chop! #Remove any extra characters appended to the string
#Terminate the loop if valid input was provided
break if answer == "y" || answer == "n" || answer == "c" #Exit loop
end
#Analyze the player's input
if answer == "n" #See if the player elected not to take the game
Console_Screen.cls #Clear the display area
#Invite the player to return and play the game some other time
puts "Okay, perhaps another time.\n\n"
else #The player wants to play the game
#Execute the Game class's display_instructions method
SQ.display_instructions
loop do
$gameCount+=1
#Execute the Game class's play_game method
SQ.play_game
$totalNoOfGuesses = $noOfGuesses * $gameCount
$avgNoOfGuesses = $totalNoOfGuesses / $noOfGuesses
print "The total number of guesses was #{$totalNoOfGuesses}"
print "The average number of guesses was #{$avgNoOfGuesses}"
Console_Screen.pause #Pause the game
print "Press Enter to continue"
Console_Screen.cls #Clear the display area
#Prompt the player for permission start a new round of play
print "Would you like to play again? (y/n): "
playAgain = STDIN.gets #Collect the player's response
playAgain.chop! #Remove any extra characters appended to the string
break if playAgain == "n" #Exit loop
end
#Call upon the Game class's determine_credits method in order to thank
#the player for playing the game and to display game information
SQ.display_credits
end
When running the code it says:
script-not-working.rb:74:in `block in play_game': undefined local variable or method `answer' for #<Game:0x0000000180f9c0> (NameError)
from script-not-working.rb:70:in `loop'
from script-not-working.rb:70:in `play_game'
from script-not-working.rb:181:in `block in <main>'
from script-not-working.rb:176:in `loop'
from script-not-working.rb:176:in `<main>'
So one solution could be make the variable answer global, adding $ before all answer variables it should look like : $answer. The code use other global variables so it could be fine for this. There are better practices than these but for this code it works fine. After that the game is running correctly. But it seems that has some other problems for evaluating the number. this should be another fix. maybe for another question. So investigate thought your code.
Here is the result of the code making answer global using $answer:
#Define a class representing the console window
class Screen
def cls #Define a method that clears the display area
puts ("\n" * 25) #Scroll the screen 25 times
puts "\a" #Make a little noise to get the player's attention
end
def pause #Define a method that pauses the display area
STDIN.gets #Execute the STDIN class's gets method to pause script
#execution until the player presses the enter key
end
end
#Define a class representing the Ruby Number Guessing Game
class Game
#This method displays the game's opening screen
def display_greeting
Console_Screen.cls #Clear the display area
#Display welcome message
print "\t\t Welcome to the Ruby Number Guessing Game!" +
"\n\n\n\n\n\n\n\n\n\n\n\n\n\nPress Enter to " +
"continue."
Console_Screen.pause #Pause the game
end
#Define a method to be used to present game instructions
def display_instructions
Console_Screen.cls #Clear the display area
puts "INSTRUCTIONS:\n\n" #Display a heading
#Display the game's instructions
puts "This game randomly generates a number from 1 to 100 and"
puts "challenges you to identify it in as few guesses as possible."
puts "After each guess, the game will analyze your input and provide"
puts "you with feedback. You may take as many turns as you need in"
puts "order to guess the game's secret number.\n\n"
puts "Game will stop if you have guessed 10 times.\n\n\n"
puts "Good luck!\n\n\n\n\n\n\n\n\n"
print "Press Enter to continue."
Console_Screen.pause #Pause the game
end
#Define a method that generates the game's secret number
def generate_number
#Generate and return a random number between 1 and 100
return randomNo = 1 + rand(1000)
end
#Define a method to be used control game play
def play_game
#Call on the generate_number method in order to get a random number
number = generate_number
#Loop until the player inputs a valid answer
loop do
Console_Screen.cls #Clear the display area
if $answer == "c"
print "Game count : #{$gameCount}"
end
#Prompt the player to make a guess
print "\nEnter your guess and press the Enter key: "
reply = STDIN.gets #Collect the player's answer
reply.chop! #Remove the end of line character
reply = reply.to_i #Convert the player's guess to an integer
#Validate the player's input only allowing guesses between 1 and 100
if reply < 1 or reply > 1000 then
redo #Redo the current iteration of the loop
end
#Analyze the player's guess to determine if it is correct
if reply == number then #The player's guess was correct
Console_Screen.cls #Clear the display area
print "You have guessed the number! Press enter to continue."
Console_Screen.pause #Pause the game
break #Exit loop
elsif reply < number then #The player's guess was too low
Console_Screen.cls #Clear the display area
print "Your guess is too low! ( valid range: 1 - 1000) Press Enter to continue."
Console_Screen.pause #Pause the game
elsif reply > number then #The player's guess was too high
Console_Screen.cls #Clear the display area
print "Your guess is too high! ( valid range: 1 - 1000) Press Enter to continue."
Console_Screen.pause #Pause the game
end
$noOfGuesses +=1
break if $noOfGuesses > 10
end
end
#This method displays the information about the Ruby Number Guessing Game
def display_credits
Console_Screen.cls #Clear the display area
#Thank the player and display game information
puts "\t\tThank you playing the Ruby Number Guessing Game.\n\n\n\n"
puts "\n\t\t\t Developed by Jerry Lee Ford, Jr.\n\n"
puts "\t\t\t\t Copyright 2010\n\n"
puts "\t\t\tURL: http://www.tech-publishing.com\n\n\n\n\n\n\n\n\n\n"
end
end
# Main Script Logic -------------------------------------------------------
Console_Screen = Screen.new #Instantiate a new Screen object
SQ = Game.new #Instantiate a new Game object
#Execute the Game class's display_greeting method
SQ.display_greeting
$answer = ""
$gameCount = 0
$noOfGuesses = 0
$totalNoOfGuesses = 0
$avgNoOfGuesses = 0
#Loop until the player enters y or n and do not accept any other input
loop do
Console_Screen.cls #Clear the display area
#Prompt the player for permission to start the game
print "Are you ready to play the Ruby Number Guessing Game? (y/n): "
$answer = STDIN.gets #Collect the player's response
$answer.chop! #Remove any extra characters appended to the string
#Terminate the loop if valid input was provided
break if $answer == "y" || $answer == "n" || $answer == "c" #Exit loop
end
#Analyze the player's input
if $answer == "n" #See if the player elected not to take the game
Console_Screen.cls #Clear the display area
#Invite the player to return and play the game some other time
puts "Okay, perhaps another time.\n\n"
else #The player wants to play the game
#Execute the Game class's display_instructions method
SQ.display_instructions
loop do
$gameCount+=1
#Execute the Game class's play_game method
SQ.play_game
$totalNoOfGuesses = $noOfGuesses * $gameCount
$avgNoOfGuesses = $totalNoOfGuesses / $noOfGuesses
print "The total number of guesses was #{$totalNoOfGuesses}"
print "The average number of guesses was #{$avgNoOfGuesses}"
Console_Screen.pause #Pause the game
print "Press Enter to continue"
Console_Screen.cls #Clear the display area
#Prompt the player for permission start a new round of play
print "Would you like to play again? (y/n): "
playAgain = STDIN.gets #Collect the player's response
playAgain.chop! #Remove any extra characters appended to the string
break if playAgain == "n" #Exit loop
end
#Call upon the Game class's determine_credits method in order to thank
#the player for playing the game and to display game information
SQ.display_credits
end
The problem is in your play_game method within the Game class, there is no where the variable answer is defined i.e assigned to a value, even an empty value. So i edited your script such that the method takes answer as an arguement, when the the method is called later here, you pass the answer expected from the console as its argument.
SQ.play_game answer
Here is the edited script below
#Define a class representing the console window
class Screen
def cls #Define a method that clears the display area
puts ("\n" * 25) #Scroll the screen 25 times
puts "\a" #Make a little noise to get the player's attention
end
def pause #Define a method that pauses the display area
STDIN.gets #Execute the STDIN class's gets method to pause script
#execution until the player presses the enter key
end
end
#Define a class representing the Ruby Number Guessing Game
class Game
#This method displays the game's opening screen
def display_greeting
Console_Screen.cls #Clear the display area
#Display welcome message
print "\t\t Welcome to the Ruby Number Guessing Game!" +
"\n\n\n\n\n\n\n\n\n\n\n\n\n\nPress Enter to " +
"continue."
Console_Screen.pause #Pause the game
end
#Define a method to be used to present game instructions
def display_instructions
Console_Screen.cls #Clear the display area
puts "INSTRUCTIONS:\n\n" #Display a heading
#Display the game's instructions
puts "This game randomly generates a number from 1 to 100 and"
puts "challenges you to identify it in as few guesses as possible."
puts "After each guess, the game will analyze your input and provide"
puts "you with feedback. You may take as many turns as you need in"
puts "order to guess the game's secret number.\n\n"
puts "Game will stop if you have guessed 10 times.\n\n\n"
puts "Good luck!\n\n\n\n\n\n\n\n\n"
print "Press Enter to continue."
Console_Screen.pause #Pause the game
end
#Define a method that generates the game's secret number
def generate_number
#Generate and return a random number between 1 and 100
return randomNo = 1 + rand(1000)
end
#Define a method to be used control game play
def play_game answer
#Call on the generate_number method in order to get a random number
number = generate_number
#Loop until the player inputs a valid answer
loop do
Console_Screen.cls #Clear the display area
if answer == "c"
print "Game count : #{$gameCount}"
end
#Prompt the player to make a guess
print "\nEnter your guess and press the Enter key: "
reply = STDIN.gets #Collect the player's answer
reply.chop! #Remove the end of line character
reply = reply.to_i #Convert the player's guess to an integer
#Validate the player's input only allowing guesses between 1 and 100
if reply < 1 or reply > 1000 then
redo #Redo the current iteration of the loop
end
#Analyze the player's guess to determine if it is correct
if reply == number then #The player's guess was correct
Console_Screen.cls #Clear the display area
print "You have guessed the number! Press enter to continue."
Console_Screen.pause #Pause the game
break #Exit loop
elsif reply < number then #The player's guess was too low
Console_Screen.cls #Clear the display area
print "Your guess is too low! ( valid range: 1 - 1000) Press Enter to continue."
Console_Screen.pause #Pause the game
elsif reply > number then #The player's guess was too high
Console_Screen.cls #Clear the display area
print "Your guess is too high! ( valid range: 1 - 1000) Press Enter to continue."
Console_Screen.pause #Pause the game
end
$noOfGuesses +=1
break if $noOfGuesses > 10
end
end
#This method displays the information about the Ruby Number Guessing Game
def display_credits
Console_Screen.cls #Clear the display area
#Thank the player and display game information
puts "\t\tThank you playing the Ruby Number Guessing Game.\n\n\n\n"
puts "\n\t\t\t Developed by Jerry Lee Ford, Jr.\n\n"
puts "\t\t\t\t Copyright 2010\n\n"
puts "\t\t\tURL: http://www.tech-publishing.com\n\n\n\n\n\n\n\n\n\n"
end
end
# Main Script Logic -------------------------------------------------------
Console_Screen = Screen.new #Instantiate a new Screen object
SQ = Game.new #Instantiate a new Game object
#Execute the Game class's display_greeting method
SQ.display_greeting
answer = ""
$gameCount = 0
$noOfGuesses = 0
$totalNoOfGuesses = 0
$avgNoOfGuesses = 0
#Loop until the player enters y or n and do not accept any other input
loop do
Console_Screen.cls #Clear the display area
#Prompt the player for permission to start the game
print "Are you ready to play the Ruby Number Guessing Game? (y/n): "
answer = STDIN.gets #Collect the player's response
answer.chop! #Remove any extra characters appended to the string
#Terminate the loop if valid input was provided
break if answer == "y" || answer == "n" || answer == "c" #Exit loop
end
#Analyze the player's input
if answer == "n" #See if the player elected not to take the game
Console_Screen.cls #Clear the display area
#Invite the player to return and play the game some other time
puts "Okay, perhaps another time.\n\n"
else #The player wants to play the game
#Execute the Game class's display_instructions method
SQ.display_instructions
loop do
$gameCount+=1
#Execute the Game class's play_game method
SQ.play_game answer
$totalNoOfGuesses = $noOfGuesses * $gameCount
$avgNoOfGuesses = $totalNoOfGuesses / $noOfGuesses
print "The total number of guesses was #{$totalNoOfGuesses}"
print "The average number of guesses was #{$avgNoOfGuesses}"
Console_Screen.pause #Pause the game
print "Press Enter to continue"
Console_Screen.cls #Clear the display area
#Prompt the player for permission start a new round of play
print "Would you like to play again? (y/n): "
playAgain = STDIN.gets #Collect the player's response
playAgain.chop! #Remove any extra characters appended to the string
break if playAgain == "n" #Exit loop
end
#Call upon the Game class's determine_credits method in order to thank
#the player for playing the game and to display game information
SQ.display_credits
end

Testing program that runs with user input from the console

I'm writing tests for a system that models an elevator. For example, I want to test that the elevator can change direction and that it can move to a specified floor.
I have the following methods:
def initialize
#current_floor = 0
#requested_floor = 0
end
def get_value
gets.chomp
end
def arrival
print "Enter floor number: "
#requested_floor = get_value
# only proceed if user entered an integer
if validate_floor_number(#requested_floor)
#requested_floor = #requested_floor.to_i
move
else
arrival
end
end
def move
msg = ""
#current_floor < #requested_floor ? msg = "Going Up!" : msg = "Going Down"
puts msg
#current_floor = #requested_floor
next_move
end
def next_move
puts "Do you want to go to another floor? Y/N"
another_floor = (get_value).upcase
another_floor == 'N' ? final_destination : arrival
end
I start the program by calling Elevator.new.arrival. To check that the elevator has changed directions, I need to store the value of #current_floor in a temporary variable then check it's value has changed after move has been called.
I am testing input from the console using an IO pipe thanks to the answers in this question, but I'm not sure how to apply that knowledge to user interaction that's part of a method.
How can I simulate the program running from the start (Elevator.new.arrival) through the move method and stop it there so I can check the value of #current_floor - all of this without running the program itself and using the IO pipe to simulate user interaction?
I have a feeling that I might have gone about the design of the program in the wrong way. If anyone can even point me in the right direction towards solving this problem I'd appreciate it.
Edit
According to the suggestions from Wand Maker I've written a test as follows:
describe "checks that the elevator can change directions" do
before do
moves = [3, 'Y', 5, 'Y', 2, 'Y', 7, 'N']
def get_value; moves.next end
end
it "should stop on floor 7" do
Elevator.new.arrival
assert_equal(#current_floor, 7)
end
end
Unfortunately, when I run my test file, the program still runs and prompts for user input. Maybe I'm calling arrival incorrectly but I can't think of another way to do it.
As demonstrated in this answer, you can override getvalue to feed in the user input.
Here is complete code that works without actually using gets. I had to add couple of missing methods - validate_floor_number and final_destination:
require 'minitest/autorun'
class Elevator
attr_accessor :current_floor
def initialize
#current_floor = 0
#requested_floor = 0
##last_floor = false
end
def get_value
gets.chomp
end
def validate_floor_number(v)
v.to_i rescue false
end
def arrival
print "Enter floor number: "
#requested_floor = get_value
# only proceed if user entered an integer
if validate_floor_number(#requested_floor)
#requested_floor = #requested_floor.to_i
move
else
arrival
end
end
def move
msg = ""
#current_floor < #requested_floor ? msg = "Going Up!" : msg = "Going Down"
puts msg
#current_floor = #requested_floor
next_move
end
def final_destination
puts "Reached your floor"
end
def next_move
puts "Do you want to go to another floor? Y/N"
another_floor = (get_value).upcase
another_floor == 'N' ? final_destination : arrival
end
end
describe "checks that the elevator can change directions" do
before do
class Elevator
##moves = [3, 'Y', 5, 'Y', 2, 'Y', 7, 'N'].each
def get_value; ##moves.next end
end
end
it "should stop on floor 7" do
e = Elevator.new
e.arrival
assert_equal(e.current_floor, 7)
end
end
Output of above program:
Run options: --seed 2561
# Running:
Enter floor number: Going Up!
Do you want to go to another floor? Y/N
Enter floor number: Going Up!
Do you want to go to another floor? Y/N
Enter floor number: Going Down
Do you want to go to another floor? Y/N
Enter floor number: Going Up!
Do you want to go to another floor? Y/N
Reached your floor
.
Finished in 0.001334s, 749.4982 runs/s, 749.4982 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
[Finished in 0.3s]
This answers this part of your question: How can I simulate the program running from the start (Elevator.new.arrival) through the move method and stop it there so I can check the value of #current_floor?
Install byebug: gem install byebug
Require it in your file: require 'byebug'
Add byebug command where you want to stop your program, for example at start of move (see code at the end of the post)
You are dropped in a shell and can examine everything, for example #current_floor by typing it out, or the instance by using self
If you want to continue, hit CTRL+D and the program will continue (with any modification you might have done)
That should help you debugging it.
def move
byebug # <-- program will stop here
msg = ""
#current_floor < #requested_floor ? msg = "Going Up!" : msg = "Going Down"
puts msg
#current_floor = #requested_floor
next_move
end

Ruby: How to exit a function and then return to it

I'm writing a text-adventure game for exercise 36 of Learn Ruby the hard Way: http://ruby.learncodethehardway.org/book/ex36.html
I want to include 'instructions' as an option players can use anytime, but once the player exits the Room() function and enters the intructions() function, I am unsure how to return them to the appropriate room. I can have the instructions() always return player to the start after, but is there a way to get them back to the same location?
Here is a quick example, sorry it's incomplete...I'm still in the middle of building it:
puts
puts <<INTRO
"Welcome to the Cave of Indifference.
It doesn't much care for you. Beware!
There are deadly areas of this cave, but if you seek
it you may find the secret treasure and escape with your life."
INTRO
puts
puts "Type \'Instructions\' at any time for direction"
puts
sword = false
monster = true
treasure = false
def Command()
puts ">>> "
end
def dead(how)
puts how.to_s
puts "PLAYER DEAD!"
Process.exit(0)
end
def instruction()
puts "Rooms will have individual instructions"
puts "but here are some general items."
puts "west, east, north, south: goes that direction"
puts "look: look around the room"
puts "take: to take item or object"
end
def Room1()
puts "You are now at the cave entrance."
puts "You may go west or east. OR exit with your life!"
Command(); choice = gets.chomp()
if choice.downcase == "exit" && treasure = true
puts "Congratulations! You win!"
Process.break
elsif choice.downcase == "exit" && treasure = false
puts "Seriously?! Giving up already?"
puts "Fine. Here is what happens:"
dead("You stumble on your exit from the cave and trip
on a rock. The fall cracks your skull and you bleed
to death. Bye bye!")
elsif choice.downcase.include? "right"
#INPUT
elsif choice.downcase.include? "left"
#INPUT
elsif choice.downcase.include? "instructions"
instructions()
else
"That command makes no sense, try again."
end
end
Room1()
I also assume there are many issues with the code and would be very appreciative of your help, but no worries I am going to keep working on this and make it really fun to play :)
You can give the instruction method the last location.
def instruction(last_room)
#do stuff
last_room.call()
end
You would call that function like so:
instructions(method(:Room1)), where Room1 is the name of the method you want to return to.
The problem you have isn't having instructions return to Room1 (or RoomX). It'll do that without you doing anything special. What you need is something like:
#room = :Room1
while true
send(#room)
end
and then set the variable #room to control which room you're in.
It's not the greatest way in the world to do that, but it'll get you started.

Resources