Naming a new file with timestamp? - ruby

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.

Related

why is my input method for the hangman game failing to function properly?

I have this method where it gets an input from the user and it checks it against a while condition. if the user inputted anything that isnt a string or if the user inputted a character that was longer than 1 the method would prompt the user again for a valid input, basically adhering to the hangman rules. Heres the code
class Hangman
def initialize
dictionary = File.open('5desk.txt',"r")
line = dictionary.readlines
#word = line[rand(1..line.length)]
#length = #word.length
random = #word.length - rand(#word.length/2)
random.times do
#word[rand(#word.length)] = "_"
end
end
This method fails to function properly.
def get_input
puts #word
puts "Letter Please?"
#letter = gets.chomp
while !#letter.kind_of? String || #letter.length != 1
puts "Invalid input,try again!"
#letter = gets.chomp
end
end
end
Game = Hangman.new
Game.get_input
class Hangman
Stop right there! Why create a class considering that you would only create a single instance of it? There's no need for one. A few methods and one instance variable are sufficient.
Generate secret words randomly
I assume the file '5desk.txt' contains one secret words per line and you will be selecting one randomly. So begin by gulping the entire file into an array held by an instance variable (as opposed to reading the file line-by-line). I assume '5desk.txt1' contains the three words shown below.
#secret_words = File.readlines('5desk.txt', chomp: true)
#=> ["cat", "violin", "whoops"]
See the doc for the class method IO::readlines1,2. The option chomp: true removes the newline character from the end of each line.
This method closes the file after it has been read. (You used File::open. When doing so you need to close the file when you are finished with it: f = File.open(fname)...f.close.)
You need a method to randomly choose a secret_word.
def fetch_secret_word
#secret_words.sample
end
fetch_secret_word
#=> "violin"
See Array#sample. You could have instead used
#secret_words[rand(#secret_words.size)]
See Kernel#rand. The first and last words in #secret_words are #secret_words[0] and #secret_words[#secret_words.size-1]. Therefore, where you wrote
#word = line[rand(1..line.length)]
it should have been
#word = line[rand(0..line.length-1)]
which is the same as
#word = line[rand(line.length)]
Now let's create a method for playing the game, passing an argument that equals the maximum number of incorrect guesses the player has before losing.
def play_hangman(max_guesses)
First get a secret word:
secret_word = fetch_secret_word
Let us suppose that secret_word #=> "violin"
Initialize objects
Next, initialize the number of incorrect guesses and an image of the secret word:
incorrect_guesses = 0
secret_word_image = "-" * secret_word.size
#=> "------"
So we now have
def play_hangman(max_guesses)
secret_word = fetch_secret_word
incorrect_guesses = 0
secret_word_image = "-" * secret_word.size
Loop over guesses
Now we need to loop over the player's guesses. I suggest you use Kernel#loop, in conjuction with the keyword break for all your looping needs. (For now, forget about while and until, and never use for.) The first thing we will do in the loop is to obtain the guess of a letter from the player, which I'll do by calling a method:
loop do
guess = get_letter(secret_word_image)
...<to be completed>
end
def get_letter(secret_word_image)
loop do
puts secret_word_image
print "Gimme a letter: "
letter = gets.chomp.downcase
break letter if letter.match?(/[a-z]/)
puts "That's not a letter. Try again."
end
end
guess = secret_letter(secret_word_image)
#=> "b"
Here this method returns "b" (the guess) and displays:
------
Gimme a letter: &
That's not a letter. Try again.
------
Gimme a letter: 3
That's not a letter. Try again.
------
Gimme a letter: b
See if letter guessed is in secret word
Now we need to see which if any of the hidden letters equal letter. Again, let's make this a method3.
def hidden_letters(guess, secret_word, secret_word_image)
(0..secret_word.size-1).select do |i|
guess == secret_word[i] && secret_word_image[i] = '-'
end
end
Suppose guess #=> "i". Then:
idx = hidden_letters(guess, secret_word, secret_word_image)
#=> [1,4]
There are two "i"'s, at indices 1 and 4. Had there been no hidden letters "i" the method would have returned an empty array.
Before continuing let's look at our play_hangman is coming along.
def play_hangman(max_guesses)
secret_word = fetch_secret_word
incorrect_guesses = 0
secret_word_image = "-" * secret_word.size
loop do
unless secret_word_image.include?('-')
puts "You've won. The secret word is '#{secret_word}'!"
break
end
guess = get_letter(secret_word_image)
idx = hidden_letters(guess, secret_word, secret_word_image)
...<to be completed>
end
Process a guess
We now have to carry out one course of action if the array idx is empty and another if it is not.
case idx.size
when 0
puts "Sorry, no #{guess}'s"
incorrect_guesses += 1
if incorrect_guesses == max_guesses
puts "Oh, my, you've used up all your guesses, but"
puts "we'd like you take home a bar of soap"
break
else
puts idx.size == 1 ? "There is 1 #{guess}!" :
"There are #{idx} #{guess}'s!"
idx.each { |i| secret_word_image[i] = guess }
if secret_word_image == secret_word
puts "You've won!! The secret word is '#{secret_word}'!"
break
end
end
Complete method
So now let's look at the full method (which calls fetch_secret_word, get_letter and hidden_letters).
def play_hangman(max_guesses)
secret_word = fetch_secret_word
incorrect_guesses = 0
secret_word_image = "-" * secret_word.size
loop do
guess = get_letter(secret_word_image)
idx = hidden_letters(guess, secret_word, secret_word_image)
case idx.size
when 0
puts "Sorry, no #{guess}'s"
incorrect_guesses += 1
if incorrect_guesses == max_guesses
puts "Oh, my, you've used up all your guesses,\n" +
"but we'd like you take home a bar of soap"
return
end
else
puts idx.size == 1 ? "There is 1 #{guess}!" :
"There are #{idx.size} #{guess}'s!"
idx.each { |i| secret_word_image[i] = guess }
if secret_word_image == secret_word
puts "You've won!! The secret word is '#{secret_word}'!"
return
end
end
end
end
Play the game!
Here is a example play of the game.
play_hangman(4)
------
Gimme a letter: #
That's not a letter. Try again.
------
Gimme a letter: e
Sorry, no e's
------
Gimme a letter: o
There is 1 o!
--o---
Gimme a letter: i
There are 2 i's!
-io-i-
Gimme a letter: l
There is 1 l!
-ioli-
Gimme a letter: v
There is 1 v!
violi-
Gimme a letter: r
Sorry, no r's
violi-
Gimme a letter: s
Sorry, no s's
violi-
Gimme a letter: t
Sorry, no t's
Oh, my, you've used up all your guesses,
but we'd like you take home a bar of soap
1 The class File has no (class) method readlines. So how can we write File.readlines? It's because File is a subclass of IO (File.superclass #=> IO) and therefore inherits IO's methods. One commonly sees IO class methods invoked with File as the receiver.
2 Ruby's class methods are referenced mod::meth (e.g., Array::new), where mod is the name of a module (which may be a class) and meth is the method. Instance methods are referenced mod#meth (e.g., Array#join).
3 Some Rubyists prefer to write (0..secret_word.size-1) with three dots: (0...secret_word.size). I virtually never use three dots because I find it tends to create bugs. The one exception is when creating an infinite range that excludes the endpoint (e.g., 1.0...1.5).

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

