Reputation: 69
I fundamentally don't understand what's going on here. Below play is my state machine. But it doesn't work. It either returns the opposite of the two choices, the same choice over and over (beginning), or 'unknown command'. I've tested it by printing the value of the variable @next_action at various points and the results are inconsistent. Sometimes the result of the case statement is instruct but it prints display. Sometimes vice versa, sometimes unknown command. Yes, I fiddle with the code to produce these different results. But not much. And never has it behaved as expected.
Obviously, I don't understand the logic of what I've written. All I want to do is pass the result of the case statement as a method call and keep everything looping. I'm a ruby newb and the handful of folks who've tried to help have either described things in a way that I don't seem to be understanding, or I've done a poor job of explaining/showing exactly what I'm trying to do.
Any help is greatly appreciated.
class Foo
def initialize(start_action)
@start = start_action
@users = %w(Adam Sarah Kanye)
@points = %w(100 200 300)
end
def play
puts @next_action
while true
case @next_action
when beginning
beginning
when "instruct"
instructions
when "display"
display_users
else
puts "Unknown command."
play
end
puts "\n----------"
end
end
def prompt
puts "\n----------"
print "> "
end
def beginning
puts <<-INTROTEXT
This is intro text.
INTROTEXT
prompt; @next_action = gets.chomp.to_s
end
def instructions
puts <<-INSTRUCT
These are instructions.
INSTRUCT
prompt; @next_action = gets.chomp.to_s
end
def display_users
puts "\nYour users now include:"
puts "\nName\tPoints"
puts "----\t------"
@users.each_with_index do |item, index|
puts "%s\t%s" % [item, @points[index]]
end
prompt; @next_action = gets.chomp
end
end
start = Foo.new(:beginning)
start.play
Upvotes: 1
Views: 135
Reputation: 334
The beginning in the case statement should be :beginning i.e a symbol otherwise you are assessing next action against a method. Also as you recurse on a case statement that never results in beginning it probably means you end up in infinite recursion. (Apologies if the beginning is just a typo)
You are also mixing symbols and strings (generally should be avoided). In instructions you create @next_action as a string but a symbol via Foo.new. :beginning != "beginning" also. I would suggest next action always be a symbol as symbols (as far as I am aware) are used to indicate an action should be taken based on the symbol when passed as an argument to a function. Although as it's based on user input it does make sense to leave it as a string as well.
Also shouldn't the function call play be outside of the case statement and then remove the while true. This would mean that after @next_action has been created the program continues. Basically I don't think this will ever stop, while true means continue forever, you have to supply a break
to stop it. I would imagine you would want it to continue unless there was an unknown action. Or at least reset the action on an unknown action i.e:
class Foo
def initialize(start_action)
@start = start_action
@users = %w(Adam Sarah Kanye)
@points = %w(100 200 300)
end
This one would stop if someone entered the wrong instructions but continue otherwise
def play
puts @next_action
case @next_action
when :beginning
beginning
when :instruct
instructions
when :display
display_users
else
puts "Unknown command."
raise Foo::UnknownCommand
end
puts "\n----------"
play
end
OR This would continue forever and start the user at the start if they entered the wrong command.
def play
puts @next_action
while true
case @next_action
when :beginning
beginning
when :instruct
instructions
when :display
display_users
else
puts "Unknown command."
@next_action = :beginning
end
puts "\n----------"
end
end
and the rest
def prompt
puts "\n----------"
print "> "
end
def beginning
puts <<-INTROTEXT
This is intro text.
INTROTEXT
prompt; @next_action = gets.chomp.to_sym
end
def instructions
puts <<-INSTRUCT
These are instructions.
INSTRUCT
prompt; @next_action = gets.chomp.to_sym
end
def display_users
puts "\nYour users now include:"
puts "\nName\tPoints"
puts "----\t------"
@users.each_with_index do |item, index|
puts "%s\t%s" % [item, @points[index]]
end
prompt; @next_action = gets.chomp.to_sym
end
end
start = Foo.new(:beginning)
start.play
I hope that helps.
Upvotes: 0
Reputation: 1051
You'll be fine, just DRY things up a bit. Also, you have two separate looping constructs in place, and that's leading to undesired behavior. Your :play
method contains an infinite loop that itself calls :play
as its last step. You really only need one or the other. I've tweaked things a bit to centralize the :prompt
functionality and use a loop without recursion (i.e. :play
no longer calls itself) to achieve what I think is your expected behavior:
class Foo
def initialize
@next_action = "beginning"
@users = %w(Adam Sarah Kanye)
@points = [100, 200, 300]
end
def play
while true
act
prompt
end
end
def prompt
puts "\n----------"
print "> "
@next_action = gets.chomp.to_s
end
def act
case @next_action
when "beginning"
beginning
when "instruct"
instructions
when "display"
display_users
else
puts "I don't know how to '#{@next_action}'."
end
end
def beginning
puts <<-INTROTEXT
This is intro text.
INTROTEXT
end
def instructions
puts <<-INSTRUCT
These are instructions.
INSTRUCT
end
def display_users
puts "\nYour users now include:"
puts "\nName\tPoints"
puts "----\t------"
@users.each_with_index do |item, index|
puts "%s\t%s" % [item, @points[index].to_s]
end
end
end
Foo.new.play
Upvotes: 1