Input only open for a certain time in Ruby - ruby

I try to programm Snakes with ruby.
The first problem I encountered was to get input with out pressing enter at the end all the time. Luckily I found a solution for that at How to get a single character without pressing enter?.
The problem I am stuck with now is, that I want to have the input from the user only open for a certain amount of time. In order to let the snake keep on going if no change in direction is demanded by the user.
The code I got looks like this:
system("stty raw -echo")
str = STDIN.getc
ensure
system("stty -raw echo")
end
p str.chr
case str
when "z"
velocityX = -1
velocityY = 0
when "q"
velocityY = -1
velocityX = 0
when "d"
velocityY = 1
velocityX = 0
when "s"
velocityX = 1
velocityY = 0
when "e"
puts "Bye"
on = false
else
puts "Not found"
end
x += velocityX
y += velocityY
It tried with an if command around the input but that wouldn't work. I also tried looking it up on stack overflow but it seems that no one asked that question before. The only thing I found was the the sleep() command but I seems like that can't help either.
Any help would be highly appreciated.

Use Timeout.
For example, if you want to wait 2 seconds, you could do something like this:
require 'timeout'
begin
system("stty raw -echo")
str = Timeout::timeout(2) { STDIN.getc }
rescue Timeout::Error
str = "no input"
ensure
system("stty -raw echo")
end

Related

Trigger a Loop to pause and continue when space bar is pressed