Trouble summing an array in vanilla Ruby

I'm having trouble summing an array. Here's my existing code:
pageArray = Array.new
puts "How many pages long is the book you're reading?"
pageArray << gets.chomp
puts "Are you reading any other books right now?"
yn = gets.chomp
while yn != "no" do
puts "How many pages long is your next book?"
pageArray << gets.chomp
puts "Are you reading any other books right now?"
yn = gets.chomp
end
pageSum = pageArray.reduce(:+)
puts pageSum
When I go through and enter the values 100 and 50, the final return is "10050," rather than "150." Am I missing something obvious?
The code could be written more clearly, and more Ruby-like. Here's some untested code that is more idiomatic:
page_array = []
puts "How many pages long is the book you're reading?"
page_array << gets.chomp
loop do
puts 'Are you reading any other books right now?'
yn = gets.chomp.downcase
break if yn == 'no'
puts 'How many pages long is your next book?'
page_array << gets.chomp.to_i
end
page_sum = page_array.reduce(:+)
puts page_sum
Notice:
variables in Ruby are in snake_case, notInCamelCase.
loop do will loop forever. Simply break when you've received the break value.
you should fold the case of the value used as a break to catch variations in 'y' and 'Y'.
take the time to format your code so it's easily read, even for quick tests. It's amazing how often code we think is just a test actually gets put into production, so do it right the first time.
You are entering strings and concatenating them.
Use gets.chomp.to_i
You need to change the string you get from gets to an int.
pageArray << gets.chomp.to_i

Variables not matching in if/else statement

I am creating a method that makes you solve random math problems. Code is below:
def subtraction()
puts "Your goal is to solve the math problem."
# Asks if user is ready
ready()
a = rand(0..5)
b = rand(0..5)
c = a - b
puts "what is #{a} - #{b}?"
prompt; next_move = gets.chomp
if next_move == c
puts "Lucky guess!"
water()
elsif next_move != c
puts "The answer was: #{c}"
dead("You suck at life")
else
dead("You didn't type anything")
end
end
I keep trying to run this and I keep getting the elsif option. Even though my variables match when I check with puts statements. I am not moving in the direction I want to. What am I doing wrong?
Change
next_move = gets.chomp
to
next_move = gets.chomp.to_i # gets.to_i will work also.
Kernel#gets will give you string, and you need to convert it to appropriate object as per your need, if your work is not with string object like this example. As per the line c = a - b, I am very much sure, you need to change your string object, that you are getting from stdin to an integer object. So you have to use String#to_i.

Why isn't my loop working?

I can't get this program to respond properly to user input. Various looping techniques either only run the block once or run the block infinitely no matter what the user inputs (I've tried case and while as well). Here's the most recent method I've tried:
work_summary = []
begin
# code that runs a sprint and adds results to the work_summary array
puts "Would you like to add a sprint, Y/N?"
sprint = gets.to_s
end until sprint == "N"
print work_summary, "\n"
Ruby never objects to my syntax with any of the various methods, but it also never works.
You need
sprint = gets.chomp
gets returns string with trailing "\n".
http://web.njit.edu/all_topics/Prog_Lang_Docs/html/ruby/syntax.html#begin
Begin is typically used for exception handling. I think you're looking for a while loop.
Here is an example
work_summary = []
while true
puts "Would you like to add a sprint?"
sprint = gets.chomp
if sprint == "N"
break
elsif sprint == "Y"
puts "What's your time?"
time = gets.chomp
work_summary << time
else
puts "I didn't understand your request. Enter Y or N"
end
end
I found two possibility over here which is suitable for you
First one is
while true
puts "Would you like to add a sprint?"
sprint = gets.chomp
if sprint == "N"
break
elsif sprint == "Y"
puts "What's your time?"
time = gets.chomp
work_summary << time
else
puts "Wrong request. Enter Y or N"
end
end
Here Lopp will run until break Doesn't get executed
Second thing you can Modify 1 Line in your code which is
sprint = gets.chomp
this will extract last special character of your string which is generated by gets and work fine in your case

Resources