While loop continues despite conditions not being met? - ruby

When I run the game and type either "yes" or "no" at the end, it always reverts back to the start of the while loop at line 41, when the conditions for both that and the containing loop are not met.
replay = true
while replay
#Pre-var
print "What difficulty? (1 for easy, 2 for medium, or 3 for hard): "
difficulty = gets.chomp.to_i
until difficulty == 1 || difficulty == 2 || difficulty == 3 do
print "Please type either 1, 2, or 3: "
difficulty = gets.chomp.to_i
end
#Variables
if difficulty == 1
r = Random.new
number = r.rand(100..1000)
puts "You have 15 guesses. "
print "Guess a number with three digits: "
within_num = 50
elsif difficulty == 2
r = Random.new
number = r.rand(1000..10000)
puts "You have 15 guesses. "
print "Guess a number with four digits: "
within_num = 500
elsif difficulty == 3
r = Random.new
number = r.rand(10000..100000)
puts "You have 15 guesses. "
print "Guess a number with five digits: "
within_num = 5000
end
guess = ""
num_guess = 0
guess_array = Array.new
array_location = 0
count_through = 0
array_print = count_through - 1
replay_inner = true
#Body
puts number
while num_guess <= 14 || replay_inner == true #Keeping as <= 14 as to avoid unnecessarily rewriting code, still gives 15 guesses
guess = gets.chomp.to_i
if guess > number * 2
print "That is more than double the number. Guess again: "
elsif guess < number / 2
print "That is less than half the number. Guess again: "
elsif guess > number && guess < number + within_num #within_num: 50 for easy, 500 for medium, 5000 for hard
print "You are close. That is too big. Guess again: "
elsif guess < number && guess > number - within_num
print "You are close. That is too small. Guess again: "
elsif guess < number
print "That is too small. Guess again: " #Hinting the user to how close they are.
elsif guess > number
print "That is too big. Guess again: "
elsif guess == number
puts "Congragulations! You win!"
print "Your "
print guess_array.length
print " incorrect guesses were: "
if num_guess == 0
sleep(0.5)
print "... Oh."
else
while count_through < num_guess #Loop to relay user's guesses with a delay of 0.5 seconds
print guess_array[count_through]
if count_through == num_guess - 2
print ", and "
elsif count_through == num_guess - 1
puts ". "
else
print ", "
end
count_through += 1
sleep(0.5)
end
puts "Would you like to play again? (yes/no)"
replay_answer = gets.chomp
until replay_answer == "yes" || replay_answer == "y" || replay_answer == "no" || replay_answer == "n" do
print "Please answer with yes, y, no, or n: "
replay_answer = gets.chomp
end
if replay_answer == "yes" || replay_answer == "y"
replay = true
puts "yes"
elsif replay_answer == "no" || replay_answer == "n" #Determining whether or not to replay
replay = false
puts "no"
end
end
end
guess_array.push guess
num_guess += 1
#puts num_guess
#puts guess_array[array_location]
array_location += 1
if num_guess >= 15 && guess != number
puts "Sorry, you lost. "
print "Your "
print guess_array.size
print " guesses were: "
while count_through < num_guess
print guess_array[count_through] #Same as loop above; for when player fails to guess correctly
if count_through == num_guess - 2
print ", and "
elsif count_through == num_guess - 1
puts ". "
else
print ", "
end
count_through += 1
sleep(0.5)
end
puts "Would you like to play again? (yes/no)"
replay_answer = gets.chomp
until replay_answer == "yes" || replay_answer == "y" || replay_answer == "no" || replay_answer == "n" do
print "Please answer with yes, y, no, or n: "
replay_answer = gets.chomp
end
if replay_answer == "yes" || replay_answer == "y"
replay = true
replay_inner = true
puts "yes"
elsif replay_answer == "no" || replay_answer == "n" #Determining whether or not to replay
replay = false
replay_inner = false
puts "no"
end
end
end

I think the condition in the while should be:
numberGuess<=14 && replay_inner == true

