gamefic 0.6.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/gamefic +3 -0
  3. data/lib/gamefic/character.rb +42 -6
  4. data/lib/gamefic/director/parser.rb +25 -25
  5. data/lib/gamefic/engine/tty.rb +14 -2
  6. data/lib/gamefic/engine.rb +8 -7
  7. data/lib/gamefic/grammar/gender.rb +1 -1
  8. data/lib/gamefic/grammar/pronouns.rb +53 -8
  9. data/lib/gamefic/grammar/verbs.rb +1 -0
  10. data/lib/gamefic/grammar/word_adapter.rb +31 -18
  11. data/lib/gamefic/html.rb +27 -13
  12. data/lib/gamefic/plot/article_mount.rb +1 -1
  13. data/lib/gamefic/plot/scene_mount.rb +27 -110
  14. data/lib/gamefic/{snapshots.rb → plot/snapshot.rb} +60 -29
  15. data/lib/gamefic/plot/you_mount.rb +1 -1
  16. data/lib/gamefic/plot.rb +56 -29
  17. data/lib/gamefic/query/base.rb +3 -1
  18. data/lib/gamefic/scene/active.rb +11 -18
  19. data/lib/gamefic/scene/base.rb +21 -0
  20. data/lib/gamefic/scene/conclusion.rb +11 -0
  21. data/lib/gamefic/scene/custom.rb +21 -0
  22. data/lib/gamefic/scene/multiple_choice/input.rb +11 -0
  23. data/lib/gamefic/scene/multiple_choice.rb +73 -0
  24. data/lib/gamefic/scene/passive.rb +17 -0
  25. data/lib/gamefic/scene/pause.rb +24 -0
  26. data/lib/gamefic/scene/question.rb +21 -0
  27. data/lib/gamefic/scene/yes_or_no.rb +30 -0
  28. data/lib/gamefic/scene.rb +10 -120
  29. data/lib/gamefic/script/base.rb +7 -2
  30. data/lib/gamefic/script.rb +4 -0
  31. data/lib/gamefic/shell/command/base.rb +38 -0
  32. data/lib/gamefic/shell/command/play.rb +51 -0
  33. data/lib/gamefic/shell/command.rb +4 -0
  34. data/lib/gamefic/shell/registry.rb +13 -0
  35. data/lib/gamefic/shell.rb +14 -71
  36. data/lib/gamefic/source/file.rb +1 -1
  37. data/lib/gamefic/source.rb +5 -0
  38. data/lib/gamefic/tester.rb +0 -1
  39. data/lib/gamefic/version.rb +1 -1
  40. data/lib/gamefic.rb +1 -6
  41. metadata +69 -61
  42. data/lib/gamefic/scene/concluded.rb +0 -22
  43. data/lib/gamefic/scene/multiplechoice.rb +0 -74
  44. data/lib/gamefic/scene/paused.rb +0 -26
  45. data/lib/gamefic/scene/yesorno.rb +0 -43
@@ -4,50 +4,25 @@ end
4
4
  module Gamefic
5
5
 
6
6
  module Plot::SceneMount
7
- # Get a Hash of SceneManager objects.
8
- #
9
- # @return [Hash<Symbol, SceneManager>]
10
- def scene_managers
11
- if @scene_managers.nil?
12
- @scene_managers ||= {}
13
- @scene_managers[:active] = ActiveSceneManager.new
14
- @scene_managers[:concluded] = ConcludedSceneManager.new
15
- end
16
- @scene_managers
17
- end
18
-
19
7
  # Create a multiple-choice scene.
20
8
  # The user will be required to make a valid choice to continue
21
9
  #
22
10
  # @yieldparam [Character]
23
- # @yieldparam [MultipleChoiceSceneData]
24
- def multiple_choice key, *args, &block
25
- scene_managers[key] = MultipleChoiceSceneManager.new do |config|
26
- config.start do |actor, data|
27
- data.options = args
28
- end
29
- config.finish(&block)
30
- end
11
+ # @yieldparam [String]
12
+ def multiple_choice key, options, &block
13
+ scenes[key] = Scene::MultipleChoice.new(
14
+ options: options,
15
+ finish: block
16
+ )
31
17
  end
