Here's my attempt:
At the moment it only prints text to the screen. I have an
interesting idea about generating a wav file directly, but I'm going
to have trouble getting that done before the summary deadline. The
script gets the exercise plan from a text file with a simple format.
It doesn't do any error checking of arguments or file format.
Usage: ruby -d coach.rb weekly_plan.txt
Use the -d switch unless you want to wait 20 minutes for all the output.
-----week3.txt-----
run 90
walk 90
run 180
walk 180
run 90
walk 90
run 180
walk 180
----------
-----coach.rb-----
$CheerThreshold = 6 #decrease to get more random encouragement
$LongThreshold = 120 #minimum time to be considered a "long" run
class Phase
attr_reader :action, :seconds
def initialize action, time
@action = action.downcase
@seconds = time.to_i
end
end
class Coach
def initialize filename
File.open(filename) {|f|
@rawdata = f.read.split("\n")
}
@duration = 0
@runs = @longs = @walks = 0
@encouragometer = 0
@step = [30,15,10,5,5]
end
def coach
build_timeline
say summarize(2)
say start_prompt
@time = Time.now
@target_time = @time
while (phase = @phases.shift)
update_summary phase
narrate_phase phase
if @phases.size > 0
say transition(@phases[0].action)
say summarize(rand(2))
end
end
say finish_line
end
def narrate_phase phase
say what_to_do_for(phase)
@target_time += phase.seconds
delta = (@target_time - Time.now).to_i
stepidx = 0
while (delta > 0)
stepidx+=1 if delta < @step[stepidx]+1
wait_time = delta%@step[stepidx]
wait_time += @step[stepidx] if wait_time <= 0
wait(wait_time)
delta = (@target_time - Time.now).to_i
encourage_maybe
say whats_left(phase.action,delta) if delta > 0
end
end
def update_summary phase
@duration -= phase.seconds
@runs -= 1 if phase.action == 'run'
@longs -= 1 if phase.action == 'run' and phase.seconds >= $LongThreshold
@walks -= 1 if phase.action == 'walk'
end
def build_timeline
@phases = @rawdata.map {|command|
p = Phase.new(*command.split)
@duration += p.seconds
@runs += 1 if p.action == 'run'
@longs += 1 if p.action == 'run' and p.seconds >= $LongThreshold
@walks += 1 if p.action == 'walk'
p
}
end
def say s
puts s
#todo: replace with speech
end
def wait n
if $DEBUG
puts "...waiting #{n} seconds..."
@target_time -= n
else
$stdout.flush
sleep(n)
end
end
def encourage_maybe
@encouragometer += rand(3)
if (@encouragometer > $CheerThreshold)
say cheer
@encouragometer = 0
end
end
def timesay secs
secs = secs.to_i
s = ""
if secs > 60
min = secs/60
secs -= min*60
s += "#{min} minute"
s += 's' if min > 1
s += ' and ' if secs > 0
end
if secs > 0
s += "#{secs} second"
s += 's' if secs > 1
end
s
end
# All the phrases should be below this line, not mixed up in the logic
def what_to_do_for phase
s = "#{phase.action} for #{timesay(phase.seconds)} \n"
s += "You are almost done" if @phases.size == 1
s
end
def whats_left act, time
timestr = timesay(time)
s = [
"You have #{timestr} more to #{act}",
"#{act} for #{timestr} more",
"only #{timestr} left of #{act}ing",
"You have #{timestr} more to #{act}",
"#{timestr} left in this phase",
"There are #{timestr} until the next activity"
]
s[rand(s.size)]
end
def start_prompt
"are you ready, go!"
end
def transition next_act
s = ["OK, you can #{next_act} now",
"get ready to #{next_act}"]
s[rand(s.size)]
end
def finish_line
"you are done, rest now."
end
def cheer
c = ["Keep it up!", "Way to go!", "Good Job!"]
c[rand(c.size)]
end
def summarize degree
shorts = @runs - @longs
s = "you have #{timesay(@duration)}"
if degree > 0
if degree > 1
s+= " for "
else
s+= " to go and there are " if @runs > 0
end
s+="#{@longs} long run" if @longs > 0
s+="s" if @longs > 1
s+=" and" if @longs > 0 and shorts > 0 and degree <=1
s+=" #{shorts} short run" if shorts > 0
s+="s" if shorts > 1
if degree >1
s+=" and" if @longs+shorts > 0
s+=" #{@walks} walk" if @walks > 0
s+="s" if @walks > 1
else
s+=" left"
end
else
s+= " left to exercise"
end
s
end
end
Coach.new(ARGV[0]||"week3.txt").coach
----------
-Adam