I was wondering if there is a way for me to avoid writing .to_s method on bottles_of_beer variable? (DRY principle)
bottles_of_beer = 99
while bottles_of_beer != 1
beer_left = bottles_of_beer - 1
bottles_of_beer = beer_left
puts bottles_of_beer.to_s + ' bottles of beer on the wall ' + bottles_of_beer.to_s + ' bottles of beer.'
puts 'Take one down and pass it around, ' + beer_left.to_s + ' of beer on the wall.'
end
if bottles_of_beer == 1
puts bottles_of_beer.to_s + ' bottle of beer on the wall, ' + bottles_of_beer.to_s + ' bottle of beer.'
puts 'Take one down and pass it around, no more bottles of beer on the wall.'
end
puts 'No more bottles of beer on the wall, no more bottles of beer.'
puts 'Go to the store and buy some more, 99 bottles of beer on the wall.'
I was wondering if there is a way for me to avoid writing .to_s method on bottles_of_beer variable?
Normally, you would use string interpolation for this, which will use to_s automatically, if necessary:
puts "#{bottles_of_beer} bottles of beer on the wall #{bottles_of_beer} bottles of beer."
By the way, your code would normally be implemented more like this:
puts 99.downto(2).map { |number_of_bottles|
"#{number_of_bottles} bottles of beer on the wall #{number_of_bottles} bottles of beer.
Take one down and pass it around, #{number_of_bottles - 1} of beer on the wall."
}
puts 'Take one down and pass it around, no more bottles of beer on the wall.'
puts 'No more bottles of beer on the wall, no more bottles of beer.'
puts 'Go to the store and buy some more, 99 bottles of beer on the wall.'
Starting from Jorg idea with interpolation but more of a functional approach, just for fun:
def bottles_song(n)
if n.zero?
puts "No more bottles of beer on the wall, no more bottles of beer!\nGo to the store and buy some more, #{n} bottles of beer on the wall.\n"
return
end
puts "#{n} #{ n != 1 ? 'bottles' : 'bottle' } of beer on the wall, #{n} #{ n != 1 ? 'bottles' : 'bottle' } of beer.\nTake one down, pass it around, #{n != 1 ? (n-1) : 'no more'} #{ n != 1 ? 'bottles' : 'bottle' } of beer on the wall!\n"
bottles_song(n-1)
end
Improved readability but it's taking other methods, not really an improvement :)
def bottles_song(n)
if n.zero?
puts "No more bottles of beer on the wall, no more bottles of beer!\nGo to the store and buy some more, #{n} bottles of beer on the wall.\n"
return
end
puts_bottles(n)
bottles_song(n-1)
end
def puts_bottles(n)
puts "#{n} #{ pluralize_bottle(n) } of beer on the wall, #{n} #{ pluralize_bottle(n) } of beer.\nTake one down, pass it around, #{n != 1 ? (n-1) : 'no more'} #{ pluralize_bottle(n-1) } of beer on the wall!\n"
end
def pluralize_bottle(n)
return 'bottles' if n != 1
'bottle'
end
Related
I'm creating a tic tac toe game in the command line using Ruby. I have a method display_board that displays my array as a game board in the command line and when I play the game players can choose between 1-9 and populate the square with their "symbol". I created a method check_square to verify if one of the squares in the grid has already been taken but it's not working properly for me. I start up the game and everything works, up until it asks me to choose my first number on the grid. It immediately responds and tells me the number is already taken even though it's the first move of the game. It asks me to choose another number and on the second try it populates the grid. It does this on every player move. The logic seems to make sense to me and I've been trying to figure it out for an hour but I'm clearly overlooking something. Any pushes in the right direction would be helpful!
class Players
attr_accessor :name, :symbol
def initialize(name, symbol)
#name = name
#symbol = symbol
end
end
class Game
##board = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
##count = 0
def initialize
puts "Tic Tac Toe!"
end
def display_board
puts " #{##board[0]} | #{##board[1]} | #{##board[2]}"
puts seperator = "-----+-----+-----"
puts " #{##board[3]} | #{##board[4]} | #{##board[5]}"
puts seperator
puts " #{##board[6]} | #{##board[7]} | #{##board[8]}"
puts "\n"
end
def game_start
puts "Time for some Tic Tac Toe! Enter your name player 1: \n"
player1 = gets.chomp
puts "Would you like to be X or O #{player1}?"
symbol1 = gets.chomp.upcase
player_one = Players.new(player1, symbol1)
puts "And a name for player 2: \n"
player2 = gets.chomp
symbol2 = player_one.symbol == "X" ? "O" : "X"
player_two = Players.new(player2, symbol2)
puts "\n"
puts "Okay #{player_one.name}, you're up. Make a move."
display_board
make_moves(player_one, player_two)
end
def make_moves(player_one, player_two)
until ##count == 9
puts "Pick a number from the grid above #{player_one.name}"
move = gets.chomp.to_i - 1
check_square(move, player_one, player_two)
##board[move] = player_one.symbol
##count += 1
display_board
puts "Pick a number from the grid above #{player_two.name}"
move = gets.chomp.to_i - 1
check_square(move, player_one, player_two)
##board[move] = player_two.symbol
##count += 1
display_board
end
end
def check_square(move, player_one, player_two)
if ##board[move] == "#{player_one.symbol}" || "#{player_two.symbol}"
puts "That number is taken, pick another!"
move = gets.chomp.to_i - 1
else
return
end
end
end
game = Game.new
game.game_start
The problem you're running into is the conditional logic in your check_square method.
##board[move] == "#{player_one.symbol}" || "#{player_two.symbol}"
This logic is suppose to check if either of the players' symbols is stored on the board at the selected position. However, what it is really doing is checking if player one's symbol is stored on the board at that position and if it's not then the string literal for player two's symbol is being evaluated as truthy. This results in check_square always returning true
Try this instead:
##board[move] == "#{player_one.symbol}" || ##board[move] == "#{player_two.symbol}"
For more idiomatic ruby you can remove the interpolation which isn't necessary since symbol is already a string.
##board[move] == player_one.symbol || ##board[move] == player_two.symbol
Ok i seriously suck at passing method to methods whenever i want to return something from the method. Can you guys explain on how do i go about passing it.
Here's my hash
$choosen_gun = {}
$Weapon = {
:Bazoka => ["Bazoka",5],
:Machine_gun => ["Machine_gun",1000],
:Hand_gun => ["Hand_gun",24,2],
:Double_Hand_gun => ["Double_Hand_gun",24,4],
:Sniper => ["Sniper",12,1],
:Shot_gun => ["Shot_gun",8,2]
}
Here's my code for method Weapon
def Weapon
puts "Now it's time to select your weapon."
puts "Please choose a weapon that is good throughout the game."
puts "Whenever you are shortage of bullets, please reload it."
puts "Please avoid last minute of reloading of weapon."
puts "Now choose your weapon based on your own preferences."
print "\n"
puts "Type 1"
puts "Gun Name: Bazoka"
puts "Description: A powerful gun that is strong with only 5 bullets."
puts "Rating: ★ ★ ★ ★"
num = gets.chomp.to_i
case num
when 1
puts "Selection of Bazoka is chosen"
puts "Loaded 5 bullets only"
$choosen_gun[num] = $Weapon[:Bazoka]
end
return num
end
Upon calling the method. The user will choose his weapon and it will add it to the $choosen_gun hash with it's num, and it's return it's num what the user types
Here's my code for method ZombieRoom
def ZombieRoom(w)
zombie = {
:Construcied => [5],
:Invader => [5],
:Damned => [5],
:Steampunk => [5],
:Stoner => [5],
:Wasted => [5],
:Romero => [5]
}
puts "Welcome to the worst night mare of Zombie Room"
puts "You will be fighting with a random zombie"
while true
puts ".........."
puts "Selecting a random zombie"
puts "Selecting your prefered gun...."
case w
when 1
$choosen_gun[1]
puts "Your selected gun is #{$choosen_gun[1][0]}"
#values = zombie.values
#puts values[rand(values.size)]
#random_zombie = zombie.keys.sample(1)
#puts random_zombie[
random_zombie = zombie.to_a.sample(1).to_h
random_zombie.each do |key,value|
puts "Your random zombie is #{key}"
puts "With a health value of #{value[0]}"
puts "Time to take down that zombie now."
while true
puts "Type Shoot to knock it down or quit."
choice = gets.chomp
if $choosen_gun[1][1] >= 1
health = value[0] -= 1
$choosen_gun[1][1] -= 1
puts "#{key} health now is #{health}"
else
puts "Please reload your gun"
puts "Reloading......"
$choosen_gun[1][1] += 5
end
if health == 0
puts "You have defeated #{key}"
puts "Congrats!!!"
puts "We are happy for you"
puts "Lets begins to collect your prize"
CollectPrize()
else
puts "You did not defeat the #{key} yet"
end
end
end
end
end
end
Here's my code for method CollectPrize
def CollectPrize
puts "Congratulations on defeating"
puts "We would now like to give you some case prizes"
print "\n"
puts "Please choose only 1 prize for yourself"
print "\n"
puts "Type 1"
puts "$50,000"
print "\n"
puts "Type 2"
puts "$25,000"
print "\n"
puts "Type 3"
puts "$55,000"
hoho = gets.chomp.to_f
if hoho == 1
puts "hehe"
end
end
Here how i call my method
ZombieRoom(Weapon())
CollectPrize()
Now the problem is that whenever the CollectPrize method is called and i type my input to collect the prize example 1 then it print "$50,000". instead of the ending the problem, it went back to the ZombieRoom and continues to loop at the "Type Shoot to knock it down or quit." Can someone atleast tell me a proper way to solve this issue or also what other way to pass a method?
Your code is in a large while true loop. Since true is always true, it will never end, so after it calls CollectPrize() it just goes back to the while statement.
You could get out of it by inserting a break line after the CollectPrize() but there's another while true loop around this one.
I think you need to pay closer attention to how you want to exit the while loops.
puts "Time to take down that zombie now."
while true # <---------------- this is ALWAYS going to loop, without end
puts "Type Shoot to knock it down or quit."
choice = gets.chomp
if $choosen_gun[1][1] >= 1
health = value[0] -= 1
$choosen_gun[1][1] -= 1
puts "#{key} health now is #{health}"
else
puts "Please reload your gun"
puts "Reloading......"
$choosen_gun[1][1] += 5
end
if health == 0
puts "You have defeated #{key}"
puts "Congrats!!!"
puts "We are happy for you"
puts "Lets begins to collect your prize"
CollectPrize()
else
puts "You did not defeat the #{key} yet"
end
end
In ruby constants start with Capital letter.
Methods are always defined in lower case.
Try this in irb
irb(main):001:0> def Weapon
irb(main):002:1> end
=> :Weapon
irb(main):003:0> Weapon
NameError: uninitialized constant Weapon
To solve your problem name methods using ruby's naming conventions:
zombie_room, collect_prize etc.
Then this code will work:
zombie_room(weapon())
What you are doing there is not really passing method weapon to method zombie room.
What is really going on is that method weapon is executed, then it returns a value and result of that value is passed to method zombie_room.
I think that is what you wanted.
If you need to pass a method, check out documentation for proc and lambda or just use blocks.
I was able to count down to 97 bottle of beers but I am having trouble looping to count down to 1. Is it possible to create a loop with what I wrote? Here's what I have so far.
all_beers = (99).to_s
one_less = ((all_beers).to_i - 1).to_s
puts '' +
all_beers + ' bottles of beer on the wall, ' +
all_beers + ' bottles of beer. You take one down you pass it around ' +
one_less + ', beers on the wall!'
all_beers = one_less
one_less = ((all_beers).to_i - 1).to_s
puts '' +
all_beers + ' bottles of beer on the wall, ' +
all_beers + ' bottles of beer. You take one down you pass it around ' +
one_less + ', beers on the wall!'
use downto :
it will loop from the number you want upto the number you wish.
99.downto(1).each do |s|
all_beers = s
one_less = s - 1
puts '' +
all_beers.to_s + ' bottles of beer on the wall, ' +
all_beers.to_s + ' bottles of beer. You take one down you pass it around ' +
one_less.to_s + ', beers on the wall!'
end
Yes, it is certainly possible. This is taken from 99 Bottles of Beer project:
#
# Rubeer.rb
# by Eric Budd, Jan. 2008
#
# Demonstrates adding functionality to a built-in class, optional method parameters, inline
# conditionals, string replacement, alcohol aversion, and excessively fancy use of hashes.
#
# This borrows the hash from Daniel Straight's excellent implementation for the "wordalize" method
#
class Integer
NUMBER_WORDS = { 0 => "no", 1 => "one", 2 => "two", 3 => "three", 4 => "four", 5 => "five",
6 => "six", 7 => "seven", 8 => "eight", 9 => "nine",
10 => "ten", 11 => "eleven", 12 => "twelve", 13 => "thirteen",
14 => "fourteen", 15 => "fifteen", 16 => "sixteen", 17 => "seventeen",
18 => "eighteen", 19 => "nineteen",
20 => "twenty", 30 => "thirty", 40 => "forty", 50 => "fifty", 60 => "sixty",
70 => "seventy", 80 => "eighty", 90 => "ninety"}
def wordalize
raise "Invalid number to wordalize - should be in the range (0..99)" unless (0..99) === self
return NUMBER_WORDS[self] if self < 20
wordalized = NUMBER_WORDS[self - (self % 10)]
wordalized += '-' + NUMBER_WORDS[self % 10] unless (self % 10) == 0
return wordalized
end
def bottles
raise "Invalid number of bottles - should be in the range (0..99)" unless (0..99) === self
how_many_bottles = self.wordalize + ' bottle'
how_many_bottles += 's' unless self == 1
return how_many_bottles
end
alias :bottle :bottles # for grammar Nazis
end
def sing(number, teetotaller = false)
beverage = teetotaller ? 'coke' : 'beer'
puts "#{number.bottles.capitalize} of #{beverage} on the wall, #{number.bottles} of #{beverage}."
if number != 0
puts "Take one down, pass it around, #{(number - 1).bottles} of #{beverage} on the wall.\n\n"
else
puts "Go to the store and buy some more, 99 bottles of #{beverage} on the wall."
end
end
99.downto(0) { |number| sing(number) }
# Uncomment the following for the alternative teetotaller version
# 99.downto(0) { |number| sing(number, true) }
There are multiple Ruby versions uploaded there, but the default one is too complicated for beginners. This one, though, is very nice, and you should be able to understand what is going on.
The point that should answer your question is the bit with 99.downto(0) { |number| ... }. This is a loop that will repeat anything within the braces (in this case, sing(number)) a hundred times, with number going from 99 to 0.
Also note that it is inefficient (and illegible) to carry around the number as a string ((99).to_s) and convert it back to integer when you need it; rather, have it always be an integer, and convert it to string just before you need it as a string, when you display (or have the string concatenation/interpolation do it for you automatically, as in this piece of code).
While Ruby does have both for and while loops, they are rarely (while) or never (for) used. Instead, Rubyists usually rely on iterators and enumerators. Other functions like Integer#downto are Integer#upto, Integer#times and pretty much everything in the most awesome Enumerable mixin.
The short answer is yes, you can make a loop using what you wrote, and there are many ways of looping with ruby. However, since this seems to be about learning programming, considering that you did not use any control structures or string interpolation, not to mention casting that doesn't make much sense, I'd recommend Why's Poignant Guide to Ruby to learn the concepts of programming while using ruby.
As well as downto, you could do something like:
(1..99).reverse_each do |number|
bottle = number == 1 ? 'bottle' : 'bottles'
verse = "#{number} #{bottle} of beer on the wall, #{number} #{bottle} of beer. "
verse << "Take one down, pass it around, #{number-1} #{number-1 == 1 ? 'bottle' : 'bottles'} of beer on the wall"
puts verse
end
Things you may use to make your life easier: downto(), proc, ternary if (--that--thing--> ?:)
I enjoyed experiencing this exercise for the first time, so I'm a little hesitant about providing an answer here but so it goes.
It's a little bit more advanced, but using a 'proc' to make sure you pluralize "bottle(s)" correctly is a nice and clean way to get it done.
'downto()' is also an awesome way to iterate through those 99 bottles since it makes it feel like you're reading English instead of code.
num_at_start = 99 # You may change this number.
num_bottles = proc { |n| "#{n} bottle#{ n == 1 ? '' : 's'}" }
num_at_start.downto(1) do |num|
print "#{num_bottles.call(num)} of beer on the wall, " +
"#{num_bottles.call(num)} of beer!\n" +
"You take one down, pass it around, "
unless num == 1
puts "#{num_bottles.call(num - 1)} of beer on the wall!"
else
puts "No more bottles of beer on the wall!"
end
end
Source: Learn to Program 2nd Edition by Chris Pine (I changed a couple of things though)
This is the fix that I found for this question:
beer = 99
while beer > 0
puts beer.to_s + " bottles of beer on the wall. " + beer.to_s +
" bottles of beer."
(beer -= 1).to_s
puts "Take one down, pass it around. " + beer.to_s +
" bottles of beer on the wall."
end
I am creating a very simple "Repository" as my first real ruby script. I have the sections created where people can create an item and starting value, but I cannot seem to nail down how to keep people from incrementing (or decrementing) by 0 or negative numbers.
My code to add is as follows:
class Item
attr_accessor :name, :count
def initialize (name,initCount )
#name=name.downcase
#count=initCount
end
def add(amount)
#count += amount
end
def sub(amount)
#count -= amount
end
end
def prompt()
puts #items.inspect
puts " (A)dd item\n (R)emove item\n (L)ist items\n (I)ncrease item\n (D)ecrease items\n (Q)uit "
select = [(print '?: '), gets.rstrip][1]
if (select.upcase=="A") then
puts "Add Item\nItem name"
name=[(print 'Name? : '), gets.rstrip][1]
puts "Initial Count"
count= [(print 'Count? : '), gets.rstrip][1]
#items.push(Item.new(name,count.to_i)) unless #items.index(#items.find { |l| l.name == name })
end
Any help is appreciated.
Consider organizing your code like this. I may have made a few errors, but you'll get the idea.
PROMPT_TEXT =
" (A)dd item
(R)emove item
(L)ist items
(I)ncrease items
(D)ecrease items
(Q)uit ?: "
ILLEGAL_PROMPT_RESPONSE_MSG =
"You can't enter that! What were you thinking??"
NEGATIVE_NUMBER_MSG =
"If I've told you once, I've told you a thousand times: NO NEGATIVE NUMBERS!"
NOT_NUMBER_MSG =
"If that's a number, it must be Roman, and they aren't allowed."
TRY_AGAIN_MSG = "Try again...
.
def prompt()
loop do
puts #items.inspect # What's this?
puts PROMPT_TEXT
gets.rstrip.upcase case
when "A"
break if add_item
when "R"
...
when "L"
...
...
when "Q" then return
else
puts ILLEGAL_PROMPT_RESPONSE_MSG
end
puts TRY_AGAIN_MSG
end
end
.
def add_item
puts "Add Item\nItem name"
print 'Name? : '
name = gets.rstrip
puts "Initial Count"
print 'Count? : '
count = gets.rstrip
unless count =~ /\d+/
if count =~ /-\s*\d+/
puts NEGATIVE_NUMBER_MSG
else
puts NOT_NUMBER_MSG
end
return false
end
#items.push...
true
end
Aside: the statement
name=[(print 'Name? : '), gets.rstrip][1]
brings to mind a word that begins with "abomin" and ends with "ation". :-)
class Item
attr_accessor :name, :count
def initialize (name,initCount )
raise if initCount<0
#name=name.downcase
#count=initCount
end
def add(amount)
raise if amount<0
#count += amount
end
def sub(amount)
raise if amount<0 || amount>#count
#count -= amount
end
end
I am trying to build a "train game" based loosely on the old video game "Drug Wars." I am currently working my way through LRTHW, and I believe that I should be using OOP, but I'm not to that lesson yet.
The premise is that you have a set number of cars on your train and you can see what products are for sale in other cities (no limit on the amount you can buy or sale presuming you can fit them in your train). This code isn't complete, but I'm wondering if I'm even approaching this half way sanely in regard to creating and accessing the product prices in a reasonable manner.
#Initializing variables. Current_location should be changed to random
#in the future.
current_location = 'omaha'
train = []
new_york = []
chicago = []
omaha = []
dallas = []
seattle = []
def prompt()
print "> "
end
#Here is the selection menu. It is possible to exploit this and
#buy, sell and move all within the same turn.
#There needs to be a "safe selection" so that once you have moved you
#can't move again, but you can get info, buy and sell
#as many times as you would like.
def selection()
puts "Do you want to travel, buy, sell or get info?"
prompt; selection = gets.chomp
if selection.include? "travel"
puts "Where would you like to travel?"
prompt; city = gets.chomp
return 'city', city
elsif selection.include? "buy"
puts "Current Prices Are:"
puts "What would you like to Buy?"
elsif selection.include? "sell"
puts "Current Prices Are:"
puts "What would you like to sell?"
elsif selection.include? "info"
puts "What city or train would you like info on?"
else
puts "Would you like to exit selection or start selection again?"
end
end
#This generates a new cost for each good at the start of each turn.
def generate_costs(new_york, chicago, omaha, dallas, seattle)
new_york[0] = rand(10)
new_york[1] = rand(10) + 25
new_york[2] = rand(5) + 10
omaha[0] = rand(10)
omaha[1] = rand(10) + 25
omaha[2] = rand(5) + 10
chicago[0] = rand(25) + 5
chicago[1] = rand(5) + 10
chicago[2] = rand(4)
dallas[0] = rand(6) + 11
dallas[1] = rand(3) + 10
dallas[2] = rand(8)
seattle[0] = rand(6)
seattle[1] = rand(10) + 24
seattle[2] = rand(14) + 13
return new_york, chicago, omaha, dallas, seattle
end
# This is my main() loop. It drives the game forward.
for i in (0..5)
new_york, chicago, omaha, dallas, seattle = generate_costs(new_york, chicago, omaha, dallas, seattle)
turns = 5 - i
puts "You are currently in #{current_location}. You have #{turns} remaining."
puts "{ ___________________________ }"
#Code Here evaluates and accesses pricing based on current_location.
#Is this the correct way to do this?
fish = eval("#{current_location}[0]")
coal = eval("#{current_location}[1]")
cattle = eval("#{current_location}[2]")
puts "Fish is worth #{fish}"
puts "Coal is worth #{coal}"
puts "Cattle is worth #{cattle}"
puts "{ ___________________________ }"
change, value = selection()
if change == 'city'
current_location = value
elsif change == 'buy'
puts 'So you want to buy?'
else
puts "I don't understand what you want to do"
end
end
eval is a nasty way of accessing data ( When is `eval` in Ruby justified? ). You should consider moving things into an object.
I have improved the code slightly, storing the cities in a hash, which gets rid of the evals. I have stubbed out the generate_costs logic but you can assign it by doing:
cities[:new_york][0] = rand(10)
Ideally, the code should be re-written in an object-oriented syntax. If I get some time then I'll knock up an example for you.
Here is the code:
#Initializing variables. Current_location should be changed to random
#in the future.
current_location = :omaha
train = []
cities = {
:new_york => [],
:chicago => [],
:omaha => [],
:dallas => [],
:seattle => []
}
def prompt()
print "> "
end
#Here is the selection menu. It is possible to exploit this and
#buy, sell and move all within the same turn.
#There needs to be a "safe selection" so that once you have moved you
#can't move again, but you can get info, buy and sell
#as many times as you would like.
def selection()
puts "Do you want to travel, buy, sell or get info?"
prompt; selection = gets.chomp
if selection.include? "travel"
puts "Where would you like to travel?"
prompt; city = gets.chomp
return 'city', city
elsif selection.include? "buy"
puts "Current Prices Are:"
puts "What would you like to Buy?"
elsif selection.include? "sell"
puts "Current Prices Are:"
puts "What would you like to sell?"
elsif selection.include? "info"
puts "What city or train would you like info on?"
else
puts "Would you like to exit selection or start selection again?"
end
end
#This generates a new cost for each good at the start of each turn.
def generate_costs(cities)
cities.each do |key,city|
0.upto(2) do |i|
city[i] = rand(10)
end
end
end
# This is my main() loop. It drives the game forward.
for i in (0..5)
generate_costs(cities)
turns = 5 - i
puts "You are currently in #{current_location}. You have #{turns} remaining."
p cities
puts "{ ___________________________ }"
fish = cities[current_location][0]
coal = cities[current_location][1]
cattle = cities[current_location][2]
puts "Fish is worth #{fish}"
puts "Coal is worth #{coal}"
puts "Cattle is worth #{cattle}"
puts "{ ___________________________ }"
change, value = selection()
if change == 'city'
current_location = value
elsif change == 'buy'
puts 'So you want to buy?'
else
puts "I don't understand what you want to do"
end
end