I want to pause the running loop when i hit space and resume it again on doing so. How do i do it?
require 'rubygems'
require 'rest-client'
URL="some url here"
data1 = 0
data2 = 0
sign = ["up","down","equal"]
i=3
while true
response = RestClient.get(URL)
arr = (response.body).split(/"/)
data2=arr[9].to_i
if data2>data1
i = 0
elsif data2<data1
i = 1
else data2 == data1
i = 2
end
puts "#{arr[9]} #{sign[i]}"
data1=arr[9].to_i #marker<<<---Here
end
Simpler saying i want the loop to come to the marker and not run again till space is pressed.
Edit
I tried putting a puts there but obviously it pauses there and waits for me to give an input every time. Please try to make things as less complex as possible. I am kind of a beginner.
Use threads? This is not a direct answer but will give you a picture of how to use them.
$key_hit = false
t1 = Thread.new{
loop{
puts "Hello"
break if $key_hit
}
}
t2 = Thread.new {
x = gets
$key_hit = true
}
t1.join
t2.join
puts "Done, exiting"

Ruby program for searching words in a file

I started with Ruby yesterday, I only have some experience with C.
Now I'm trying to write a program that gets a file and a word to search in that file from ARGV, and prints how many times the word appeared. Got rid of any error, but it prints 0 anyway when I test it.
if ARGV.size !=2
puts "INSERT A FILE AND A WORD OR A CHAR TO SEARCH FOR"
exit 1
else
file = File.open(ARGV[0], mode = "r")
word = ARGV[1]
if !file
puts "ERROR: INVALID INPUT FILE"
exit 1
end
while true
begin
i = 0
count_word = 0
string = []
string[i] = file.readline
if string[i].upcase.include? word.upcase
count_word += 1
end
i += 1
rescue EOFError
break
end
end
print "The word searched is ", word, " Frequency: ", count_word, "\n"
end
I hope you could tell me what's wrong (I believe I do something wrong when counting), thanks in advance.
A great thing about Ruby it that it operates on a way higher level of abstraction. Here is a snippet that does what you want:
if ARGV.size != 2
puts "Provide file to be searched in and word to be found"
exit 1
end
file = ARGV[0]
word = ARGV[1]
count = 0
File.open(file, 'r').each { |line| count += 1 if line.downcase.include? word.downcase }
puts "The word searched is #{word} Frequency: #{count}"
As you can see, the language provides a lot of features like string interpolation, enumeration of the file contents, etc.
There is a handful of problems with the code you provided. From styling issues like indentation, to incorrect assumptions about the language like the if !file check and strange decisions overall - like why do you use a list if you want only the current line.
I suggest you to look at http://tryruby.org/ . It is very short and will get you a feel of the Ruby way to do things. Also it covers your question (processing files).
As a general note when you post a question on stackoverflow, please include the code in the question, rather than link to an external page. This way people can read through it faster, edit it and the code wont be lost if the other site goes down. You can still link to external pages if you want to show the snippet in action.
Hope this will help you, the error that you did is that you included this part:
i = 0
count_word = 0
string = []
into the while loop, which every time resets your counter to zero even if it found the word, so to correct this error here what you should do:
if ARGV.size !=2
puts "INSERT A FILE AND A WORD OR A CHAR TO SEARCH FOR"
exit 1
else
file = File.open(ARGV[0], mode = "r")
word = ARGV[1]
if !file
puts "ERROR: INVALID INPUT FILE"
exit 1
end
i = 0
count_word = 0
string = []
while true
begin
string[i] = file.readline
if string[i].upcase.include? word.upcase
count_word += 1
end
i += 1
rescue EOFError
break
end
end
print "The word searched is ", word, " Frequency: ", count_word, "\n"
end

Naming a new file with timestamp?

I'm trying to write a simple command line program that lets me keep track of how many times I get distracted during a study session.
I'm getting an argument error when I run this code saying my file.open has invalid arguments. If I want to name each new_session file with a timestamp, what would be a simple solution?
def first_question
puts "Would you like to start a new session?"
answer = gets.chomp
answer = answer.downcase
if answer == "yes"
new_session
else
puts "Ok. Would you like to review a previous session?"
prev_session = gets.chomp
prev_session.downcase
if prev_session == "yes"
#GET AND REVIEW PREVIOUS SESSIONS
elsif prev_session == "no"
puts "Well if you don't want a new session, and you don't want to review your old sessions, then you're SOL."
else
"That's not an acceptable response."
first_question
end
end
end
def new_session
distractions = 0
d = File.open("Session"+Time.now.to_s , 'w'){|f| f.write(distractions) }
puts "What would you like to do (add track(s) or review tracks)?"
request = gets.chomp
request.downcase
if request == "add track"
distractions = distractions.to_i + 1
puts "You have #{distractions} tracks in this session."
elsif request == "add tracks"
puts "How many times have you been distracted since we last met?"
answer = gets.chomp
distractions = distractions.to_i + answer.to_i
puts "You have #{distractions} tracks."
elsif request == "review tracks"
puts distractions
end
File.open( d , 'w') {|f| f.write(distractions) }
end
first_question
Most of your code is messy and redundant. The problem you are referring to, though, comes from here:
d = File.open("Session"+Time.now.to_s , 'w'){|f| f.write(distractions) }
d will be the number of bytes written to the file and thus a Fixnum. You can't open a Fixnum which you're trying to do in the last line of the function.
Further,
request = gets.chomp
request.downcase
The second line here does nothing.
You have two File.open statements:
d = File.open("Session"+Time.now.to_s , 'w'){|f| f.write(distractions) }
and:
File.open( d , 'w') {|f| f.write(distractions) }
Your error code will tell you which one is wrong, but, from looking at them, I'd say it's the second one.
d will be assigned the result of the block for the first File.open, which is going to be the result of f.write(distractions):
The File.open docs say:
The value of the block will be returned from File.open.
The File.write docs say:
Returns the number of bytes written.
As a result, you are assigning d a number of bytes, then trying to create a file with an integer for a filename, which is an error because a filename MUST be a string.
That leads to a bigger problem, which is, your code makes no sense.
d = File.open("Session"+Time.now.to_s , 'w'){|f| f.write(distractions) } writes a 0 to the file created by "Session"+Time.now.to_s.
request.downcase converts the contents of request to lowercase and immediately throws it away. Perhaps you meant request.downcase!, but it'd be better to write:
request = gets.chomp
request.downcase
As:
request = gets.chomp.downcase
distractions = distractions.to_i + 1? distractions is already 0 which is a Fixnum. You're converting a Fixnum to an integer using to_i then adding 1 to it. Simply do:
distractions += 1
distractions = distractions.to_i + answer.to_i should be:
distractions += answer.to_i
File.open( d , 'w') {|f| f.write(distractions) }, because it's trying to write to a file with the integer name, won't update your original file. If it succeeded, it'd write to an entirely new file, which would end up overwriting the previously created file, which was the result of writing a single 0 to disk. Instead, d should be the name of the file previously created.
Consider this:
def new_session
distractions = 0
puts "What would you like to do (add track(s) or review tracks)?"
request = gets.chomp.downcase
case request
when "add track"
distractions += 1
puts "You have #{distractions} tracks in this session."
when "add tracks"
puts "How many times have you been distracted since we last met?"
distractions += gets.chomp.to_i
puts "You have #{distractions} tracks."
when "review tracks"
puts distractions
end
File.write( "Session" + Time.now.to_s, distractions)
end
This code is cleaner, and makes more sense now.

Is there a way to 'cd ..' up a nested "if" statement tree?

I'm curious if there's a way to have the program go back up the if statement stack?
Ideally, the program would return to line 2 and prompt the user for the input variable, then continue to evaluate like it did the first time. Think of it like a cursor in a text editor, I just want to move it from either of those two comments back up to line 2. The two places of interest are commented out below:
while true
input = gets.chomp
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
elsif input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "GOOD BYE!";
break
else
# return to top-level if statement
end
else
# return to top-level if statement
end
else
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
end
end
In the code you show, you don't need to do anything to make the flow of execution go back to line 2. Just omit the else clauses in the two places you marked. The flow of execution will drop down to the bottom of the while loop, then loop back to the top, then go back to line 2.
You need to use a while statement to set a condition flag and check it, which will loop back to the while statement if you don't change the flag:
flag = 0
while flag1 == 0
if var = "string"
then ...statements...
flag1 = 1 ; this allows us to break out of this while loop
else ...statements...
end
end
If flag1 is not 0 at the end of the while statement, the while statement will loop back. For two such conditions, you need to nest the while loops. You might have to re-order your statements to make multiple while loops work this way.
You can avoid this level of neasted ifs with:
byecount = 0
while byecount < 3
input = gets.chomp
if input == "BYE"
byecount += 1
next
else
byecount = 0
end
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
else
puts "NO, NOT SINCE #{rand(1930..1950)}!"
end
end
puts "GOOD BYE!"
Or you can write a catch..throw flow structure. (Really.. if you need to use it, something is wrong with your design)
catch :exitloop do
while ...
if ...
if ...
if ...
throw :exitloop
end
end
end
end
end
Here's how I'd write a similar exercise:
BYE = 'BYE'
HUH = "HUH?! SPEAK UP, SONNY!"
loop do
input = gets.chomp
if input != input.upcase
puts HUH
next
end
if input != BYE
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
next
end
puts HUH
input = gets.chomp
if input == BYE
puts HUH
input = gets.chomp
if input == BYE
puts "GOOD BYE!";
break
end
end
end
I used loop instead of while. Matz, the main man for Ruby, recommends loop. See "Is there a “do … while” loop in Ruby?" for further discussion about it.

Is there a limit to the number of `and`s in Ruby statement?

The code below works as I want, switching a light on or off with each keypress of l. However, when I try to add other things I want done with the switching, I can't.
look = 0
lighton = 0
while look < 10
system 'stty cbreak'
q = $stdin.sysread 1
case q ### #{q}
when "l" then if lighton==1 then lighton=0 and puts "ight off"
else lighton=1 and puts "ight on" end
end
system 'stty cooked'
look += 1
end #while
If I add another and it isn't seen but I get no error:
look = 0
lighton = 0
while look <10
system 'stty cbreak'
q = $stdin.sysread 1
case q ### #{q}
when "l" then if lighton==1 then lighton=0 and puts "ight off" and puts "light still off"
else lighton=1 and puts "ight on" end
end
system 'stty cooked'
look += 1
end #while
I'd like to add several more statements to both the if and else portions but can't.
There's no limit, you're just misusing the and operator. It's not meant to do "this and that", it does "do this and, if it is truthy, do this too". Here's a quick example:
1 and puts 'falsy'
nil and puts 'truthy'
# prints: falsy
Since puts returns nil, which is falsy, puts 'hello' and puts 'world' will only print "hello".
So, don't use and to create a one-liner. You could use ; instead, but that doesn't help readability. Instead just use multiple lines! It's much clearer what's going on:
case q
when "l"
if lighton == 1
lighton = 0
puts "light off"
puts "light still off"
else
lighton = 1
puts "light on"
end
end
You may wish to read more about and/or in Ruby and how they differ from &&/||.
I know nothing about ruby but as there are multiple thing happening after the then in your if statment, do you need to group them somehow? ie Java you would stick them in {}

Resources