The way you and especially your fellow programmers will find this and future bugs is by properly commenting your code, e.g.:
# ask the user about an optional replay
replay_answer = gets.chomp
# only accept yes/y/no/n
until replay_answer == "yes" || replay_answer == "y" || replay_answer == "no" || replay_answer == "n" do
print "Please answer with yes, y, no, or n: "
replay_answer = gets.chomp
end
## recoded this to a case, as I think it's much nicer :)
# determining whether to replay or to stop the loop
case replay_answer
when "yes", "y"
replay = true
puts "yes"
when "no", "n"
replay = false
puts "no"
replay_inner = false
end

I've modified a little your code, you should really use some decent editor with at least syntax errors highlighting. I've changed OR to AND condition in inner loop (comment in code) and added way of breaking from it when guessed number. I've also removed second occurence of code responsible of playing again, remember, DRY yourself.
replay = true
while replay
p replay
#Pre-var
print "What difficulty? (1 for easy, 2 for medium, or 3 for hard): "
difficulty = gets.chomp.to_i
until difficulty == 1 || difficulty == 2 || difficulty == 3 do
print "Please type either 1, 2, or 3: "
difficulty = gets.chomp.to_i
end
#Variables
if difficulty == 1
r = Random.new
number = r.rand(100..1000)
puts "You have 15 guesses. "
print "Guess a number with three digits: "
within_num = 50
elsif difficulty == 2
r = Random.new
number = r.rand(1000..10000)
puts "You have 15 guesses. "
print "Guess a number with four digits: "
within_num = 500
elsif difficulty == 3
r = Random.new
number = r.rand(10000..100000)
puts "You have 15 guesses. "
print "Guess a number with five digits: "
within_num = 5000
end
guess = ""
num_guess = 0
guess_array = Array.new
array_location = 0
count_through = 0
array_print = count_through - 1
replay_inner = true
#Body
puts number
while num_guess <= 14 && replay_inner == true #Keeping as <= 14 as to avoid unnecessarily rewriting code, still gives 15 guesses
guess = gets.chomp.to_i
if guess > number * 2
print "That is more than double the number. Guess again: "
elsif guess < number / 2
print "That is less than half the number. Guess again: "
elsif guess > number && guess < number + within_num #within_num: 50 for easy, 500 for medium, 5000 for hard
print "You are close. That is too big. Guess again: "
elsif guess < number && guess > number - within_num
print "You are close. That is too small. Guess again: "
elsif guess < number
print "That is too small. Guess again: " #Hinting the user to how close they are.
elsif guess > number
print "That is too big. Guess again: "
elsif guess == number
puts "Congragulations! You win!"
print "Your "
print guess_array.length
print " incorrect guesses were: "
if num_guess == 0
sleep(0.5)
print "... Oh."
else
while count_through < num_guess #Loop to relay user's guesses with a delay of 0.5 seconds
print guess_array[count_through]
if count_through == num_guess - 2
print ", and "
elsif count_through == num_guess - 1
puts ". "
else
print ", "
end
count_through += 1
sleep(0.5)
end
end
replay_inner = false # or just `break`, you have to break somehow from inner loop here
end
guess_array.push guess
num_guess += 1
#puts num_guess
#puts guess_array[array_location]
array_location += 1
if num_guess >= 15 && guess != number
puts "Sorry, you lost. "
print "Your "
print guess_array.size
print " guesses were: "
while count_through < num_guess
print guess_array[count_through] #Same as loop above; for when player fails to guess correctly
if count_through == num_guess - 2
print ", and "
elsif count_through == num_guess - 1
puts ". "
else
print ", "
end
count_through += 1
sleep(0.5)
end
end
end
puts "\nWould you like to play again? (yes/no)"
replay_answer = gets.chomp
until replay_answer == "yes" || replay_answer == "y" || replay_answer == "no" || replay_answer == "n" do
print "Please answer with yes, y, no, or n: "
replay_answer = gets.chomp
end
if replay_answer == "yes" || replay_answer == "y"
replay = true
replay_inner = true
puts "yes"
elsif replay_answer == "no" || replay_answer == "n" #Determining whether or not to replay
replay = false
replay_inner = false
puts "no"
end
end

