Ruby: Condition as a variable does not work - ruby

Just started learning looping and flow control in Ruby and got stuck with this exercise. I've been testing and searching for answers but did not find any so I'm posting here.
If I have my code setup like this:
ask_play = ''
loop do
print "Play?: "
ask_play = gets.chomp
break if (ask_play == 'n') || (ask_play == 'N')
end
Then I exit out of the loop after entering n or N.
However, if I have my code setup like this:
ask_play = ''
play_stop = (ask_play == 'n') || (ask_play == 'N')
loop do
print "Play?: "
ask_play = gets.chomp
break if play_stop
end
The condition does not seem to work. I still keep on looping even after typing in n or N and I'm just puzzled why.

play_stop = (ask_play == 'n') || (ask_play == 'N') gets evaluated before the loop begins. It sets the play_stop variable to false because the ask_play variable that you assigned to an empty string is neither n nor N.
It looks like you want to encapsulate the logic of the condition. You can create a method and pass a value to it.
def play_stop(input)
input == 'n' || input == 'N'
end
loop do
print "Play?: "
ask_play = gets.chomp
break if play_stop(ask_play)
end

You could use a proc to encapsulate the condition:
ask_play = ''
play_stop = -> { ask_play == 'n' || ask_play == 'N' }
loop do
print "Play?: "
ask_play = gets.chomp
break if play_stop.call
end
You could also pass the string to the proc instead of referring to the local variable:
play_stop = -> (s) { s == 'n' || s == 'N' }
loop do
print "Play?: "
break if play_stop.call(gets.chomp)
end
Procs also work nicely in case statements:
play_stop = -> (s) { s == 'n' || s == 'N' }
loop do
print "Play?: "
case gets.chomp
when play_stop
break
end
end

Related

Stuck in while loop even though condition has been met

I'm writing this script to build a tic-tac-toe game. This is only the beginning (no turns yet). I want to let the user input again if the previous input is invalid.
def display_board(board)
first_row = " #{board[0]} | #{board[1]} | #{board[2]} "
second_row = " #{board[3]} | #{board[4]} | #{board[5]} "
third_row = " #{board[6]} | #{board[7]} | #{board[8]} "
row_divider = "-----------"
puts first_row
puts row_divider
puts second_row
puts row_divider
puts third_row
end
def valid_move?(board,index)
if (index >= 0) && (index <= board.length - 1) && (position_taken?(board,index) != FALSE)
return TRUE
else
return FALSE
end
end
def input_to_index(user_input)
index = user_input.to_i - 1
return index
end
def move(board, index, character = "X")
board[index] = character
end
def position_taken?(board,index)
if (board[index] == "X") || (board[index]=="O")
return FALSE
end
end
def turn(board)
puts "Please enter 1-9:"
user_input = gets.strip
index = input_to_index(user_input)
while valid_move?(board,index) == FALSE
puts "invalid"
turn(board)
end
move(board, index, character = "X")
display_board(board)
end
I am stuck on the while loop. If I input an invalid input and then a valid input, it runs through the while loop instead of ending the program. It should be true. The problem is fixed if I use an if statement instead of a while loop, but I want to learn to use while loops better.
Because you call "turn" (internal) from "turn" (external) and then the internal "turn" called is valid but the board and index on the external
"turn" didn't change.
try this:
def turn(board)
loop do
puts "Please enter 1-9:"
user_input = gets.strip
index = input_to_index(user_input)
if valid_move?(board,index) == TRUE
break
end
puts "invalid"
end
move(board, index, character = "X")
display_board(board)
end

How to break out of a loop by writing the same word 3 times in a row (word+return key ,3 times)?

I have to break out of a loop by typing the same word "BYE" 3 times in a row and i'm out of ideas, this is how i tried but its not working:
the test:
elsif var == 'BYE' * 3
break
the original code:
while true
timeVar = rand(1930..1950)
inputVar = gets.chomp
if inputVar == inputVar.downcase
puts ' HUH?! SPEAK UP SONNY!!! '
elsif inputVar == inputVar.upcase && inputVar != 'BYE'
puts 'NO, NOT SINCE ' + timeVar.to_s + ' !'
elsif inputVar == 'BYE'
break
end
end
and the working solution implemented :
bye_count = 0
while bye_count < 3 do
time_grandma = rand(1930..1950)
input_var = gets.chomp
if input_var == "BYE" then
bye_count += 1
else
bye_count = 0
end
if input_var == input_var.downcase
puts ' HUH?!? SPEAK UP SONNY!!'
elsif input_var == input_var.upcase
puts ' NO, NOT SINCE ' + time_grandma.to_s + ' !'
end
end
Adjust your code to look something like this.
bye_count = 0
while bye_count < 3 do
input_var = gets.chomp
# ...
if input_var == "BYE" then
bye_count += 1
else
bye_count = 0
end
end