32
18
 
33
19
  # Create a yes-or-no scene.
34
20
  # The user will be required to answer Yes or No to continue.
35
21
  #
36
22
  # @yieldparam [Character]
37
- # @yieldparam [YesOrNoSceneData]
23
+ # @yieldparam [String] "yes" or "no"
38
24
  def yes_or_no key, prompt = nil, &block
39
- manager = YesOrNoSceneManager.new do |config|
40
- config.prompt = prompt
41
- config.finish do |actor, data|
42
- if data.answer.nil?
43
- actor.tell "Please answer Yes or No."
44
- else
45
- data.next_cue ||= :active
46
- block.call(actor, data)
47
- end
48
- end
49
- end
50
- scene_managers[key] = manager
25
+ scenes[key] = Scene::YesOrNo.new(prompt, &block)
51
26
  end
52
27
 
53
28
  # Create a scene with a prompt.
@@ -55,18 +30,11 @@ module Gamefic
55
30
  # from the user.
56
31
  #
57
32
  # @param key [Symbol] A unique name for the scene.
58
- # @param prompt [String] The prompt message to display to the user.
33
+ # @param prompt [String] The input prompt to display to the user.
59
34
  # @yieldparam [Character]
60
- # @yieldparam [SceneData]
61
- def prompt key, prompt, &block
62
- scene_managers[key] = SceneManager.new do |config|
63
- config.prompt = prompt
64
- config.finish do |actor, data|
65
- data.next_cue ||= :active
66
- block.call actor, data
67
- end
68
- end
69
- scene_managers[key].state = "Prompted"
35
+ # @yieldparam [String]
36
+ def question key, prompt, &block
37
+ scenes[key] = Scene::Question.new prompt, &block
70
38
  end
71
39
 
72
40
  # Create a scene that pauses the game.
@@ -75,15 +43,8 @@ module Gamefic
75
43
  #
76
44
  # @param key [Symbol] A unique name for the scene.
77
45
  # @yieldparam [Character]
78
- # @yieldparam [SceneData]
79
46
  def pause key, &block
80
- manager = PausedSceneManager.new do |config|
81
- config.start do |actor, data|
82
- data.next_cue = :active
83
- block.call actor, data
84
- end
85
- end
86
- scene_managers[key] = manager
47
+ scenes[key] = Scene::Pause.new &block
87
48
  end
88
49
 
89
50
  # Create a conclusion.
@@ -91,32 +52,18 @@ module Gamefic
91
52
  #
92
53
  # @param key [Symbol] A unique name for the scene.
93
54
  # @yieldparam [Character]
94
- # @yieldparam [SceneData]
95
55
  def conclusion key, &block
96
- manager = ConcludedSceneManager.new do |config|
97
- config.start(&block)
98
- end
99
- scene_managers[key] = manager
56
+ scenes[key] = Scene::Conclusion.new &block
100
57
  end
101
58
 
102
59
  # Create a generic scene.
103
- # After the scene is complete, it will automatically start the next cue.
60
+ # After the scene is complete, it will automatically start the next
61
+ # prepared scene, or the :active scene if none is prepared.
104
62
  #
105
63
  # @param [Symbol] A unique name for the scene.
106
64
  # @yieldparam [Character]
107
- # @yieldparam [SceneData]
108
65
  def scene key, &block
109
- scene = SceneManager.new do |manager|
110
- manager.start do |actor, data|
111
- data.next_cue = :active
112
- block.call(actor, data) if !block.nil?
113
- cue actor, data.next_cue
114
- actor.scene.start actor
115
- end
116
- # Since generic scenes always cue a new scene, there's no reason to
117
- # define a finish block.
118
- end
119
- scene_managers[key] = scene
66
+ scenes[key] = Scene::Passive.new &block
120
67
  end
121
68
 
122
69
  # Branch to a new scene based on a list of options.
@@ -132,50 +79,20 @@ module Gamefic
132
79
  # actor.tell "You went to scene two"
133
80
  # end
134
81
  # introduction do |actor|
135
- # cue actor, :select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
82
+ # actor.cue, :select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
136
83
  # end
