From: Adam Shelly Date: 2006-06-14T10:41:54+09:00 Subject: Re: [QUIZ part solution] Running Coach (#82) 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