ruby returning true if a string consists an "a" and then a "z"

I need help on Writing a method that takes a string in and returns true if the letter "z" appears within three letters after an "a". You may assume that the string contains only lowercase letters. here's what I have:
def nearby_az(string)
string.downcase!
i = 0
while i < string.length
if (string[i] == "a" && string[i] == "z")
true
else
false
end
end
end
puts('nearby_az("baz") == true: ' + (nearby_az('baz') == true).to_s)
puts('nearby_az("abz") == true: ' + (nearby_az('abz') == true).to_s)
puts('nearby_az("abcz") == true: ' + (nearby_az('abcz') == true).to_s)
puts('nearby_az("a") == false: ' + (nearby_az('a') == false).to_s)
puts('nearby_az("z") == false: ' + (nearby_az('z') == false).to_s)
puts('nearby_az("za") == false: ' + (nearby_az('za') == false).to_s)
A regular expression would be the best for this. Try
def nearby_az(string)
(string =~ /a.{0,2}z/) != nil
end
EDIT:
as per the "if statement required requirement" :)
def nearby_az(string)
if (string =~ /a.{0,2}z/) != nil
return true
end
return false
end
The way this code works is it searches the input string for an "a". After that, the period indicates that you can have any character. After that you have {0,2} which is a modifier of the period indicating you can have 0 to 2 of any character. After this you must have a "z", and this fulfills your must have a z within 3 characters of an "a".
I've saved this regex to regex101 here so you can try various inputs as well as change the regular expression around to understand it better.
To fix you code you need to:
increment i at the end of the loop.
search the z letter within the 3 next letters
return true when condition is met
return false when getting out of the loop
Here is what it should look like:
def nearby_az(string)
string.downcase!
i = 0
while i < string.length do
return true if string[i] == "a" && string[i+1,3].include?(?z)
i+=1
end
return false
end

Why does the else condition of my if-statement not work?