137
84
  #
138
85
  # @param key [Symbol] A unique name for the scene.
139
- # @param options [Hash] A Hash of options and associated scene keys.
140
- def branch key, options
141
- multiple_choice key, *options.keys do |actor, data|
142
- cue actor, options[data.selection]
143
- end
144
- end
145
-
146
- # Cue a Character's next scene
147
- #
148
- # @param actor [Character] The character being cued
149
- # @param key [Symbol] The name of the scene
150
- def cue actor, key
151
- if !actor.scene.nil? and actor.scene.state == "Concluded"
152
- return
153
- end
154
- if key.nil?
155
- raise "Cueing scene with nil key"
156
- end
157
- manager = scene_managers[key]
158
- if manager.nil?
159
- raise "No '#{key}' scene found"
160
- else
161
- actor.scene = manager.prepare key
162
- end
163
- @scene
164
- end
165
-
166
- # This is functionally identical to #cue, but it also raises an
167
- # exception if the selected scene is not a Concluded state.
168
- #
169
- # @param actor [Character] The character being cued
170
- # @param key [Symbol] The name of the scene
171
- def conclude actor, key
172
- key = :concluded if key.nil?
173
- manager = scene_managers[key]
174
- if manager.state != "Concluded"
175
- raise NotConclusionError("Cued scene '#{key}' is not a conclusion")
176
- end
177
- cue actor, key
86
+ # @param map [Hash] A Hash of options and associated scene keys.
87
+ def branch key, map
88
+ scenes[key] = Scene::MultipleChoice.new(
89
+ options: map.keys,
90
+ finish: proc { |actor, input|
91
+ actor.cue map[input.choice]
92
+ }
93
+ )
178
94
  end
95
+
179
96
  end
180
97
 
181
98
  end
@@ -1,13 +1,8 @@
1
1
  require 'json'
2
2
 
3
3
  module Gamefic
4
- class Snapshots
5
- attr_accessor :history
6
- def initialize entities
7
- @history = []
8
- @entities = entities
9
- end
10
- def save entities
4
+ module Plot::Snapshot
5
+ def save
11
6
  store = []
12
7
  index = 0
13
8
  entities.each { |e|
@@ -19,11 +14,11 @@ module Gamefic
19
14
  end
20
15
  if e.respond_to?(m) == true
21
16
  begin
22
- xxx = e.send(m)
23
- if xxx == false
17
+ val = e.send(m)
18
+ if val == false
24
19
  hash[con] = false
25
- elsif xxx
26
- hash[con] = serialize_obj(xxx)
20
+ elsif val
21
+ hash[con] = serialize_obj(val)
27
22
  else
28
23
  hash[con] = nil
29
24
  end
@@ -32,6 +27,7 @@ module Gamefic
32
27
  end
33
28
  end
34
29
  }
30
+ hash[:class] = e.class.to_s
35
31
  hash[:session] = {}
36
32
  e.session.each_pair { |k, v|
37
33
  hash[:session][k] = serialize_obj(v)
@@ -39,29 +35,68 @@ module Gamefic
39
35
  store.push hash
40
36
  index += 1
41
37
  }
42
- if @history.length > 10
43
- @history.shift
38
+ if @initial_state.nil?
39
+ @initial_state = store
40
+ store = []
41
+ @initial_state.length.times do
42
+ store.push {}
43
+ end
44
+ else
45
+ store = reduce(store)
44
46
  end
45
- json = JSON.generate(store)
46
- #puts json
47
- json
47
+ store
48
48
  end
49
49
  def restore snapshot
50
- data = JSON.parse(snapshot)
50
+ internal_restore snapshot, true
51
+ end
52
+ private
53
+ def internal_restore snapshot, with_restore = true
54
+ if with_restore
55
+ @entities[@initial_state.length..-1].each { |e|
56
+ e.parent = nil
57
+ }
58
+ @entities.slice! @initial_state.length..-1
59
+ internal_restore @initial_state, false
60
+ end
51
61
  index = 0