Related

"Already initialized constant" warning [duplicate]

This question already has answers here:
What is the difference between a constant and a variable in Ruby?
(3 answers)
Closed 6 years ago.
Here's my code:
puts "Input a number."
Divisor = 2
inputNumber = gets.chomp
if inputNumber.to_i == 1 || inputNumber.to_i == 2
if inputNumber.to_i == 1
puts inputNumber + " is not a prime."
else
puts inputNumber + " is a prime."
end
else
while Divisor.to_i < inputNumber.to_i
if inputNumber.to_i%Divisor.to_i == 0
puts inputNumber + " is not a Prime as " + Divisor.to_s + " is a factor."
break
else
Divisor = Divisor.to_i + 1
end
end
puts inputNumber + " is a prime!"
end
I got the following error:
test1.rb:30: warning: already initialized constant Divisor
test1.rb:3: warning: previous definition of Divisor was here
What is wrong?
Constants and variables
Divisor is a constant. You want a variable : divisor. See this answer.
Notes
puts inputNumber + " is a prime!" is always called.
You could replace break by exit, or use a boolean variable.
Since you work with numbers, you could also call .to_i once, do your calculation, and just use .to_s to display results
You only have to check for factors in the range (2..Math.sqrt(inputNumber))
You don't have to check twice if inputNumber == 1
puts 'Input a number.'
divisor = 2
inputNumber = gets.chomp.to_i
prime = true
if inputNumber == 1
puts inputNumber.to_s + ' is not a prime.'
else
while divisor <= Math.sqrt(inputNumber)
if inputNumber % divisor == 0
puts inputNumber.to_s + ' is not a Prime as ' + divisor.to_s + ' is a factor.'
prime = false
break
else
divisor += 1
end
end
puts inputNumber.to_s + ' is a prime!' if prime
end
Alternative
A shorter alternative can be written with Enumerable#find. It executes the block with every element, stops as soon as the code in block returns a truthy value, and returns the element for which the block is truthy. If no element is found, it returns nil :
puts 'Input a number.'
number = gets.chomp.to_i
divisor = (2..Math.sqrt(number)).find { |i| number % i == 0 }
if number == 1
puts '1 is not a prime.'
elsif divisor
puts format('%d is not a prime as %d is a factor.', number, divisor)
else
puts format('%d is a prime!', number)
end

Solving foobar in Ruby?

So far I have been able to get the numbers to print properly but I don't know how to loop them.
puts "Please enter a number"
val1 = gets.to_i
val2 = val1 % 3
val3 = val1 % 5
def ordinal(a,b,c)
if b == 0 && c == 0
return "foobar"
elsif b == 0
return "foo"
elsif c == 0
return "bar"
else
return a
end
end
val5 = ordinal(val1,val2,val3)
puts "#{val5}"
I also made a loop that works but it won't recognize strings.
n = 0
x = gets.to_i
while n != x
puts "#{n}"
n = n + 1
end
How do I combine my method and loop? Or is there any other way to solve this? But I would prefer if you solve it using my code IF its any good off course so that I can understand it better.
As par your comments, the loop part is easy when leveraging Integer#times.
This is what you have that works for you:
puts "Please enter a number"
val1 = gets.to_i
val2 = val1 % 3
val3 = val1 % 5
def ordinal(a,b,c)
if b == 0 && c == 0
return "footer"
elsif b == 0
return "foo"
elsif c == 0
return "bar"
else
return a
end
end
val5 = ordinal(val1,val2,val3)
puts "#{val5}"
Right now you're just running through the final number. Try using the Integer#times to run a loop up to the number...:
puts "Please enter a number"
num = gets.to_i
num.times do |a|
a += 1 # counting is done from 0 to n-1
b = a % 3
c = a % 5
if b == 0 && c == 0
print "foobar "
elsif b == 0
print "foo "
elsif c == 0
print "bar "
else
print "#{a} "
end
end
print "\n"
Personally I would probably have written this a bit differently, but I guess it's sound enough.
I would probably write something messy because I hate long if...else statements...:
def foo_bar n
n.times {|i| i+=1; print( (i%15==0 && "FooBar ") || (i%3==0 && "Foo ") || (i%5 ==0 && "Bar ") || ("#{i} ") ) }
print "\n"
end
puts "Enter number:"
foo_bar gets.to_i
You can do this too:
def foobar n
n.times do |i|
i += 1
num = ""
num << "Foo" if i % 3 == 0
num << "Bar" if i % 5 == 0
num = i.to_s if num == ""
puts num
end
end
foobar 15
The logic is simpler than Myst's.
You don't need to test if it's a factor of 15.
Output:
1
2
Foo
4
Bar
Foo
7
8
Foo
Bar
11
Foo
13
14
FooBar
=> 15