I wrote a tic-tac-toe program. The problem I am experiencing is that in my if statement, which allows the user enter his/her desired coordinate, my else condition is not working. The else condition is in place in case the user enters a coordinate not on the board.
This is my code:
class Game
def initialize
#board=Array.new
#board[1]="1 __|"
#board[2]="__"
#board[3]="|__"
#board[4]="\n2 __|"
#board[5]="__"
#board[6]="|__"
#board[7]="\n3 |"
#board[8]=" "
#board[9]="| "
#turn="o"
#win_status = false
end
def turn
#turn
end
def show_board
puts " 1 2 3"
#board.each do |i|
print i
end
puts ""
end
def set_turn #switches turns
if #turn == "x"
#turn = "o"
else #turn == "o"
#turn = "x"
end
end
def make_move
puts "Enter x coordinate"
x=gets.to_i
puts "Enter y coordinate"
y=gets.to_i
if y==1 && x==1
#board[1]="1 _"+#turn+"|"
elsif y==2 && x==1
#board[2]="_"+#turn
elsif y==3 && x==1
#board[3]="|_"+#turn
elsif y==1 && x==2
#board[4]="\n2 _"+#turn+"|"
elsif y==2 && x==2
#board[5]="_"+#turn
elsif y==3 && x==2
#board[6]="|_"+#turn
elsif y==1 && x==3
#board[7]="\n3 "+#turn+"|"
elsif y==2 && x==3
#board[8]=" "+#turn
elsif y==3 && x==3
#board[9]="| "+#turn+" \n"
else
"You entered an invalid coordinate"
end
end
def win_combo
return [[#board[1][4] + #board[2][1] + #board[3][2]], [#board[4][5] + #board[5][1] + #board[6][2]], [#board[7][5] + #board[8][1] + #board[9][2]],[#board[1][4] + #board[4][5] + #board[7][5]], [#board[2][1] + #board[5][1] + #board[8][1]], [#board[3][2] + #board[6][2] + #board[9][2]], [#board[1][4] + #board[5][1] + #board[9][2]], [#board[3][2] + #board[5][1] + #board[7][5]]]
end
def check_win
#if some row or column or diagonal is "xxx" or "ooo" then set #win_status = true
self.win_combo.each do |arr|
str = arr.join
if str == "xxx"
puts "X Wins!"
return true
elsif str == "ooo"
puts "O Wins!"
return true
end
end
return false
end
g = Game.new
while g.check_win != true
g.show_board
g.set_turn
g.make_move
end
end
You are just returning the string: "You entered an invalid coordinate".
I suspect that you want to display it using:
puts "You entered an invalid coordinate"
Otherwise it is passed as the result of g.make_move and then ignored.
I'm assuming you would like to print: "You entered an invalid coordinate" to the console in the event of an invalid x, y coordinate. You need to add a method to that statement like:
else
puts "You entered an invalid coordinate"
end
Or:
else
abort "You entered an invalid coordinate"
end
It looks you you forgot to use puts or print in front of your "You entered an invalid coordinate" string. As it is currently written it is returned from the method.
In Ruby, the return value of a method is the value returned by the last statement evaluated. For example, both of these methods will return the same value if x=3:
def square_example(x)
if x ==3
x_squared = 9
end
end
def square_example2(x)
if x == 3
x_squared = 9
end
return x_squared
end
For simplicity of testing you might try using explicit returns so that you can easily tell what it is you are returning from the method. Or (as a beginner with Ruby myself) you could add in a puts statement with each if/else result so that you can easily monitor the results of each move and then remove those puts lines when you know everything is working properly.
Looks like this is a misinterpretation of the site below, but if you're interested in the difference between 'and' and '&&' you should check out the comments below.
From: http://www.tutorialspoint.com/ruby/ruby_operators.htm
You will want to use "and" instead of "&&", an example:
if y==1 and x==1
# do move
elsif y==2 and x==1
# do move
.
.
.
else
"invalid coordinate"
end
The "&&" operator will check that the values on either side of it are nonzero. If they are both nonzero then it will return true. In your case it is doing the operation
false && false
Where false != 0, so it returns true.
Here is another discussion of this: http://archive.railsforum.com/viewtopic.php?id=27353

Paper, Scissor, Rock game in Ruby. When an array changes data, it changes the data of another array (¿?)

This is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError
end
def self.winner(player1, player2)
if ((player1[1] == 'R') && (player2[1] == 'S') ||
(player1[1] == 'S') && (player2[1] == 'P') ||
(player1[1] == 'P') && (player2[1] == 'R'))
return player1
elsif ((player1[1] == 'R') && (player2[1] == 'P') ||
(player1[1] == 'S') && (player2[1] == 'R') ||
(player1[1] == 'P') && (player2[1] == 'S'))
return player2
elsif ((player1[1] == 'R') && (player2[1] == 'R') ||
(player1[1] == 'S') && (player2[1] == 'S') ||
(player1[1] == 'P') && (player2[1] == 'P'))
return player1
end
end
def self.tournament_winner(tournament)
player1 = Array.new
player2 = Array.new
nextround = Array.new
while tournament.length != 1 do
tournament.each_with_index {|item, index|
if (index%2!=0)
player2[0] = item[0]
player2[1] = item[1]
elsif (index%2 ==0)
player1[0] = item[0]
player1[1] = item[1]
else
puts 'bananas'
end
if (index%2!=0)
nextround[(index-1)/2] = winner(player1, player2)
end
}
tournament=nextround
end
return tournament
end
end
RockPaperScissors.tournament_winner([["num1", "R"], ["num2", "P"], ["num3", "P"], ["num4", "R"]])
Well, the last line is an execution launch. This code makes a tournament of rock, paper scissors. It takes as input an array of arrays with each character and its attack, and it has to return the array with the champion and its attack.
The tournament is num1 vs num2 (num2 wins), and num3 vs num4 (num3 wins). Then the final is Num2 vs Num3, and in this stablemate wins the first guy in the array (Num2).
It seems overcomplicated because the code has to work with any number of characters, as long as their number is base2 (2, 4, 8, 16 characters..., etc).
My problem is next (debug the code and you will see). When it changes the value of the array 'Player1' or 'Player2', it also changes the value in the array 'nextround', even if it is not in that line!
That is not suppose to happen!
By the way, I am learning Ruby so it may be a really stupid failure.
why does this need to be true?
"It seems overcomplicated because the code has to work with any number of characters, as long as their number is base2 (2, 4, 8, 16 characters..., etc)."
Rather having player1 and player2 be arrays, I would rewrite them to be instances of a class Player. Then write methods in the Player class so you can call player1.hand and it returns 'S' || 'R' || 'P'
that way you can store how many wins a player has on the player object,
things I'd look into learning more about
case/when statements
the special initialize method
attrs_accessor (used for making data accessible across classes)
modules
also i've seen it done and i may be wrong on this, but generally you don't put classes inside classes.

Resources