Decisions, Decisions

it'presents selections to player'doget'/',From:'Dude8',SkipToState:'D1S108'expect(last_response.body).toinclude("what is the solution?\n1) Six\n2) Sixteen\n3) I wasn't paying attention")endit'respects selections from player'doget'/',From:'Dude8',SkipToState:'D1S108'get'/',From:'Dude8',Body:'1'expect(last_response.body).toinclude('Wrong. You haven\'t been paying attention, have you?')endit'rejects non-numeric selections from player'doget'/',From:'Dude9',SkipToState:'D1S108'get'/',From:'Dude9',Body:'Dan'expect(last_response.body).toinclude("what is the solution?\n1) Six\n2) Sixteen\n3) I wasn't paying attention")endit'rejects invalid selections from player'doget'/',From:'Dude',SkipToState:'D1S108'get'/',From:'Dude',Body:'4'expect(last_response.body).toinclude("what is the solution?\n1) Six\n2) Sixteen\n3) I wasn't paying attention")end

It may change in the future, but numeric choices seemed the best initial interface. Implementing
the selections changed the engine code significantly.

## State transitionifparams.key?('SkipToState')state=params['SkipToState']elsifGameStates.states[state].key?('selection')# handle using the input to choose selectionif(index=body.to_i)!=0selections=GameStates.states[state]['selection'].to_aifselections.length>=indexstate=selections[index-1].lastelseputs"selections.length #{selections.length} index #{index}"puts"Player selected #{body} but selections are #{selections}"endelseputs"Player gave non-numeric selection: #{body}"endelsifGameStates.states[state].key?('next')state=GameStates.states[state]['next']elseputs"Not sure where to go from state: #{GameStates.states[state]}"end

This was initially a bit of a struggle, as can be seen with the debugging that I left in. A big
result was the GameStates module replacing the awkward global I had before:

# The set of game states. After being loaded from a JSON file, should never change.moduleGameStates@states={}defself.load@states=JSON.parse(File.open('script.json','r').read)enddefself.states@statesenddefself.story(state)if@states[state]&&@states[state].key?('story')String.new(@states[state]['story'])else''endenddefself.selections(state)response=''if@states[state]&&@states[state].key?('selection')@states[state]['selection'].keys.each_with_indexdo|selection,index|response+="\n#{index+1}) #{selection}"endendresponseendend

I’ve been migrating the appropriate logic into it, so that (for example) creating the output with
story text and selections is as simple as:

Go For Broke

it'can be played all the way through'doget'/',From:'Dude'Timeout::timeout(10)dountillast_response.body.include?('And you won\'t be alone...')||last_response.body.include?('Royalty does not concern itself with common dances.')||last_response.body.include?('True power is slow dancing with someone who could beat you senseless.')||last_response.body.include?('Thirst for knowledge. Hunger for power. No feast is fine enough.')doget'/',From:'Dude',Body:['1','2','3'].sampleputslast_response.bodyendendend

(The .sample is to avoid spinning forever in the narrative loops that exist. Not elegant, but
simple.)