How to create a new struct when an error occurs - ruby

I am trying to make a text based game using ruby to learn the language, I want to throw an error if a player enters a string incorrectly, it should ask them to start the character making process over again.
My issue is that when I re-call the function to make the character, it creates a new struct and keeps the old one. So at the end I have something that is printing the new corrected struct as well as the old wrong struct. I obviously only want to keep the correct one so how do I go about getting rid of the old one?
Character = Struct.new(:name,:race,:alignment,:str,:dex,:int,:const,:char,:wis)
def makeChar()
newChar = Character.new()
until i == 6 do
case skillArray[i]
when "str"
newChar.str = rolledStats[i]
when "int"
newChar.int = rolledStats[i]
when "dex"
newChar.dex = rolledStats[i]
when "wis"
newChar.wis = rolledStats[i]
when "const"
newChar.const = rolledStats[i]
when "char"
newChar.char = rolledStats[i]
else
puts("It appears you have entered an invalid value for a skill")
puts("Please try again but enter only the following: str, dex, int, char, wis, const")
makeChar()
end
i += 1
end
puts("Our brave adventurer's name is #{name} the #{race}")
puts("#{name}'s stats are as follows:\n Str: #{newChar.str} \n Dex: #{newChar.dex}
Const: #{newChar.const} \n Int: #{newChar.int} \n Wis: #{newChar.wis} \n Char: #{newChar.char}")
print("#{name} prepare for your quest!")

There are cases when you need to 'get rid of' variables but this doesn't sound like one.
Just see this example:
word = "test"
def guess_word
guess = gets.chomp
exit if word == guess
end
loop do
puts "guess word"
guess_word
end
This program runs on a loop. Each iteration, it prompts the user for a word. If the word is correct, it exits. You can see in guess_word that the guess variable is assigned each time the method is called.
If you have a method which defines a variable, that variable's scope is just in that method unless there is a variable with the same name in a higher scope.

Related

Ruby if/else flow

Good morning,
I'm starting on Ruby, wanted to create a small tool that fetches my public IP and sends it over by email. I'm stumbling on a basic problem with a string comparison and an if/else block that won't process.
Code is quite simple (see below). The problem comes at the string comparison at line 21. What I'd want is that whenever the IP address changed from what was recorded in a file, the new IP overwrites the one in the file, and other actions (not in the current code) will ensue.
It looks like that sometimes the comparison is not executed, thus the statements following the if do not execute.
I'd like to understand why is that. Line 21 has been changed at times for if (!oldIP == ip) to if (oldIP != ip), same result. I also suspect the return value to be ignored (dead code path ?) sometimes.
Here's the code
#!/usr/bin/env ruby
require "net/http"
puts "\e[H\e[2J"
def FetchIPAddress()
oldIP = ""
if File::exists?('/tmp/wanipaddress.txt')
iFile = File.open('/tmp/wanipaddress.txt')
oldIP = iFile.read()
iFile.close()
end
oFile = File.open('/tmp/wanipaddress.txt', "w+")
ip = Net::HTTP.get(URI("https://api.ipify.org"))
puts "old = " + oldIP
puts "new = " + ip
if (!oldIP == ip)
puts "changed"
oFile.puts ip
oFile.close()
else
ip = "unchanged"
puts "unchanged"
end
return ip
end
Really, I do see some erratic behaviour here; I suspect it's just me being a newbie with Ruby.
Your file likely contains a line break.
Try this
if old_ip.chomp != ip.chomp
...
end
chomp removes a trailing linebreak.
Best use p to print values for debugging, this will escape whitespace and thus make trailing linebreaks visible. You should never use puts for debugging.
And here is why !a == b will never ever work.
!a == b is the same as a.!().==(b) and executed as follows
first the ! method is called on object a, which returns false for a string
then ==(b) method is called on the resulting boolean
and since a boolean is never equal to a string the comparison will always fail
The problem with this line (if (!oldIP == ip)) is pretty simple - what you want it to do is check whether the oldIP is different from the new IP.
What you do instead is take oldIP, check whether it's true or false, then negate it (that's what the ! does), then compare it to ip. Since oldIP is a String, and thus always true, which gets negated to false, and ip is (I'm guessing) a String, it will always be true, your line essentially reads if (false == true).
To solve this problem, you could use the != comparison operator, like if oldIP != ip, or, if you like the negation, if !(oldIP == ip).

Why do `while` loops never check user input, whereas `if` statements do?

An if statement such as:
if first_name || first_name.length == 0
puts "You can't leave the first name blank, try again: "
first_name = gets
end
functions correctly, because it asks for user input, and when tested by not entering any input (including spaces), it prints an error message, followed by prompting for user input, following which, However, when I attempt to use the same in a while loop:
while first_name || first_name.length == 0
puts "You can't leave the first name blank, try again: "
first_name = gets
end
the loop doesn't function correctly.
The loop does the following instead:
• Print the error message above (OK)
• Asks for user input (OK)
• As soon as user input is received, the loop executes once again (Wrong).
It isn't testing the length of the user's (now correct) input, and then allowing the rest of the application to carry out its tasks.
I also tried unless, nil?, and so on to rectify the loop's error using an if statement inside the loop, with no success. I thought the while loop would evaluate first_name again once the error was corrected, and find that its length was no longer zero, however, that turned out not to be the case.
In Java, to say something isn't equal to something else (in terms of strings, for example), I can use ! before the variable name. Can I do this in Ruby?
You can try this:
while first_name.length == 0 do
puts "You can't leave the first name blank, try again: "
first_name = gets
end
The way while cycle works is it evaluates the statement after while keyword. If it is true then it runs the block. And after that just the same. In your case statement is first_name || first_name.length == 0 which always will be true as first_name is always present. That's the reason it will loop again each time whatever you would input.
I think your condition is just wrong : the condition
first_name || first_name.length == 0
Will be true if first_name is non-nil, this will always be the case since you are setting it to a string.
After some further digging on the Internet, I gave .chomp a go, to see what it would do, as I found out that when gets is used, it takes your input, but apparently also inserts an \n escape sequence, when Enter is pressed, meaning that my loop didn't function as required.
By using gets.chomp, in conjunction with .empty? I was able to remove this escape sequence from my input, and have the loop function correctly.
Here's the correct code, which prevents an infinite loop:
first_name = gets.chomp
while first_name.empty? do
puts "You can't leave the first name blank, try again: "
first_name = gets.chomp
end

Does ruby's case statement fall through?

I am writing a hangman game in ruby and I wanted to use a case statement to determine which body part to place corresponding to a number of incorrect guesses. I made this game using a board class I use for other games like chess and connect-4 because I have a method which serializes the board class allowing me to save and load the game without any extra code. For the game to be saved, I needed some way of determining the number of incorrect guesses for the hangman without adding extra variables to the board class. To solve this I used an instance variable on the board class called history, which can be used to push moves from the game to the boards history. When the board gets serialized, the history is saved as well, which can be read by the game and used to determine incorrect guesses.
In the hangman game, I have a method called read history (which I use for all the games since it solves the serialization issue described above). The read_history method is responsible for reading the past guesses, display them, and determine the number of incorrect guesses. This number is then passed to a hang method which determines which body parts of the hangman to add.
def hang(incorrect)
case incorrect
when 0
#hangman = [" ", " ", " "]
break
when 7
#hangman[2][2] = '\\'
when 6
#hangman[2][0] = '/'
when 5
#hangman[2][1] = '*'
when 4
#hangman[1][2] = '\\'
when 3
#hangman[1][0] = '/'
when 2
#hangman[1][1] = '|'
when 1
#hangman[0][1] = 'o'
end
end
If I were writing this in java, and a value of 5 were passed to the above method, it would read the statement until it hit "when 5" or in java terms "case 5:". It would notice that there is not a break in the statement and will move down the list executing the code in "case 4:" and repeating until a break is found. If 0 were passed however it would execute the code, see the break, and would not execute and other statements.
I am wondering if Ruby is capable of using case statements the way java does in the way that they fall through to the next statement. For my particular problem I am aware that I can use a 0.upto(incorrect) loop and run the cases that way, but I would like to know the similarities and differences in the case statement used in ruby as opposed to the switch-case used in java
No, Ruby's case statement does not fall through like Java. Only one section is actually run (or the else). You can, however, list multiple values in a single match, e.g. like this site shows.
print "Enter your grade: "
grade = gets.chomp
case grade
when "A", "B"
puts 'You pretty smart!'
when "C", "D"
puts 'You pretty dumb!!'
else
puts "You can't even use a computer!"
end
It's functionally equivalent to a giant if-else. Code Academy's page on it recommends using commas to offer multiple options. But you can still won't be able to execute more than one branch of logic.
It does not fall through.
Ruby just doesn't have the same behavior as Java for this type of statement.
If you want to simulate the fall through behavior, you can do something like this:
def hang(incorrect)
#hangman = [" ", " ", " "]
#hangman[2][2] = '\\' if incorrect > 6
#hangman[2][0] = '/' if incorrect > 5
#hangman[2][1] = '*' if incorrect > 4
#hangman[1][2] = '\\' if incorrect > 3
#hangman[1][0] = '/' if incorrect > 2
#hangman[1][1] = '|' if incorrect > 1
#hangman[0][1] = 'o' if incorrect > 0
#hangman
end

ruby find and replace portion of string

I have a large file in a ruby variable, it follows a common pattern like so:
// ...
// comment
$myuser['bla'] = 'bla';
// comment
$myuser['bla2'] = 'bla2';
// ...
I am trying to given a 'key' replace the 'value'
This replaces the entire string how do I fix it? Another method I thought is to do it in two steps, step one would be to find the value within the quotes then to perform a string replace, what's best?
def keyvalr(content, key, value)
return content.gsub(/\$bla\[\'#{key}\'\]\s+\=\s+\'(.*)\'/) {|m| value }
end
The .* is greedy and consumes as much as possible (everything until the very last '). Make that . a [^'] then it is impossible for it to go past the first closing '.
/(\$bla\[\'#{key}\'\]\s+\=\s+\')[^']*(\')/
I also added parentheses to capture everything except for the value, which is to be replaced. The first set of parens will correspond to \1 and the second to \2. So that you replace the match of this with:
"\1yournewvaluehere\2"
I'd use something like:
text = %q{
// ...
// comment
$myuser['bla'] = 'bla';
// comment
$myuser['bla2'] = 'bla2';
// ...
}
from_to = {
'bla' => 'foo',
'bla2' => 'bar'
}
puts text.gsub(/\['([^']+)'\] = '([^']+)'/) { |t|
key, val = t.scan(/'([^']+)'/).flatten
"['%s'] = '%s'" % [ key, from_to[key] ]
}
Which outputs:
// ...
// comment
$myuser['bla'] = 'foo';
// comment
$myuser['bla2'] = 'bar';
// ...
This is how it works:
If I do:
puts text.gsub(/\['([^']+)'\] = '([^']+)'/) { |t|
puts t
}
I see:
['bla'] = 'bla'
['bla2'] = 'bla2'
Then I tried:
"['bla'] = 'bla'".scan(/'([^']+)'/).flatten
=> ["bla", "bla"]
That gave me a key, "value" pair, so I could use a hash to look-up the replacement value.
Sticking it inside a gsub block meant whatever matched got replaced by my return value for the block, so I created a string to replace the "hit" and let gsub do its "thang".
I'm not a big believer in using long regex. I've had to maintain too much code that tried to use complex patterns, and got something wrong, and failed to accomplish what was intended 100% of the time. They're very powerful, but maintenance of code is a lot harder/worse than developing it, so I try to keep patterns I write in spoon-size pieces, having mercy on those who follow me in maintaining the code.

Ruby: String Comparison Issues

I'm currently learning Ruby, and am enjoying most everything except a small string comparason issue.
answer = gets()
if (answer == "M")
print("Please enter how many numbers you'd like to multiply: ")
elsif (answer. == "A")
print("Please enter how many numbers you'd like to sum: ")
else
print("Invalid answer.")
print("\n")
return 0
end
What I'm doing is I'm using gets() to test whether the user wants to multiply their input or add it (I've tested both functions; they work), which I later get with some more input functions and float translations (which also work).
What happens is that I enter A and I get "Invalid answer."The same happens with M.
What is happening here? (I've also used .eql? (sp), that returns bubcus as well)
gets returns the entire string entered, including the newline, so when they type "M" and press enter the string you get back is "M\n". To get rid of the trailing newline, use String#chomp, i.e replace your first line with answer = gets.chomp.
The issue is that Ruby is including the carriage return in the value.
Change your first line to:
answer = gets().strip
And your script will run as expected.
Also, you should use puts instead of two print statements as puts auto adds the newline character.
your answer is getting returned with a carriage return appended. So input "A" is never equal to "A", but "A(return)"
You can see this if you change your reject line to print("Invalid answer.[#{answer}]"). You could also change your comparison to if (answer.chomp == ..)
I've never used gets put I think if you hit enter your variable answer will probably contain the '\n' try calling .chomp to remove it.
Add a newline when you check your answer...
answer == "M\n"
answer == "A\n"
Or chomp your string first: answer = gets.chomp

Resources