Total newb here and other fizzbuzz issue

I'm trying to write a looping fizzbuzz code that ends with the user_input's number. So far the code works, but it loops the number of times you put in for the user_input, not end at the user_input's limit. For example, if I type in 25, it will loop 25 times, and not end at 25. How do I set the parameters/range?
Here is my code:
puts("Please select a number that is at least 25. This is the limit for the fizzbuzz game.")
user_input = gets().chomp().to_i
if
user_input < 25
puts("Please select a larger number.")
else
user_input >= 25
user_input = user_input
counter = 1
while counter < user_input
puts(counter)
counter = counter + 1
(1..user_input).step do |i|
if i % 3 == 0 && i % 5 == 0
puts("fizzbuzz")
elsif i % 3 == 0
puts("fizz")
elsif i % 5 == 0
puts("buzz")
else
puts(i)
end
end
end
end
It is optional to write () when you send no parameters to a method and usually discouraged
You shouldn't use else user_input >= 25, else is enough
user_input = user_input is totally useless line
while cycles with counters isn't the way rubists code, prefer iterators. Moreover, you shouldn't have while here at all
puts 'Please select a number that is at least 25. This is the limit for the fizzbuzz game.'
user_input = gets.chomp.to_i
if user_input < 25
puts 'Please select a larger number.'
else
1.upto(user_input) do |i|
if i % 3 == 0 && i % 5 == 0
puts 'fizzbuzz'
elsif i % 3 == 0
puts 'fizz'
elsif i % 5 == 0
puts 'buzz'
else
puts i
end
end
end
optionally, you can use case-when instead of multiple elsif statements:
puts 'Please select a number that is at least 25. This is the limit for the fizzbuzz game.'
user_input = gets.chomp.to_i
if user_input < 25
puts 'Please select a larger number.'
else
1.upto(user_input) do |i|
case
when [3, 5].all? { |n| i % n == 0 }; puts 'fizzbuzz'
when i % 3 == 0; puts 'fizz'
when i % 5 == 0; puts 'buzz'
else; puts i
end
end
end

If type is character I want to puts error

puts "Let's sum many numbers"
sum = 0
num = 0
while(num != 'x')
puts "Press a number and then Enter if you exit press 'x'"
num = gets.chomp
if num != 'x'
num = num.to_i
print "#{sum} + #{num} = "
sum += num
puts "#{sum}"
elsif num == 'x'
puts "Total sum is #{sum}"
break
else
puts "error!"
end
end
I want to make code to show error If user press char except 'x'.
How should I do?
Change your first if to a condition that checks if the input is a number, e.g.
if num =~ /\A[0..9]+\z/ # or /\A\d+\z/
The way your code is currently, all strings except 'x' are treated as number -- with value 0 in case they aren't really numbers:
'foobar'.to_i # => 0

How to Use Hashes to Store methods in Ruby?

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.

Resources