52
- data.each { |hash|
62
+ snapshot.each { |hash|
63
+ if entities[index].nil?
64
+ if with_restore
65
+ cls = Kernel.const_get(hash[:class])
66
+ entities[index] = make cls
67
+ else
68
+ break
69
+ end
70
+ end
53
71
  hash.each_pair { |k, v|
54
- @entities[index].send("#{k}=", unserialize(v)) if k.to_s != "session"
55
- }
56
- #unser = unserialize(hash["session"])
57
- hash["session"].each_pair { |k, v|
58
- @entities[index].session[k.to_sym] = unserialize(v)
72
+ if k == :scene
73
+ entities[index].cue v.to_sym
74
+ else
75
+ entities[index].send("#{k}=", unserialize(v)) if k != :session and k != :class
76
+ end
59
77
  }
78
+ unless hash[:session].nil?
79
+ hash[:session].each_pair { |k, v|
80
+ entities[index].session[k.to_sym] = unserialize(v)
81
+ }
82
+ end
60
83
  index += 1
61
84
  }
62
85
  end
63
- def blacklist
64
- [:children, :session, :scene, :object_of_pronoun, :test_queue, :test_queue_scene, :test_queue_length, :testing]
86
+ def reduce entities
87
+ reduced = []
88
+ index = 0
89
+ entities.each { |e|
90
+ r = {}
91
+ e.each_pair { |k, v|
92
+ if index >= @initial_state.length or @initial_state[index][k] != v
93
+ r[k] = v
94
+ end
95
+ }
96
+ reduced.push r
97
+ index += 1
98
+ }
99
+ reduced
65
100
  end
66
101
  private
67
102
  def can_serialize? obj
@@ -98,8 +133,6 @@ module Gamefic
98
133
  return "#<EIN_#{@entities.index(obj)}>"
99
134
  elsif obj.kind_of?(Direction)
100
135
  return "#<DIR_#{obj.name}>"
101
- #elsif obj.kind_of?(Symbol)
102
- # return "#<SYM_#{obj.to_s}>"
103
136
  end
104
137
  end
105
138
  return obj
@@ -123,8 +156,6 @@ module Gamefic
123
156
  @entities[i]
124
157
  elsif obj.to_s.match(/^#<DIR_[a-z]+>$/)
125
158
  Direction.find(obj[6..-2])
126
- #elsif obj.to_s.match(/^#<SYM_[a-z]+>$/)
127
- # Direction.find(obj[6..-2].to_sym)
128
159
  else
129
160
  obj
130
161
  end
@@ -2,7 +2,7 @@ require 'gamefic'
2
2
  require 'gamefic/grammar'
3
3
 
4
4
  module Gamefic
5
- module YouMount
5
+ module Plot::YouMount
6
6
  class YouGrammarSet
7
7
  include Grammar::Gender
8
8
  include Grammar::Person
data/lib/gamefic/plot.rb CHANGED
@@ -5,8 +5,6 @@ require 'gamefic/tester'
5
5
  require 'gamefic/source'
6
6
  require 'gamefic/script'
7
7
  require 'gamefic/query'
8
- require 'gamefic/plot/article_mount'
9
- require 'gamefic/plot/you_mount'
10
8
 
11
9
  module Gamefic
12
10
 
@@ -15,35 +13,52 @@ module Gamefic
15
13
  autoload :CommandMount, 'gamefic/plot/command_mount'
16
14
  autoload :EntityMount, 'gamefic/plot/entity_mount'
17
15
  autoload :QueryMount, 'gamefic/plot/query_mount'
18
- #autoload :ArticleMount, 'gamefic/plot/article_mount'
19
- #autoload :YouMount, 'gamefic/plot/you_mount'
16
+ autoload :ArticleMount, 'gamefic/plot/article_mount'
17
+ autoload :YouMount, 'gamefic/plot/you_mount'
18
+ autoload :Snapshot, 'gamefic/plot/snapshot'
19
+
20
20
  attr_reader :commands, :imported_scripts, :rules, :asserts, :source
21
- attr_accessor :default_scene
21
+ # TODO Metadata could use better protection
22
+ attr_accessor :default_scene, :metadata
22
23
  include Stage
23
24
  # TODO This include is only here to make the module's methods visible in the IDE.
24
25
  # Gamefic Studio has a PlotStageMetaMapper that handles it, but it doesn't run if
25
26
  # the plugin isn't activated.
26
- include Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount
27
- mount Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount
28
- expose :script, :introduction, :assert_action, :on_update, :on_player_update, :entities, :on_ready, :on_player_ready, :players
27
+ #include Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount, Snapshot
28
+ mount Gamefic, Tester, SceneMount, CommandMount, EntityMount, QueryMount, ArticleMount, YouMount, Snapshot
29
+ expose :script, :introduction, :assert_action, :before_player_update, :on_update, :on_player_update, :entities, :on_ready, :on_player_ready, :players, :scenes, :metadata
29
30
 
30
31
  # @param [Source::Base]
31
32
  def initialize(source = nil)
32
33
  @source = source || Source::Text.new({})
33
- @commands = Hash.new
34
- @syntaxes = Array.new
35
- @ready_procs = Array.new
36
- @update_procs = Array.new
37
- @player_ready = Array.new
38
- @player_procs = Array.new
39
- @working_scripts = Array.new
40
- @imported_scripts = Array.new
41
- @entities = Array.new
42
- @players = Array.new
43
- @asserts = Hash.new
34
+ @commands = {}
35
+ @syntaxes = []
36
+ @ready_procs = []
37
+ @before_player_update_procs = []
38
+ @update_procs = []
39
+ @player_ready = []
40
+ @player_procs = []
41
+ @working_scripts = []
42
+ @imported_scripts = []
43
+ @entities = []
44
+ @players = []
45
+ @asserts = {}
44
46
  @default_scene = :active
45
47
  post_initialize
46
48
  end
49
+
50
+ def scenes
51
+ if @scenes.nil?
52
+ @scenes = {}
53
+ @scenes[:active] = Scene::Active.new
54
+ @scenes[:concluded] = Scene::Conclusion.new
55
+ end
56
+ @scenes
57
+ end
58
+
59
+ def concluded?(actor)
60
+ scenes[actor.scene].kind_of?(Scene::Conclusion)
61
+ end
47
62
 
48
63
  # Get an Array of all Actions defined in the Plot.
49
64
  #
@@ -158,7 +173,7 @@ module Gamefic
158
173
  # by the plot, which would be :active by default. We could
159
174
  # get it like player.cue nil.
160
175
  if player.scene.nil?
161
- cue player, default_scene
176
+ player.cue :active
162
177
  ready
163
178
  update
164
179
  end
@@ -175,7 +190,6 @@ module Gamefic
175
190
  @player_ready.each { |block|
176
191
  block.call player
177
192
  }
178
- player.scene.start player
179
193
  }
180
194
  end
181
195
 
@@ -184,14 +198,24 @@ module Gamefic
184
198
  def update
185
199
  # Update the plot.
186
200
  @players.each { |player|
187
- process_input player
201
+ # TODO: This really doesn't belong here. We need a before_update in the plot.
202
+ @before_player_update_procs.each { |p|
203
+ p.call player
204
+ }
205
+ this_scene = player.next_scene || player.scene
206
+ player.prepare nil
207
+ if this_scene != player.scene
208
+ player.cue this_scene
209
+ player.queue.shift
210
+ else
211
+ process_input player
212
+ end
188
213
  }
189
214
  @entities.each { |e|
190
215
  e.update
191
216
  }
192
217
  @players.each { |player|
193
218
  update_player player
194
- cue player, player.scene.data.next_cue if !player.scene.data.next_cue.nil?
195
219
  }
196
220
  @update_procs.each { |p|
197
221
  p.call
@@ -242,22 +266,25 @@ module Gamefic
242
266
  @player_procs.push block
243
267
  end
244
268
 
269
+ # Add a block to be executed for each player before the turn's update is
270
+ # performed.
271
+ #
272
+ # @yieldparam [Character]
273
+ def before_player_update &block
274
+ @before_player_update_procs.push block
275
+ end
276
+
245
277
  private
246
278
  def process_input player
247
279
  line = player.queue.shift
248
280
  if !line.nil?
249
- player.scene.finish player, line
250
- #cue player, player.scene.data.next_cue if !player.scene.data.next_cue.nil?
281
+ scenes[player.scene].finish player, line
251
282
  end
252
283
  end
253
284
  def update_player player
254
285
  @player_procs.each { |proc|
255
286
  proc.call player
256
287
  }
257
- # HACK Exception for running tests
258
- if player[:testing] == true
259
- cue player, :test
260
- end
261
288
  end
262
289
  def rem_entity(entity)
263
290
  @entities.delete(entity)
@@ -16,6 +16,8 @@ module Gamefic::Query
16
16
  @arguments = arguments
17
17
  @match_hash = Hash.new
18
18
  end
19
+ # Check whether the query allows ambiguous matches.
20
+ # If allowed, this query's
19
21
  def allow_ambiguous?
20
22
  false
21
23
  end
@@ -110,7 +112,7 @@ module Gamefic::Query
110
112
  arguments.each { |a|
111
113
  if a.kind_of?(Class) or a.kind_of?(Module)
112
114
  my_classes.push a
113
- elsif a.kind_of?(Entity)
115
+ elsif a.kind_of?(Gamefic::Entity)
114
116
  my_objects.push a
115
117
  elsif a.kind_of?(Symbol)
116
118
  if my_classes.length == 0 and my_objects.length == 0
@@ -1,25 +1,18 @@
1
1
  module Gamefic
2
2
 
3
- class ActiveSceneManager < SceneManager
4
- def scene_class
5
- ActiveScene
3
+ # Active Scenes handle the default command prompt, where input is parsed
4
+ # into an Action performed by the Character. This is the default scene in
5
+ # a Plot.
6
+ #
7
+ class Scene::Active < Scene::Base
8
+ def start actor
9
+ # TODO Anything necessary here?
6
10
  end
7
- def state
8
- @state ||= "Active"
9
- end
10
- end
11
-
12
- class ActiveScene < Scene
13
11
  def finish actor, input
14
- @data.input = input
15
- if @finish.nil?
16
- last_order = actor.perform data.input
17
- # HACK Set the last_order here so inline performs don't set it
18
- actor.send(:last_order=, last_order)
19
- else
20
- @finish.call actor, data
21
- end
12
+ last_order = actor.perform input
13
+ # HACK Set the last_order here so inline performs don't set it
14
+ actor.send(:last_order=, last_order)
22
15
  end
23
16
  end
24
-
17
+
25
18
  end
@@ -0,0 +1,21 @@
1
+ module Gamefic::Scene
2
+
3
+ # The Base Scene is not intended for instantiation. Other Scene classes
4
+ # should inherit from it.
5
+ #
6
+ class Base
7
+ def prompt
8
+ @prompt ||= '>'
9
+ end
10
+ def start actor
11
+
12
+ end
13
+ def finish actor, input
14
+
15
+ end
16
+ def state
17
+ self.class.to_s.split('::').last
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,11 @@
1
+ module Gamefic
2
+
3
+ # A Conclusion ends the Plot (or the character's participation in it).
4
+ #
5
+ class Scene::Conclusion < Scene::Custom
6
+ def initialize &block
7
+ @start = block
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,21 @@
1
+ module Gamefic
2
+
3
+ # A Custom Scene is a generic scene that allows for complete configuration
4
+ # of its behavior upon instantiation. It is suitable for direct instantion
5
+ # or extension by other Scene classes.
6
+ #
7
+ class Scene::Custom < Scene::Base
8
+ def initialize config = {}
9
+ @start = config[:start]
10
+ @finish = config[:finish]
11
+ @prompt = config[:prompt]
12
+ end
13
+ def start actor
14
+ @start.call(actor) unless @start.nil?
15
+ end
16
+ def finish actor, input
17
+ @finish.call(actor, input) unless @finish.nil?
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,11 @@
1
+ module Gamefic
2
+ class Gamefic::Scene::MultipleChoice::Input
3
+ attr_reader :raw, :number, :index, :choice
4
+ def initialize raw, index, choice
5
+ @raw = raw
6
+ @index = index
7
+ @number = index + 1
8
+ @choice = choice
9
+ end
10
+ end
11
+ end