gamefic 1.7.0 → 2.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 (99) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.solargraph.yml +5 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +20 -0
  8. data/README.md +28 -0
  9. data/Rakefile +10 -0
  10. data/gamefic.gemspec +27 -0
  11. data/lib/gamefic.rb +7 -6
  12. data/lib/gamefic/action.rb +38 -28
  13. data/lib/gamefic/active.rb +325 -280
  14. data/lib/gamefic/actor.rb +8 -5
  15. data/lib/gamefic/command.rb +9 -7
  16. data/lib/gamefic/core_ext/array.rb +24 -49
  17. data/lib/gamefic/core_ext/string.rb +25 -16
  18. data/lib/gamefic/describable.rb +21 -23
  19. data/lib/gamefic/element.rb +43 -31
  20. data/lib/gamefic/entity.rb +6 -12
  21. data/lib/gamefic/index.rb +121 -0
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -44
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +69 -89
  26. data/lib/gamefic/plot/darkroom.rb +92 -264
  27. data/lib/gamefic/plot/host.rb +42 -48
  28. data/lib/gamefic/plot/snapshot.rb +5 -18
  29. data/lib/gamefic/query.rb +14 -18
  30. data/lib/gamefic/query/base.rb +30 -18
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/external.rb +18 -14
  33. data/lib/gamefic/query/family.rb +1 -7
  34. data/lib/gamefic/query/matches.rb +75 -67
  35. data/lib/gamefic/query/parent.rb +0 -0
  36. data/lib/gamefic/query/siblings.rb +0 -0
  37. data/lib/gamefic/query/text.rb +2 -1
  38. data/lib/gamefic/scene.rb +0 -2
  39. data/lib/gamefic/scene/activity.rb +24 -26
  40. data/lib/gamefic/scene/base.rb +64 -8
  41. data/lib/gamefic/scene/conclusion.rb +0 -2
  42. data/lib/gamefic/scene/custom.rb +0 -2
  43. data/lib/gamefic/scene/multiple_choice.rb +18 -3
  44. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  45. data/lib/gamefic/scene/pause.rb +7 -2
  46. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  47. data/lib/gamefic/scriptable.rb +87 -0
  48. data/lib/gamefic/serialize.rb +68 -0
  49. data/lib/gamefic/subplot.rb +29 -35
  50. data/lib/gamefic/syntax.rb +14 -13
  51. data/lib/gamefic/version.rb +3 -3
  52. data/lib/gamefic/world.rb +16 -0
  53. data/lib/gamefic/world/callbacks.rb +135 -0
  54. data/lib/gamefic/world/commands.rb +184 -0
  55. data/lib/gamefic/{plot → world}/entities.rb +30 -29
  56. data/lib/gamefic/{plot → world}/playbook.rb +255 -240
  57. data/lib/gamefic/world/players.rb +21 -0
  58. data/lib/gamefic/world/scenes.rb +226 -0
  59. metadata +41 -92
  60. data/bin/gamefic +0 -9
  61. data/lib/gamefic/engine.rb +0 -7
  62. data/lib/gamefic/engine/base.rb +0 -59
  63. data/lib/gamefic/engine/tty.rb +0 -24
  64. data/lib/gamefic/grammar.rb +0 -13
  65. data/lib/gamefic/grammar/conjugator.rb +0 -20
  66. data/lib/gamefic/grammar/gender.rb +0 -11
  67. data/lib/gamefic/grammar/person.rb +0 -10
  68. data/lib/gamefic/grammar/plural.rb +0 -13
  69. data/lib/gamefic/grammar/pronouns.rb +0 -106
  70. data/lib/gamefic/grammar/tense.rb +0 -6
  71. data/lib/gamefic/grammar/verb_set.rb +0 -43
  72. data/lib/gamefic/grammar/verbs.rb +0 -26
  73. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  74. data/lib/gamefic/plot/articles.rb +0 -22
  75. data/lib/gamefic/plot/callbacks.rb +0 -126
  76. data/lib/gamefic/plot/commands.rb +0 -120
  77. data/lib/gamefic/plot/players.rb +0 -15
  78. data/lib/gamefic/plot/scenes.rb +0 -187
  79. data/lib/gamefic/plot/theater.rb +0 -73
  80. data/lib/gamefic/plot/you_mount.rb +0 -22
  81. data/lib/gamefic/script.rb +0 -13
  82. data/lib/gamefic/script/base.rb +0 -42
  83. data/lib/gamefic/script/file.rb +0 -14
  84. data/lib/gamefic/script/text.rb +0 -14
  85. data/lib/gamefic/shell.rb +0 -76
  86. data/lib/gamefic/source.rb +0 -14
  87. data/lib/gamefic/source/base.rb +0 -12
  88. data/lib/gamefic/source/file.rb +0 -23
  89. data/lib/gamefic/source/text.rb +0 -16
  90. data/lib/gamefic/tester.rb +0 -19
  91. data/lib/gamefic/text.rb +0 -8
  92. data/lib/gamefic/text/ansi.rb +0 -53
  93. data/lib/gamefic/text/html.rb +0 -68
  94. data/lib/gamefic/text/html/conversions.rb +0 -250
  95. data/lib/gamefic/text/html/entities.rb +0 -9
  96. data/lib/gamefic/tty.rb +0 -10
  97. data/lib/gamefic/user.rb +0 -7
  98. data/lib/gamefic/user/base.rb +0 -29
  99. data/lib/gamefic/user/tty.rb +0 -38
