Guessing game for ruby - ruby

I'm currently trying to create a simply made guessing game, In the code there will be three set number (for now) that a person has to guess. If he/she guesses all the numbers correctly it puts, "Congrats, you win!"
Now as a beginning test i just wanted the user to guess one number correctly and the code gives back correct or incorrect.
random_guess = [1, 3, 5]
puts "Please Pick a number, 1-5"
pick_num = gets.chomp
if pick_num == random_guess = true
puts "Correct!"
else
puts "Incorrect!"
end
(I know this code is very beginner, i'm very new to ruby.) for some reason every time i run this program it puts incorrect.

Your if statement is wrong. It should be:
if random_guess.include? pick_num.to_i
Note that if you leave off the to_i the equality check will always fail because you're comparing the integer 3 against the string "3".
Just to help you a bit more. Since your end goal is to have the user guess all the numbers correctly. You can just loop on the same if statement I wrote above. And every time they guess a correct number you can remove it from the array like such:
random_guess.delete(pick_num.to_i)
Once the array is empty, the user has won.

Making random_guess random:
random_guess = []
3.times{random_guess << rand(1..5)}
random_guess
# => [5, 1, 4] # will be random in every other iteration
Checking if the number exists, you can either use index or include?:
if random_guess.index(pick_num.to_i) # alternatively random_guess.index(pick_num.to_i)
puts "Correct!"
else
puts "Incorrect!"
end
Why is your code always printing incorrect?
You are doing if pick_num == random_guess = true, which is a blunder. What is actually happening here is:
you are assigning true to random_guess. i.e. irrespective to what value (Array) random_guess holds, you are overwriting it with true.
Then you are comparing random_guess with pick_num.
So essentially you are doing this:
if pick_num == (random_guess = true) # say pick_num = "1"
# "1" == true # which is obviously false.
The correct conditional statement should be:
if pick_num == random_guess
However this will also print false every time. Reason?
pick_num is a string.
random_guess is an array that contains integer values.
You are comparing two different object types. So its always false.
Therefore the right way to solve this is checking whether the user entered value exists in the Array. For that you can use Array#index or Array#include?. Hence the statement in my proposed solution:
if random_guess.index(pick_num.to_i)
NOTE: pick_num.to_i converts pick_num (a character) into an integer. This is required as your array contains only integers and not characters.

Related

Optimize print output where i use check on zero. Ruby

Currently, I'm having print like this
print ((stamp_amount[0], 'first mark') unless stamp_amount[0].zero?), (', ' if !stamp_amount[0].zero? && !stamp_amount[1].zero?),
((stamp_amount[1], 'second mark') unless stamp_amount[1].zero?)
stamp_amount is an array with 2 integer values
Let's say in the current situation stamp_amount[0] = 10 and stamp_amount[1] = 3
Output preview:
10 first mark, 3 second mark
So if stamp_amount[0] = 0 the 10 first mark, part won't be show. Same if stamp_amount[1] = 0 the , 3 second mark part won't be shown
For me, it seems a little bit incorrect in terms of theory. Could you please suggest me the more correct or less painful print of this? :)
Cheers!
Your code is trying to join a sequence of up to two elements with a separator. The joining is a solved problem, see Array#join.
The problem can be then reduced to "how can I produce the correct sequence, given my stamp_amount input". Now this can be done in a thousand ways. Here's one:
def my_print(stamp_amount)
ary = [
!stamp_amount[0].zero? && stamp_amount[0],
!stamp_amount[1].zero? && stamp_amount[1],
].select{|elem| elem }
ary.join(', ')
end
my_print([10, 3]) # => "10, 3"
my_print([0, 3]) # => "3"
my_print([10, 0]) # => "10"
my_print([0, 0]) # => ""
Here's another
ary = []
ary << stamp_amount[0] unless stamp_amount[0].zero?
ary << stamp_amount[1] unless stamp_amount[1].zero?
ary.join(', ')
Here's yet another. This version can handle stamp_amount of any length.
ary = stamp_amount.reject(&:zero?)
ary.join(', ')
I'd go with the third, but the second one may be the easiest to understand for a beginner.
Use the select, as an alternative to reject (shown in part 3 of the answer by Sergio Tulentsev). It is just asa readable, and depending on the context and on the future changes to the code, you may prefer one versus the other.
puts stamp_amount.select{ |a| !a.zero? }.join(", ")
A few examples of inputs and outputs are:
stamp_amount output
--------------------------------------------------------------------------
10, 3 10, 3
10, 0 10
0, 3 3
0, 0 (prints an empty line, because the selected array is empty)
You're calculating zero? on index points more often than is needed, but the first thing I would look at refactoring here is the readability of the code. It might be nicer to calculate the message to print outside of the print method and explain what is happening with variable names.
# rubocop is going to complain about variable assignment like this
first_amount, second_amount = *stamp_amount
We can actually use the reason rubocop prefers the .zero? over == 0 or .empty? method to guide our development. zero? is in essence just empty? but it communicates the meaning of what you are attempting to do in a better manner. I would use this reasoning when assigning strings to variables that explain what they are doing.
some_name_that_explains_what_this_is_0 = "#{first_amount} piecu centu marka"
some_name_that_explains_what_this_is_1 = "#{second_amount} tris centu marka"
Your current code is confusing as you have the possibility of printing a string like "10 tris centu marka" which does not make lexical sense and probably not what you are after considering tis evaluates to 'second mark', which would pose an issue if the first value is zero. We also could reject zero integers before we start converting them to strings.
array = [1, 0].reject(&:zero?)
Now we can take the array and do something like:
string = []
array.each_with_index { |e, i| string << "#{e} #{Ordinalize.new(i).ordinalize} mark" }
message = string.join(', ')
print(message)
# ord class
class Ordinalize
def initialize(value)
#value = value
end
def ordinalize
mapping[#value]
end
def mapping
# acounting for zero index
['first', 'second']
end
end
where we are calculating the ordinalization and letting our new class handle the sentence structure for us.
Outputs:
[1, 0] => "1 first mark"
[0, 1] => "1 first mark"
[1, 2] => "1 first mark, 2 second mark"

How to do a simple test in ruby

I have some code below that gives square numbers. I also wrote a simple test that prints true or false.
numbers = (1..20)
test=numbers.each do |number|
puts number * number
end
puts test == [1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289,324,361,400]
I expect it to print true, but it puts false.
I was wondering if anyone could help to see why.
Try below code to get your answer :
numbers = (1..20)
test = numbers.map{|a| a*a}
puts test == [1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289,324,361,400]
You are using first test which contents 1..20. So you have to modify your code.
In your code numbers.each do |number| .. end returns the range (1..20) and that's the value that test will be assigned to after the loop through numbers is done. So even if you deleted the line puts number* number, test will still have the value (1..20). So using map would be the right choice to keep your values in an array. But you should first delete puts, because it shows the value on the screen but returns nil. So if you used map but didn't delete puts test will be assigned to an array of nils.

Converting to_i doesn't work (Ruby)

I'm pretty new to programming and i'm doing fundamentals on codewars.com and I'm having some trouble with this one. The objective is to take a group of integers, reverse them, and put them into an array. Here's my code. (I made the tf function to see what was going on in the code.)
def digitize(n)
answer = n.to_s.split(//).reverse!
def tf(it)
str_test = it.is_a? String
int_test = it.is_a? Integer
puts "String: #{str_test}"
puts "Integer: #{int_test}"
end
Array(answer)
unless answer.is_a? Integer
for item in answer
item.to_i
puts item
tf(item)
end
end
return answer
end
Sample test:
Test.assert_equals(digitize(35231),[1,3,2,5,3])
When tested, it returns:
1
String: true
Integer: false
3
String: true
Integer: false
2
String: true
Integer: false
5
String: true
Integer: false
3
String: true
Integer: false
Can one of you guys help me figure out where it goes wrong?
Assigning
item = item.to_i
Would fix the output in tf, but your returned answer would still be all strings. If you want to do this one by one like you're doing you would need to assign it back into the index of the array:
answer.each_with_index do |item, index|
answer[index] = item.to_i
end
Though, an even better way to do this would be with map (returns a new array) or map! (in-place):
# return this line (make it the last line in the method) or make sure
# to re-assign answer
answer.map(&:to_i)
# or do this one to use `answer` later on with all integers.
answer.map!(&:to_i)
(See this question about that &:to_i syntax).
It should also be noted (maybe), that Rubyists in general don't like for loops and prefer each loops.
Also, the line:
Array(answer)
doesn't modify the answer in place, and returns it cast to an array, so the line is doing nothing:
a = "1"
Array(a) # => ["1"]
a # => "1"
a = Array(a) # => ["1"]
a # => ["1"]
You also, don't even need to do this, since answer is already an array from where you split it (You could also have used chars instead of split(//)). The line unless answer.is_a?(Integer) will thusly never be true.
The last major thing, I see is that in newer versions of ruby, there's a built-in method to do all this, digits:
35231.digits # => [1, 3, 2, 5, 3]

Towers of Hanoi, Ruby conditional

I'm having trouble with my first if conditional, which checks to make sure the the new piece added is smaller than the one under/before it. My Towers of Hanoi game worked fine until I added it. Below is my code:
arrays = [[5,4,3,2,1],[],[]]
win = false
while win != true
puts "Choose a top piece: (1, 2, 3) "
top = gets.to_i
puts "Which stack to place this piece? (1, 2, 3)"
stack = gets.to_i
if (arrays[stack-1] == nil) ||
(arrays[stack-1][arrays[stack-1].count-1] > arrays[top-1][arrays[top-1][arrays[top-1].count]])
arrays[stack-1].push(arrays[top-1].pop)
else
"You need to follow the rules."
end
print arrays
if arrays[1] == [5,4,3,2,1] || arrays[2] == [5,4,3,2,1]
print "You're a winner!"
win = true
end
end
~
Below is the error I get. How do I perform my check and deal with my nil value arrays in a concise manner?
towers_hanoi:13:in `[]': no implicit conversion from nil to integer (TypeError)
from towers_hanoi:13:in `<main>'
Use the empty? method to determine if an array is empty. FYI, though, if you want to see if a variable has a nil value, use nil?
Also, the last method will help a ton here and subtracting 1 from the inputs right away will make the code more readable. Try this:
arrays = [[5,4,3,2,1],[],[]]
win = false
while win != true
puts "Choose a top piece: (1, 2, 3) "
stack_from = gets.to_i - 1
puts "Which stack to place this piece? (1, 2, 3)"
stack_to = gets.to_i - 1
if (arrays[stack_to].empty?) ||
(arrays[stack_to].last > arrays[stack_from].last)
arrays[stack_to].push(arrays[stack_from].pop)
else
"You need to follow the rules."
end
print arrays
if arrays[1] == [5,4,3,2,1] || arrays[2] == [5,4,3,2,1]
print "You're a winner!"
win = true
end
end
There are a lot of strange things going on in that if statement.
Definitely use Array#empty? to check if an array if empty. An empty array is not nil.
Secondly some of your array bracketing is way too convoluted, I'm not sure what you are trying to accomplish here but you are definitely going to be checking if nil > number in some cases:
(arrays[stack-1][arrays[stack-1].count-1] > arrays[top-1][arrays[top-1][arrays[top-1].count]])
I doubt this is what you are trying to do (since it will throw an error). I would take a minute to think about your logic and refactor. In Towers of Hanoi, you only need to worry about checking if the piece you are moving is less than the LAST piece on the stack you are moving to (which represents the top).
Use Array#last and you will be on your way to a much simpler solution.

Coderbyte Second Great Low - code works but is rejected

I'm currently working through the Coderbyte series to get better at Ruby programming. Maybe this is just a bug in their site (I don't know), but my code works for me everywhere else besides on Coderbyte.
The purpose of the method is to return the 2nd smallest and the 2nd largest elements in any inputted array.
Code:
def SecondGreatLow(arr)
arr=arr.sort!
output=[]
j=1
i=(arr.length-1)
secSmall=''
secLarge=''
while output.length < 1
unless arr.length <= 2
#Get second largest here
while (j<arr.length)
unless arr[j]==arr[j-1]
unless secSmall != ''
secSmall=arr[j]
output.push(secSmall)
end
end
j+=1
end
#get second smallest here
while i>0
unless arr[i-1] == arr[i]
unless secLarge != ''
secLarge=arr[i-1]
output.push(secLarge)
end
end
i-=1
end
end
end
# code goes here
return output
end
# keep this function call here
# to see how to enter arguments in Ruby scroll down
SecondGreatLow(STDIN.gets)
Output
Input: [1,2,3,100] => Output: [2,3] (correct)
Input: [1,42,42,180] => Output: [42,42] (correct)
Input: [4,90] => Output: [90,4] (correct)
The problem is that I'm awarded 0 points and it tells me that my output was incorrect for every test. Yet, when I actually put any inputs in, it gives me the output that I expect. Can someone please assist with what the problem might be? Thanks!
Update
Thanks to #pjs answer below, I realized this could be done in just a few lines:
def SecondGreatLow(arr)
arr=arr.sort!.uniq
return "#{arr[1]} #{arr[-2]}"
end
# keep this function call here
# to see how to enter arguments in Ruby scroll down
SecondGreatLow(STDIN.gets)
It's important to pay close attention to the problem's specification. Coderbyte says the output should be the values separated by a space, i.e., a string, not an array. Note that they even put quotes around their "Correct Sample Outputs".
Spec aside, you're doing way too much work to achieve this. Once the array is sorted, all you need is the second element, a space, and the second-to-last element. Hint: Ruby allows both positive and negative indices for arrays. Combine that with .to_s and string concatenation, and this should only take a couple of lines.
If you are worried about non-unique numbers for the max and min, you can trim the array down using .uniq after sorting.
You need to check condition for when array contains only two elements. Here is the complete code:
def SecondGreatLow(arr)
arr.uniq!
arr.sort!
if arr.length == 2
sec_lowest = arr[1]
sec_greatest = arr[0]
else
sec_lowest = arr[1]
sec_greatest = arr[-2]
end
return "#{sec_lowest} #{sec_greatest}"
end

Resources