Basically, I was told that if I wanted to get better at programming, I should pick a projeect and stick to it. I chose to write a BMI calculator. Here's the code.
def get_values()
puts("Please enter your height in metres.")
height = gets.to_f
puts("Please enter your weight in kilograms.")
weight = gets.to_f
bmi = ((weight) / (height * height))
puts("Your BMI is: " + bmi.to_s)
if (bmi < 18.5)
puts("You are underweight.")
elsif (bmi > 18.5) and (bmi < 25.0)
puts("You are a healthy weight.")
elsif (bmi > 25) and (bmi < 30)
puts("You are overweight.")
elsif (bmi > 30)
puts("You are obese.")
end
end
def get_values_imperial()
puts("Please enter your height in inches.")
height = gets.to_f
puts("Please enter your weight in pounds.")
weight = gets.to_f
bmi = (weight * 703) / (height * height)
puts("Your BMI is: " + bmi.to_s)
if (bmi < 18.5)
puts("You are underweight.")
elsif (bmi > 18.5) and (bmi < 25.0)
puts("You are a healthy weight.")
elsif (bmi > 25) and (bmi < 30)
puts("You are overweight.")
elsif (bmi > 30)
puts("You are obese.")
end
end
def main()
puts("Welcome to the BMI calculator. Would you like to continue with metric or imperial values?")
answer = gets.chomp
if (answer == "metric") or (answer == "Metric")
get_values()
elsif (answer == "imperial") or (answer == "Imperial")
get_values_imperial()
else
("Unsupported unit of measurement.")
end
end
main()
Basically, the issue is this: most people don't know thir height in inches, or so I would assume. I'd like the user to be able to enter a height, eg. 6'1", in feet and inches. Is this posssible without using the Numeric class?
Going with your question's title:
str = "6'11\""
def to_inch(x)
( x.split("'")[0].to_i * 12 ) +
x.split("'")[1].to_i
end
to_inch(str)
=> 83
If you want to complete the example you should also check the format of str.
Edit:
If you want to be inventive, here's a version without to_i:
h = {}
0.upto(12) { |x| h[x.to_s] = x }
def to_inch(x, ha)
( ha[x.split("'")[0]] * 12 ) +
ha[x.split("'")[1].chomp("\"")]
end
to_inch(str, h)
=> 83
Related
def get_values()
puts("Please enter your height in metres.")
height = gets.chomp.to_i
puts("Please enter your weight in kilograms.")
weight = gets.chomp.to_i
bmi = ((weight).to_f / (height * height).to_f)
puts("Your BMI is: ")
puts bmi.to_s
end
def main()
puts("Welcome to the BMI calculator. Would you like to continue?")
answer = gets.chomp
if (answer == "yes" or "Yes")
get_values()
else
("Understandable, have a nice day.")
end
end
main()
If you run this, you'll see that whatever value you enter for weight, will be returned as the BMI. Why is this? I simply cannot understand why this would be. I am new to Ruby, obviously. Previously, I did not have the .to_f conversions in the calculation, but that also returned the user input value for "weight". Please help.
EDIT: Whilst I greatly appreciate the re-write of my code below, as it's a very diffferent style to my usual style of coding, I have fixed the problem with the answers below - I simply had the .to_f conversions in the wrong spot. This one works very well and is accurate to 14 decimal places!
def get_values()
puts("Please enter your height in metres.")
height = gets.to_f
puts("Please enter your weight in kilograms.")
weight = gets.to_f
bmi = ((weight) / (height * height))
puts("Your BMI is: ")
puts bmi.to_s
end
def main()
puts("Welcome to the BMI calculator. Would you like to continue?")
answer = gets.chomp
if (answer == "yes" or "Yes")
get_values()
else
("Understandable, have a nice day.")
end
end
main()
EDIT: I do not remember if I changed anything relevant to the calculation, but the code has been updated again.
def get_values()
puts("Please enter your height in metres.")
height = gets.to_f
puts("Please enter your weight in kilograms.")
weight = gets.to_f
bmi = ((weight) / (height * height))
puts("Your BMI is: ")
puts bmi.to_s
return bmi
end
def harsh_truths()
bmi = get_values()
if (bmi < 18.5)
puts("You are underweight.")
elsif (bmi > 18.5) and (bmi < 25.0)
puts("You are a healthy weight.")
elsif (bmi > 25) and (bmi < 30)
puts("You are overweight.")
elsif (bmi > 30)
puts("You are obese.")
end
end
def main()
puts("Welcome to the BMI calculator. Would you like to continue?")
answer = gets.chomp
if (answer == "yes") or (answer == "Yes")
harsh_truths()
else
("Understandable, have a nice day.")
end
end
main()
The above is the "updated code" that I keep referring to. I hope to see you all again when I add the option to calculate BMI using imperial values.
Here's a fixed, more idiomatic Ruby version of the same code:
# Define a generic method to grab input
def ask(prompt)
puts prompt
gets.to_f
end
# Loop while more input is available
loop do
# puts doesn't always use brackets, it's subjective. You can, but
# in many cases they're omitted for simplicity.
puts("Welcome to the BMI calculator. Would you like to continue?")
case (gets.chomp)
when /\An/i # Anything that "starts with" (\A) "n" + case insensitive (/i)
puts "Understandable, have a nice day."
break
end
# Get some input
height = ask("Please enter your height in metres.")
weight = ask("Please enter your weight in kilograms.")
# Notice how if they're already float this becomes super easy
bmi = weight / (height * height)
# Use string interpolation, automatic .to_s conversion happens.
puts "Your BMI is: #{bmi}"
end
Just change height and weight to float as follow:
height = gets.chomp.to_f
weight = gets.chomp.to_f
I want my shape not to go out of the window area. for example when I press the left button the shape moves out of the window area but I want it to hit the side and stay there.
def update
if button_down?(Gosu::KbRight)
if #shape_x != (WIDTH - SHAPE_DIM)
#shape_x += 3
end
end
if button_down?(Gosu::KbLeft)
if #shape_x != (WIDTH - SHAPE_DIM)
#shape_x -= 3
end
end
if button_down?(Gosu::KbUp)
#shape_y -= 3
end
if button_down?(Gosu::KbDown)
#shape_y += 3
end
end
Assuming WIDTH is Window width, you must use < and >, instead of !=
def update
if button_down?(Gosu::KbRight)
if #shape_x < (WIDTH - SHAPE_DIM)
#shape_x += 3
end
end
if button_down?(Gosu::KbLeft)
if #shape_x > (WIDTH - SHAPE_DIM)
#shape_x -= 3
end
end
if button_down?(Gosu::KbUp)
#shape_y -= 3
end
if button_down?(Gosu::KbDown)
#shape_y += 3
end
end
Going through Chris Pine's Learn To Program and working on the number to roman numeral conversion project. The code below works, however it's pretty ugly w/ all those if (and end) statements. However, when I use elsif the program doesn't respond (appears to freeze up). Any thoughts would be helpful!
def calc input
roman_numeral = ''
while true
if input >= 1000
roman_numeral += 'M' * (input / 1000)
input = input - (1000 * (input / 1000))
if input <= 999 || input >= 500
roman_numeral += 'D' * (input / 500)
input = input - (500 * (input / 500))
if input <= 499 || input >= 100
roman_numeral += 'C' * (input / 100)
input = input - (100 * (input / 100))
if input <= 99 || input >= 50
roman_numeral += 'L' * (input / 50)
input = input - (50 * (input / 50))
if input <= 49 || input >= 10
roman_numeral += 'X' * (input / 10)
input = input - (10 * (input / 10))
if input <= 9 || input >= 5
roman_numeral += 'V' * (input / 5)
input = input - (5 * (input / 5))
if input <= 4 || input >= 1
roman_numeral += 'I' * (input / 1)
input = input - (1 * (input / 1))
puts roman_numeral
break
end
end
end
end
end
end
end
end
end
puts 'Give me a number, any number:'
input = gets.chomp.to_i
calc(input)
It's convenient to use the method Enumerable#find with an array:
ARR = [[1000,'M'], [ 500,'D'], [100,'C'], [50,'L'], [10,'X'], [5,'V'], [1,'I']]
def which(input)
ARR.find { |v,_| input >= v }
end
which(2) #=> [1, "I"]
which(7) #=> [5, "V"]
which(17) #=> [10, "X"]
which(77) #=> [50, "L"]
which(777) #=> [500, "D"]
which(7777) #=> [1000, "M"]
Assuming you are converting an integer to a roman numeral, consider making use of the method Fixnum#divmod. Suppose the integer were 2954 and you've already determined that there are two "M"'s and one "D" (so the beginning of your roman numeral string is "MMD"), and that 454 is left over. Then:
c, rem = 454.divmod(100)
#=>[4, 54]
c #=> 4
rem #=> 54
tells you there are four "C"'s with 54 left over.
Four "C"'s are written "CD" (not "CCCC"), however, so you may want to use a hash such as the following:
REP = {..., "C"=>["C", "CC", "CCC", "CD"], ...}
to convert the number of "C"'s to a roman numeral. Here you would append REP["C"][4-1] #=> "CD" to "MMD": "MMD" << "CD" #=> "MMDCD".
The answer from Cary Swoveland is an excellent way to decrease your if block nesting.
His answer tells you which numeral comes next, but not how many (as in your code). A natural way to tie it together is with a recursive function call:
class Romans
def self.calc(input, acc = "")
raise ArgumentError.new("Roman Numerals must be positve") if input < 0
raise ArgumentError.new("Roman Numerals must be integers") if ! input.is_a? Integer
return acc if input == 0
amount, numeral = which(input)
acc += numeral
input -= amount
calc(input, acc)
end
##ARR = [[1000,'M'], [ 500,'D'], [100,'C'], [50,'L'], [10,'X'], [5,'V'], [1,'I']]
def self.which(input)
##ARR.find { |v,_| input >= v }
end
end
In use:
pry(main)> (1..10).each{|i| puts "#{i}=> #{Romans.calc(i)}"}
1=> I
2=> II
3=> III
4=> IIII
5=> V
6=> VI
7=> VII
8=> VIII
9=> VIIII
10=> X
pry(main)> [Random.rand(1..100000)].each{|i| puts "#{i}=> #{Romans.calc(i)}"}
63124=> MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMCXXIIII
Be aware that ruby doesn't have TCO, and so will blow the stack with large enough numbers- but if you need the Roman numeral version of 8 million, you might want to make up some new letters.
Here's one that uses string multiplication. For example: ('M' * 3) = 'MMM'
def to_roman(number)
raise 'Must use positive numbers.' if number <= 0
roman = ''
roman << 'M' * (number / 1000)
roman << 'D' * (number % 1000 / 500)
roman << 'C' * (number % 500 / 100)
roman << 'L' * (number % 100 / 50)
roman << 'X' * (number % 50 / 10)
roman << 'V' * (number % 10 / 5)
roman << 'I' * (number % 5 / 1)
roman
end
puts to_roman(1234) # <-- test
Reference: Learn to Program 2nd by Chris Pine
I'm trying to do in the Ruby program the summation of a factorial. and I can not solve. a clarification, when they run out points .... means that the logic is fine. if you get a F in one of the points, it means that the logic is wrong.
Write a program to calculate the sum of 1 + 1 / (2!) + 1 / (3!) + 1 / (4!) + .... + 1 / (n!) For a given n. Write the program in two ways: using While, For
def factorial(n)
#(n == 0) ? 1 : n * factorial(n - 1)
fact = 1
for i in 1..n
fact = fact * i
end
return fact
end
def sumatoriaWhile(n)
total = n
sumatoria = 0.0
while n > 1
total = total * (n - 1)
n = n - 1
sumatoria =sumatoria + total.to_f
end
return (1 + (1 / total.to_f)).round(2)
end
def sumatoriaFor(n)
fact = 1
sumatoria = 0.0
for i in 1..n
for j in 1..i
fact = fact * j
end
sumatoria = sumatoria + (1 / fact.to_f)
i = i + 1
end
return sumatoria.round(2)
end
#--- zona de test ----
def test_factorial
print validate(120, factorial(5))
print validate(5040, factorial(7))
print validate(362880, factorial(9))
end
def test_sumatoriaWhile
print validate(1.50, sumatoriaWhile(2))
print validate(1.83, sumatoriaWhile(3))
end
def test_sumatoriaFor
print validate(1.50, sumatoriaFor(2))
print validate(1.83, sumatoriaFor(3))
end
def validate (expected, value)
expected == value ? "." : "F"
end
def test
puts "Test program"
puts "---------------------------"
test_factorial
test_sumatoriaWhile
test_sumatoriaFor
puts " "
end
test
My friend, Thank you for your prompt response. First of all, I appreciate the help given. I am learning ruby programming and want to learn more as you and the other people. if indeed the answer is wrong. I have modified the answer. and also need to know what the function of While. and I hereby amended program again. and now I get an F on the part of WHILE.
def factorial(n)
fact = 1
for i in 1..n
fact = fact * i
end
return fact
end
def sumatoriaWhile(n)
total = n
sumatoria = 0.0
while n < 1
total = total * (n - 1)
sumatoria = sumatoria + (1.0 / total.to_f)
n = n - 1
end
return sumatoria.round(2)
end
def sumatoriaFor(n)
fact = 1
sumatoria = 0.0
for i in 1..n
fact = fact * i
sumatoria = sumatoria + (1.0 / fact.to_f)
end
return sumatoria.round(2)
end
#--- zona de test ----
def test_factorial
print validate(120, factorial(5))
print validate(5040, factorial(7))
print validate(362880, factorial(9))
end
def test_sumatoriaWhile
print validate(1.50, sumatoriaWhile(2))
print validate(1.67, sumatoriaWhile(3))
end
def test_sumatoriaFor
print validate(1.50, sumatoriaFor(2))
print validate(1.67, sumatoriaFor(3))
end
def validate (expected, value)
expected == value ? "." : "F"
end
def test
puts "Test de prueba del programa"
puts "---------------------------"
test_factorial
test_sumatoriaWhile
test_sumatoriaFor
puts " "
end
test
I have a hard time figuring out what you're doing in the summation function. Here's a straightforward function:
def sumatoriaFor(n)
return 0 if n <= 0
factorial = 1
sum = 0.0
for i in 1..n
factorial *= i
sum += 1.0 / factorial.to_f
end
return sum.round(2)
end
def sumatoriaWhile(n)
return 0 if n <= 0
i = 1
factorial = 1
sumatoria = 0.0
while i <= n
factorial *= i
sumatoria += (1.0 / factorial.to_f)
i = i + 1
end
return sumatoria.round(2)
end
The while look is straight forward too now. Also your validation is wrong:
1 + 1/2 + 1/6 ~= 1 + 0.5 + 0.17 = 1.67
I was wondering if anyone could explain to me why I can't use my Hash($player_x_command_list) to access my player_x_move(position) method? What's the correct way of doing this?
What I am trying to do is, create a Hash that lets the user enter input and it reflecting back to my methods in my class.
I have tried various ways of altering so that the code could work correctly but nothing works.
I'm getting the error:
undefined method `player_x_move' for Game:Class (NoMethodError)
Does that mean hashes can't store methods?
Here's my code:
#Tic Tac Toe Game
#The format is the below: where index 0 represents top left and index 8 represents bottom right
#goes 0,1,2
# 3,4,5
# 6,7,8
#"e" is for empty. "x" is for Player X moves and "o" is for Player O moves
class Game
##player_x_win_count = 0
##player_o_win_count = 0
def initialize
#board = Array.new(9, "e")
#move_number = 0
end
def get_names
puts "Hi Welcome to my Tic Tac Toe Game. The board looks like this:
|TL|TM|TR|
|ML|MM|MR|
|BL|BM|BR|
Each position of the Tic Tac Toe board is represented by two letters. To \"X\" or \"O\" a position, just input the two letters in CAPS like \"MM\"
The command list is as follows:
TL = top left
TM = top mid
TR = top right
ML = mid left
MM = mid mid
MR = mid right
BL = bottom left
BM = bottom mid
BR = bottom right
board = to view the board
new game = to clean the board and create a new game (note that this command should only be used when you don't want to continue on the current game. The game automatically creates a new game if a winner, loser, or draw is declared)
"
puts "Please Enter PlayerX's name. He/she will be using X's to mark the board."
#player_x = gets.chomp
puts "Please Enter PlayerO's name. He/she will be using O's to mark the board."
#player_o = gets.chomp
self.new_round
end
$player_x_command_list = {"TL" => self.player_x_move(0), "TM" => self.player_x_move(1), "TR" => self.player_x_move(2), "ML" => self.player_x_move(3), "MM" => self.player_x_move(4),
"MR" => self.player_x_move(5), "BL" => self.player_x_move(6), "BM" => self.player_x_move(7), "BR" => self.player_x_move(8), "board" => self.board, "new game" => self.clean_board,
"win count" => self.win_count}
$player_o_command_list = {"TL" => self.player_o_move(0), "TM" => self.player_o_move(1), "TR" => self.player_o_move(2), "ML" => self.player_o_move(3), "MM" => self.player_o_move(4),
"MR" => self.player_o_move(5), "BL" => self.player_o_move(6), "BM" => self.player_o_move(7), "BR" => self.player_o_move(8), "board" => self.board, "new game" => self.clean_board,
"win count" => self.win_count}
def enter_command_player_x
puts "Please input your command, #{#player_x} aka PlayerX"
command = gets.chomp
$player_x_command_list[command]
end
def enter_command_player_o
puts "Please input your command, #{#player_o} aka PlayerY. Type \"help\" to see a full list of commands"
command = gets.chomp
$player_o_command_list[command]
end
def new_round
puts "So who wants to go first this round"
went_first_this_round = gets.chomp
if went_first_this_round == #player_x
self.enter_command_player_x
elsif went_first_this_round == #player_o
self.enter_command_player_o
else
puts "Not a valid name. Please enter one of the player's names"
end
end
def board
print "|#{#board[0]}|#{#board[1]}|#{#board[2]}|\n|#{#board[3]}|#{#board[4]}|#{#board[5]}|\n|#{#board[6]}|#{#board[7]}|#{#board[8]}|"
end
def player_x_move(position)
if #board[position] == "x" || #board[position] == "o"
return "That move was invalid as someone has already moved there. Please enter a valid move"
end
#board[position] = "x"
#move_number += 1
puts "That was move number #{#move_number} and the current board looks like: "
self.board
self.check_game
puts "Now it is #{player_o}'s turn. #{player_o} please input your next command."
self.enter_command_player_o
end
def player_o_move(position)
if #board[position] == "x" || #board[position] == "o"
return "That move was invalid as someone has already moved there. Please enter a valid move"
end
#board[position] = "o"
#move_number += 1
puts "That was move number #{#move_number} and the current board looks like: "
self.board
self.check_game
puts "Now it is #{player_x}'s turn. #{player_x} please input your next command"
self.enter_command_player_x
end
def check_game
triple_x = "xxx"
triple_o = "ooo"
if #move_number == 9
#move_number = 0
self.clean_board
return "The board is completely filled up. Looks like this is a draw. This is Game Over. Make a new game by setting any variable = to new.Game and using that variable to play"
elsif #board[0] + #board[1] + #board[2] == triple_x
##player_x_win_count += 1
#move_number = 0
self.clean_board
return "Player X Wins"
elsif #board[3] + #board[4] + #board[5] == triple_x
##player_x_win_count += 1
#move_number = 0
self.clean_board
return "Player X Wins"
elsif #board[6] + #board[7] + #board[8] == triple_x
##player_x_win_count += 1
#move_number = 0
self.clean_board
return "Player X Wins"
elsif #board[0] + #board[3] + #board[6] == triple_x
##player_x_win_count += 1
#move_number = 0
self.clean_board
return "Player X Wins"
elsif #board[1] + #board[4] + #board[7] == triple_x
##player_x_win_count += 1
#move_number = 0
self.clean_board
return "Player X Wins"
elsif #board[2] + #board[5] + #board[8] == triple_x
##player_x_win_count += 1
#move_number = 0
self.clean_board
return "Player X Wins"
elsif #board[0] + #board[4] + #board[8] == triple_x
##player_x_win_count += 1
#move_number = 0
self.clean_board
return "Player X Wins"
elsif #board[2] + #board[4] + #board[6] == triple_x
##player_x_win_count += 1
#move_number = 0
self.clean_board
return "Player X Wins"
#now check if Player O Wins
elsif #board[0] + #board[1] + #board[2] == triple_o
##player_y_win_count += 1
#move_number = 0
self.clean_board
return "Player O Wins"
elsif #board[3] + #board[4] + #board[5] == triple_o
##player_y_win_count += 1
#move_number = 0
self.clean_board
return "Player O Wins"
elsif #board[6] + #board[7] + #board[8] == triple_o
##player_y_win_count += 1
#move_number = 0
self.clean_board
return "Player O Wins"
elsif #board[0] + #board[3] + #board[6] == triple_o
##player_y_win_count += 1
#move_number = 0
self.clean_board
return "Player O Wins"
elsif #board[1] + #board[4] + #board[7] == triple_o
##player_y_win_count += 1
#move_number = 0
self.clean_board
return "Player O Wins"
elsif #board[2] + #board[5] + #board[8] == triple_o
##player_y_win_count += 1
#move_number = 0
self.clean_board
return "Player O Wins"
elsif #board[0] + #board[4] + #board[8] == triple_o
##player_y_win_count += 1
#move_number = 0
self.clean_board
return "Player O Wins"
elsif #board[2] + #board[4] + #board[6] == triple_o
##player_y_win_count += 1
#move_number = 0
self.clean_board
return "Player O Wins"
else
return "no one has WON YET! Continue your GAME!!"
end
end
def clean_board
#board = Array.new(9, "e")
end
def win_count
puts "So far Player X has won #{##player_x_win_count} times and Player O has won #{##player_o_win_count} times."
end
end
a = Game.new
a.get_names
You have a few things wrong here:
Your scope is wrong. When defining the hash, self is the class, not the instance of the class that you want to operate on.
When you create the hash, the value of the hash is going to be return value of the player_x_move etc method at the time it was defined.
You can simplify this a lot by using case.
def enter_command
puts "Please input your command, #{#player_x} aka PlayerX"
command = gets.chomp
case command
when "TL"
player_x_move(0)
when "TM"
player_x_move(1)
# etc
else
puts "#{command} is not a valid command."
end
end
You can also simplify your code further by creating methods that accept parameters like enter_command, player_move, and then passing the player to operate on to them. This prevents you from having to duplicate each of your methods for each player.
Something else you could consider is just looking up the index of the move based on the command given:
COMMAND_POSITIONS = %w(TL TM TR ML MM MR BL BM BR)
def enter_command(player)
puts "Please input your command, #{player}"
command = gets.chomp
case command
when *COMMAND_POSITIONS
player_move player, COMMAND_POSITIONS.index(command)
when "board"
board
when "new game"
clean_board
when "win count"
win_count
else
puts "#{command}" is not a valid command
end
end
Hashes can not store methods. Methods aren't objects, but can be converted to Procs. Method definitions return nilchanged since this writing. However, you can store a Proc or a lambda. You can also store the return (evaluation) of a method.
Here is an example of how you can store a Proc to a Hash that was derived from a method.
>> def hello(name)
>> "Hello #{name}!"
>> end
=> nil
>> my_stored_methods = {:hello => method(:hello).to_proc}
=> {:hello=>#<Proc:0x816f604 (lambda)>}
>> my_stored_methods[:hello].call("World")
=> "Hello World!"
Do not let the stroop effect of me calling the hash "my_stored_methods" lead you to believe that there is actually a real method stored there. It is lambda (a specialized Proc) stored in the hash, and used as appropriate. Indeed, had I not used .to_proc there, it would hold a Method instance.
Also, this solution does not grow with the development of the open method, if the method were to change, the Proc stored in the hash would continue to work as the method did at the point when the method was stored as a Proc.
As #AndrewMarshall reminds me, I could have left it as a Method instance. This still will not "store" the method itself, as when the method changes, the result will still be the historical behavior of the source method as it was when stored. It also provides for a stronger "Stroop effect" in that you may mistakenly think that an actual method is stored there. It simply is not.