@@ -1,240 +1,255 @@
1
- module Gamefic
2
-
3
- class Plot
4
- class Playbook
5
- def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
6
- @commands = commands
7
- @syntaxes = syntaxes
8
- @validators = validators
9
- @disambiguator = disambiguator
10
- end
11
-
12
- def syntaxes
13
- @syntaxes
14
- end
15
-
16
- def actions
17
- @commands.values.flatten
18
- end
19
-
20
- def verbs
21
- @commands.keys
22
- end
23
-
24
- def validators
25
- @validators
26
- end
27
-
28
- def disambiguator
29
- @disambiguator ||= Action.new(nil, Query::Base.new) do |actor, entities|
30
- definites = []
31
- entities.each { |entity|
32
- definites.push entity.definitely
33
- }
34
- actor.tell "I don't know which you mean: #{definites.join_or}."
35
- end
36
- end
37
-
38
- #def disambiguate &block
39
- # @disambiguator = Action.new(nil, Query::Base.new, &block)
40
- # @disambiguator.meta = true
41
- # @disambiguator
42
- #end
43
-
44
- def validate &block
45
- @validators.push block
46
- end
47
-
48
- # Get an Array of all Actions associated with the specified verb.
49
- #
50
- # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
51
- # @return [Array<Action>] The verb's associated Actions
52
- def actions_for verb
53
- @commands[verb] || []
54
- end
55
-
56
- # Create an Action that responds to a command.
57
- # An Action uses the command argument to identify the imperative verb that
58
- # triggers the action.
59
- # It can also accept queries to tokenize the remainder of the input and
60
- # filter for particular entities or properties.
61
- # The block argument contains the code to be executed when the input
62
- # matches all of the Action's criteria (i.e., verb and queries).
63
- #
64
- # @example A simple Action.
65
- # respond :salute do |actor|
66
- # actor.tell "Hello, sir!"
67
- # end
68
- # # The command "salute" will respond "Hello, sir!"
69
- #
70
- # @example An Action that accepts a Character
71
- # respond :salute, Use.visible(Character) do |actor, character|
72
- # actor.tell "#{The character} returns your salute."
73
- # end
74
- #
75
- # @param verb [Symbol] An imperative verb for the command
76
- # @param queries [Array<Query::Base>] Filters for the command's tokens
77
- # @yieldparam [Gamefic::Character]
78
- # @return [Gamefic::Action]
79
- def respond(verb, *queries, &proc)
80
- act = Action.subclass verb, *queries, order_key: raise_order_key, &proc
81
- add_action act
82
- act
83
- end
84
-
85
- # Create a Meta Action that responds to a command.
86
- # Meta Actions are very similar to standard Actions, except the Plot
87
- # understands them to be commands that operate above and/or outside of the
88
- # actual game world. Examples of Meta Actions are commands that report the
89
- # player's current score, save and restore saved games, or list the game's
90
- # credits.
91
- #
92
- # @example A simple Meta Action
93
- # meta :credits do |actor|
94
- # actor.tell "This game was written by John Smith."
95
- # end
96
- #
97
- # @param verb [Symbol] An imperative verb for the command
98
- # @param queries [Array<Query::Base>] Filters for the command's tokens
99
- # @yieldparam [Gamefic::Character]
100
- def meta(verb, *queries, &proc)
101
- act = Action.subclass verb, *queries, meta: true, &proc
102
- add_action act
103
- act
104
- end
105
-
106
- # Create an alternate Syntax for an Action.
107
- # The command and its translation can be parameterized.
108
- #
109
- # @example Create a synonym for the Inventory Action.
110
- # interpret "catalogue", "inventory"
111
- # # The command "catalogue" will be translated to "inventory"
112
- #
113
- # @example Create a parameterized synonym for the Look Action.
114
- # interpret "scrutinize :entity", "look :entity"
115
- # # The command "scrutinize chair" will be translated to "look chair"
116
- #
117
- # @param input [String] The format of the original command
118
- # @param translation [String] The format of the translated command
119
- # @return [Syntax] the Syntax object
120
- def interpret(input, translation)
121
- syn = Syntax.new(input, translation)
122
- add_syntax syn
123
- syn
124
- end
125
-
126
- def dispatch(actor, *command)
127
- result = []
128
- if command.length > 1
129
- result.concat dispatch_from_params(actor, command[0], command[1..-1])
130
- end
131
- if result.empty?
132
- result.concat dispatch_from_string(actor, command.join(' '))
133
- end
134
- result
135
- end
136
-
137
- def dispatch_from_string actor, text
138
- result = []
139
- commands = Syntax.tokenize(text, syntaxes)
140
- commands.each { |c|
141
- available = actions_for(c.verb)
142
- available.each { |a|
143
- next if a.hidden?
144
- o = a.attempt(actor, c.arguments)
145
- result.unshift o unless o.nil?
146
- }
147
- }
148
- sort_and_reduce_actions result
149
- end
150
-
151
- def dispatch_from_params actor, verb, params
152
- result = []
153
- available = actions_for(verb)
154
- available.each { |a|
155
- result.unshift a.new(actor, params) if a.valid?(actor, params)
156
- }
157
- sort_and_reduce_actions result
158
- end
159
-
160
- # Duplicate the playbook.
161
- # This method will duplicate the commands hash and the syntax array so
162
- # the new playbook can be modified without affecting the original.
163
- #
164
- # @return [Playbook]
165
- def dup
166
- Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
167
- end
168
-
169
- def freeze
170
- @commands.freeze
171
- @syntaxes.freeze
172
- end
173
-
174
- private
175
-
176
- def add_action(action)
177
- @commands[action.verb] ||= []
178
- @commands[action.verb].push action
179
- #@commands[action.verb].uniq!
180
- #@commands[action.verb].sort! { |a, b|
181
- # b.rank <=> a.rank
182
- #}
183
- generate_default_syntax action
184
- end
185
-
186
- def generate_default_syntax action
187
- user_friendly = action.verb.to_s.gsub(/_/, ' ')
188
- args = []
189
- used_names = []
190
- action.queries.each { |c|
191
- num = 1
192
- new_name = ":var"
193
- while used_names.include? new_name
194
- num = num + 1
195
- new_name = ":var#{num}"
196
- end
197
- used_names.push new_name
198
- user_friendly += " #{new_name}"
199
- args.push new_name
200
- }
201
- add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}")
202
- end
203
-
204
- def add_syntax syntax
205
- if @commands[syntax.verb] == nil
206
- raise "No actions exist for \"#{syntax.verb}\""
207
- end
208
- @syntaxes.unshift syntax
209
- @syntaxes.uniq!
210
- @syntaxes.sort! { |a, b|
211
- if a.token_count == b.token_count
212
- # For syntaxes of the same length, length of action takes precedence
213
- b.first_word <=> a.first_word
214
- else
215
- b.token_count <=> a.token_count
216
- end
217
- }
218
- end
219
-
220
- def sort_and_reduce_actions arr
221
- arr.sort { |a,b|
222
- if a.rank == b.rank
223
- b.order_key <=> a.order_key
224
- else
225
- b.rank <=> a.rank
226
- end
227
- }.uniq{|a| a.class}
228
- end
229
-
230
- def raise_order_key
231
- @@order_key ||= 0
232
- tmp = @@order_key
233
- @@order_key += 1
234
- tmp
235
- end
236
-
237
- end
238
- end
239
-
240
- end
1
+ module Gamefic
2
+ module World
3
+ # A collection of rules for performing commands.
4
+ #
5
+ class Playbook
6
+ def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
7
+ @commands = commands
8
+ @syntaxes = syntaxes
9
+ @validators = validators
10
+ @disambiguator = disambiguator
11
+ end
12
+
13
+ # An array of available syntaxes.
14
+ #
15
+ # @return [Array<Gamefic::Syntax>]
16
+ def syntaxes
17
+ @syntaxes
18
+ end
19
+
20
+ # An array of available actions.
21
+ #
22
+ # @return [Array<Gamefic::Action>]
23
+ def actions
24
+ @commands.values.flatten
25
+ end
26
+
27
+ # An array of recognized verbs.
28
+ #
29
+ # @return [Array<Symbol>]
30
+ def verbs
31
+ @commands.keys
32
+ end
33
+
34
+ # An array of defined validators.
35
+ #
36
+ # @return [Array<Proc>]
37
+ def validators
38
+ @validators
39
+ end
40
+
41
+ # Get the action for handling ambiguous entity references.
42
+ #
43
+ def disambiguator
44
+ @disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities|
45
+ definites = []
46
+ entities.each { |entity|
47
+ definites.push entity.definitely
48
+ }
49
+ actor.tell "I don't know which you mean: #{definites.join_or}."
50
+ end
51
+ end
52
+
53
+ # Set the action for handling ambiguous entity references.
54
+ #
55
+ def disambiguate &block
56
+ @disambiguator = Action.subclass(nil, Query::Base.new, meta: true, &block)
57
+ @disambiguator
58
+ end
59
+
60
+ # Add a block that determines whether an action can be executed.
61
+ #
62
+ def validate &block
63
+ @validators.push block
64
+ end
65
+
66
+ # Get an Array of all Actions associated with the specified verb.
67
+ #
68
+ # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
69
+ # @return [Array<Action>] The verb's associated Actions
70
+ def actions_for verb
71
+ @commands[verb] || []
72
+ end
73
+
74
+ # Create an Action that responds to a command.
75
+ # An Action uses the command argument to identify the imperative verb that
76
+ # triggers the action.
77
+ # It can also accept queries to tokenize the remainder of the input and
78
+ # filter for particular entities or properties.
79
+ # The block argument contains the code to be executed when the input
80
+ # matches all of the Action's criteria (i.e., verb and queries).
81
+ #
82
+ # @example A simple Action.
83
+ # respond :salute do |actor|
84
+ # actor.tell "Hello, sir!"
85
+ # end
86
+ # # The command "salute" will respond "Hello, sir!"
87
+ #
88
+ # @example An Action that accepts a Character
89
+ # respond :salute, Use.visible(Character) do |actor, character|
90
+ # actor.tell "#{The character} returns your salute."
91
+ # end
92
+ #
93
+ # @param verb [Symbol] An imperative verb for the command
94
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
95
+ # @yieldparam [Gamefic::Actor]
96
+ # @return [Class<Gamefic::Action>]
97
+ def respond(verb, *queries, &proc)
98
+ act = Action.subclass verb, *queries, &proc
99
+ add_action act
100
+ act
101
+ end
102
+
103
+ # Create a Meta Action that responds to a command.
104
+ # Meta Actions are very similar to standard Actions, except the Plot
105
+ # understands them to be commands that operate above and/or outside of the
106
+ # actual game world. Examples of Meta Actions are commands that report the
107
+ # player's current score, save and restore saved games, or list the game's
108
+ # credits.
109
+ #
110
+ # @example A simple Meta Action
111
+ # meta :credits do |actor|
112
+ # actor.tell "This game was written by John Smith."
113
+ # end
114
+ #
115
+ # @param verb [Symbol] An imperative verb for the command
116
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
117
+ # @yieldparam [Gamefic::Actor]
118
+ # @return [Class<Gamefic::Action>]
119
+ def meta(verb, *queries, &proc)
120
+ act = Action.subclass verb, *queries, meta: true, &proc
121
+ add_action act
122
+ act
123
+ end
124
+
125
+ # Create an alternate Syntax for an Action.
126
+ # The command and its translation can be parameterized.
127
+ #
128
+ # @example Create a synonym for the Inventory Action.
129
+ # interpret "catalogue", "inventory"
130
+ # # The command "catalogue" will be translated to "inventory"
131
+ #
132
+ # @example Create a parameterized synonym for the Look Action.
133
+ # interpret "scrutinize :entity", "look :entity"
134
+ # # The command "scrutinize chair" will be translated to "look chair"
135
+ #
136
+ # @param input [String] The format of the original command
137
+ # @param translation [String] The format of the translated command
138
+ # @return [Syntax] the Syntax object
139
+ def interpret(input, translation)
140
+ syn = Syntax.new(input, translation)
141
+ add_syntax syn
142
+ syn
143
+ end
144
+
145
+ # Get an array of actions, derived from the specified command, that the
146
+ # actor can potentially execute.
147
+ # The command can either be a single string (e.g., "examine book") or a
148
+ # list of tokens (e.g., :examine, @book).
149
+ #
150
+ # @return [Array<Gamefic::Action>]
151
+ def dispatch(actor, *command)
152
+ result = []
153
+ if command.length > 1
154
+ result.concat dispatch_from_params(actor, command[0], command[1..-1])
155
+ end
156
+ if result.empty?
157
+ result.concat dispatch_from_string(actor, command.join(' '))
158
+ end
159
+ result
160
+ end
161
+
162
+ # Get an array of actions, derived from the specified command, that the
163
+ # actor can potentially execute.
164
+ # The command should be a plain-text string, e.g., "examine the book."
165
+ #
166
+ # @return [Array<Gamefic::Action>]
167
+ def dispatch_from_string actor, text
168
+ result = []
169
+ commands = Syntax.tokenize(text, actor.syntaxes)
170
+ commands.each { |c|
171
+ available = actions_for(c.verb)
172
+ available.each { |a|
173
+ next if a.hidden?
174
+ o = a.attempt(actor, c.arguments)
175
+ result.unshift o unless o.nil?
176
+ }
177
+ }
178
+ sort_and_reduce_actions result
179
+ end
180
+
181
+ # Get an array of actions, derived from the specified verb and params,
182
+ # that the actor can potentially execute.
183
+ #
184
+ # @return [Array<Gamefic::Action>]
185
+ def dispatch_from_params actor, verb, params
186
+ result = []
187
+ available = actions_for(verb)
188
+ available.each { |a|
189
+ result.unshift a.new(actor, params) if a.valid?(actor, params)
190
+ }
191
+ sort_and_reduce_actions result
192
+ end
193
+
194
+ # Duplicate the playbook.
195
+ # This method will duplicate the commands hash and the syntax array so
196
+ # the new playbook can be modified without affecting the original.
197
+ #
198
+ # @return [Playbook]
199
+ def dup
200
+ Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
201
+ end
202
+
203
+ def freeze
204
+ @commands.freeze
205
+ @syntaxes.freeze
206
+ end
207
+
208
+ private
209
+
210
+ def add_action(action)
211
+ @commands[action.verb] ||= []
212
+ @commands[action.verb].push action
213
+ generate_default_syntax action
214
+ end
215
+
216
+ def generate_default_syntax action
217
+ user_friendly = action.verb.to_s.gsub(/_/, ' ')
218
+ args = []
219
+ used_names = []
220
+ action.queries.each { |c|
221
+ num = 1
222
+ new_name = ":var"
223
+ while used_names.include? new_name
224
+ num = num + 1
225
+ new_name = ":var#{num}"
226
+ end
227
+ used_names.push new_name
228
+ user_friendly += " #{new_name}"
229
+ args.push new_name
230
+ }
231
+ add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}") unless action.verb.to_s.start_with?('_')
232
+ end
233
+
234
+ def add_syntax syntax
235
+ if @commands[syntax.verb] == nil
236
+ raise "No actions exist for \"#{syntax.verb}\""
237
+ end
238
+ @syntaxes.unshift syntax
239
+ @syntaxes.uniq! &:signature
240
+ @syntaxes.sort! { |a, b|
241
+ if a.token_count == b.token_count
242
+ # For syntaxes of the same length, length of action takes precedence
243
+ b.first_word <=> a.first_word
244
+ else
245
+ b.token_count <=> a.token_count
246
+ end
247
+ }
248
+ end
249
+
250
+ def sort_and_reduce_actions arr
251
+ arr.sort_by.with_index{|a, i| [a.rank, -i]}.reverse.uniq(&:class)
252
+ end
253
+ end
254
+ end
255
+ end