gamefic 2.1.1 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d4af1f6add0c467fce62acde7d4903e2165a0007312720ac0a43fb324e2a594
4
- data.tar.gz: fe6adad9a8c85fd9eac1b9b6921e13f356da73489e9d82c95b63974f94f45df0
3
+ metadata.gz: 361eaf767babb3c8b5d7e47616d326a85e1b42269da33c518de33f16f9568451
4
+ data.tar.gz: 96f4d79e833f93c41714c5781cf863d7e0b8832e90c746f6bafe6164e10095b5
5
5
  SHA512:
6
- metadata.gz: b2b0fda9b69825c655f6dfa90fa4c1bca78a6a53ea625946d54f9981d11104660f1e4f312bc53086559345f0c211edf979f237e18fd4bf95d83a2755e7b1cc9b
7
- data.tar.gz: b1c85ff6f5c103354af38588bc129c989929c259ef4062abd8e9a0e5f92100fcf2134e1d16f3d8e3180bbcacd672d85b3cf5d1f162a8ff19217a64dc419e9bd7
6
+ metadata.gz: b9ad845f6f0271da17b64268b911f5d3fe40fbfd7fd563b411f6ef02e15b8a117abcd924d3586072040f428aafc0a92827458639f9f2c0ba9fa4883af7f33e05
7
+ data.tar.gz: 6f65996ac0ee16805763dd25fa89b1809a89acd0e44cb5d55f83ce22636aeadb6e43b733a15d44e6a397a636d5abde899c332d3601af60bcc4322a0c0ad1fb72
data/CHANGELOG.md CHANGED
@@ -1,13 +1,16 @@
1
- # 2.1.1 - July 23, 2021
1
+ ## 2.2.0 - September 4, 2021
2
+ - Dynamically inherit default attributes
3
+
4
+ ## 2.1.1 - July 23, 2021
2
5
  - Remove gamefic/scene/custom autoload
3
6
 
4
- # 2.1.0 - June 21, 2021
7
+ ## 2.1.0 - June 21, 2021
5
8
  - Remove redundant MultipleChoice prompt
6
9
  - Deprecate Scene::Custom
7
10
 
8
- # 2.0.3 - December 14, 2020
11
+ ## 2.0.3 - December 14, 2020
9
12
  - Remove unused Index class
10
13
  - Active#conclude accepts data argument
11
14
 
12
- # 2.0.2 - April 25, 2020
15
+ ## 2.0.2 - April 25, 2020
13
16
  - Improved snapshot serialization
@@ -123,10 +123,8 @@ module Gamefic
123
123
 
124
124
  def valid? actor, objects
125
125
  return false if objects.length != queries.length
126
- i = 0
127
- queries.each do |p|
126
+ queries.each_with_index do |p, i|
128
127
  return false unless p.include?(actor, objects[i])
129
- i += 1
130
128
  end
131
129
  true
132
130
  end
@@ -135,9 +133,11 @@ module Gamefic
135
133
  # provided tokens, or nil if the tokens are invalid.
136
134
  #
137
135
  # @param action [Gamefic::Entity]
138
- # @param tokens [Array<String>]
136
+ # @param command [Command]
139
137
  # @return [self, nil]
140
- def attempt actor, tokens
138
+ def attempt actor, command
139
+ return nil if command.verb != verb
140
+ tokens = command.arguments
141
141
  result = []
142
142
  matches = Gamefic::Query::Matches.new([], '', '')
143
143
  queries.each_with_index do |p, i|
@@ -6,12 +6,6 @@ module Gamefic
6
6
  # subclass that includes this module.
7
7
  #
8
8
  module Active
9
- # The last action executed by the entity, as reported by the
10
- # Active#performed method.
11
- #
12
- # @return [Gamefic::Action]
13
- attr_reader :last_action
14
-
15
9
  # The scene in which the entity is currently participating.
16
10
  #
17
11
  # @return [Gamefic::Scene::Base]
@@ -104,17 +98,29 @@ module Gamefic
104
98
  # @example Send a command as a verb with parameters
105
99
  # character.perform :take, @key
106
100
  #
101
+ # @todo Modify this method so it only accepts a single command.
102
+ # Verbs with parameters should use Active#execute instead.
103
+ # It might be necessary to support command splats with a deprecation
104
+ # warning until version 3.
105
+ #
107
106
  # @return [Gamefic::Action]
108
107
  def perform(*command)
109
- actions = []
110
- playbooks.reverse.each { |p| actions.concat p.dispatch(self, *command) }
111
- execute_stack actions
108
+ if command.length > 1
109
+ execute command.first, *command[1..-1]
110
+ else
111
+ dispatchers.push Dispatcher.dispatch(self, command.first.to_s)
112
+ proceed
113
+ dispatchers.pop
114
+ end
112
115
  end
113
116
 
114
117
  # Quietly perform a command.
115
118
  # This method executes the command exactly as #perform does, except it
116
119
  # buffers the resulting output instead of sending it to the user.
117
120
  #
121
+ # @todo Modify this method so it only accepts a single command.
122
+ # See Active#perform for more information.
123
+ #
118
124
  # @return [String] The output that resulted from performing the command.
119
125
  def quietly(*command)
120
126
  clear_buffer if buffer_stack == 0
@@ -137,9 +143,9 @@ module Gamefic
137
143
  #
138
144
  # @return [Gamefic::Action]
139
145
  def execute(verb, *params, quietly: false)
140
- actions = []
141
- playbooks.reverse.each { |p| actions.concat p.dispatch_from_params(self, verb, params) }
142
- execute_stack actions, quietly: quietly
146
+ dispatchers.push Dispatcher.dispatch_from_params(self, verb, params)
147
+ proceed quietly: quietly
148
+ dispatchers.pop
143
149
  end
144
150
 
145
151
  # Proceed to the next Action in the current stack.
@@ -166,20 +172,19 @@ module Gamefic
166
172
  # end
167
173
  #
168
174
  def proceed quietly: false
169
- return if performance_stack.empty?
170
- a = performance_stack.last.shift
171
- unless a.nil?
172
- if quietly
173
- if buffer_stack == 0
174
- @buffer = ""
175
- end
176
- set_buffer_stack(buffer_stack + 1)
177
- end
178
- a.execute
179
- if quietly
180
- set_buffer_stack(buffer_stack - 1)
181
- @buffer
175
+ return if dispatchers.empty?
176
+ a = dispatchers.last.next
177
+ return if a.nil?
178
+ if quietly
179
+ if buffer_stack == 0
180
+ @buffer = ""
182
181
  end
182
+ set_buffer_stack(buffer_stack + 1)
183
+ end
184
+ a.execute
185
+ if quietly
186
+ set_buffer_stack(buffer_stack - 1)
187
+ @buffer
183
188
  end
184
189
  end
185
190
 
@@ -235,14 +240,6 @@ module Gamefic
235
240
  !scene.nil? && scene.kind_of?(Scene::Conclusion)
236
241
  end
237
242
 
238
- # Record the last action the entity executed. This method is typically
239
- # called when the entity performs an action in response to user input.
240
- #
241
- def performed action
242
- action.freeze
243
- @last_action = action
244
- end
245
-
246
243
  def accessible?
247
244
  false
248
245
  end
@@ -270,37 +267,7 @@ module Gamefic
270
267
 
271
268
  # @return [Array<Gamefic::Scene::Base>]
272
269
  def entered_scenes
273
- @entered_scenes ||= []
274
- end
275
-
276
- # @param actions [Array<Gamefic::Action>]
277
- # @param quietly [Boolean]
278
- def execute_stack actions, quietly: false
279
- return nil if actions.empty?
280
- a = actions.first
281
- okay = true
282
- unless a.meta?
283
- playbooks.reverse.each do |playbook|
284
- okay = validate_playbook playbook, a
285
- break unless okay
286
- end
287
- end
288
- if okay
289
- performance_stack.push actions
290
- proceed quietly: quietly
291
- performance_stack.pop
292
- end
293
- a
294
- end
295
-
296
- def validate_playbook playbook, action
297
- okay = true
298
- playbook.validators.each { |v|
299
- result = v.call(self, action.verb, action.parameters)
300
- okay = (result != false)
301
- break unless okay
302
- }
303
- okay
270
+ @entered_scenes ||= []
304
271
  end
305
272
 
306
273
  def buffer_stack
@@ -324,8 +291,8 @@ module Gamefic
324
291
  @buffer = ''
325
292
  end
326
293
 
327
- def performance_stack
328
- @performance_stack ||= []
294
+ def dispatchers
295
+ @dispatchers ||= []
329
296
  end
330
297
  end
331
298
  end
@@ -64,7 +64,7 @@ class Array
64
64
  join_and(sep, orSep, serial)
65
65
  end
66
66
 
67
- # private
67
+ private
68
68
 
69
69
  def _keep(arr, cls, bool)
70
70
  if (cls.kind_of?(Class) or cls.kind_of?(Module))
@@ -6,15 +6,9 @@ class String
6
6
  #
7
7
  # @return [String] The capitalized text
8
8
  def capitalize_first
9
- "#{self[0,1].upcase}#{self[1,self.length]}"
10
- end
11
-
12
- # An alias for #capitalize_first.
13
- #
14
- # @return [String]
15
- def cap_first
16
- self.capitalize_first
9
+ "#{self[0, 1].upcase}#{self[1, self.length]}"
17
10
  end
11
+ alias cap_first capitalize_first
18
12
 
19
13
  # Get an array of words split by any whitespace.
20
14
  #
@@ -0,0 +1,76 @@
1
+ module Gamefic
2
+ # The action selector for character commands.
3
+ #
4
+ class Dispatcher
5
+ # @param actor [Actor]
6
+ # @param commands [Array<Command>]
7
+ # @param actions [Array<Action>]
8
+ def initialize actor, commands = [], actions = []
9
+ @actor = actor
10
+ @commands = commands
11
+ @actions = actions
12
+ end
13
+
14
+ def merge dispatcher
15
+ commands.concat dispatcher.commands
16
+ actions.concat dispatcher.actions
17
+ end
18
+
19
+ def next
20
+ instance = nil
21
+ while instance.nil? && !@actions.empty?
22
+ action = actions.shift
23
+ commands.each do |cmd|
24
+ instance = action.attempt(actor, cmd)
25
+ if instance
26
+ unless instance.meta?
27
+ actor.playbooks.reverse.each do |playbook|
28
+ return nil unless validate_playbook(playbook, instance)
29
+ end
30
+ end
31
+ break
32
+ end
33
+ end
34
+ end
35
+ instance
36
+ end
37
+
38
+ # @param actor [Active]
39
+ # @param command [String]
40
+ # @return [Dispatcher]
41
+ def self.dispatch actor, command
42
+ group = actor.playbooks.reverse.map { |p| p.dispatch(actor, command) }
43
+ dispatcher = Dispatcher.new(actor)
44
+ group.each { |d| dispatcher.merge d }
45
+ dispatcher
46
+ end
47
+
48
+ # @param actor [Active]
49
+ # @param verb [Symbol]
50
+ # @param params [Array<Object>]
51
+ # @return [Dispatcher]
52
+ def self.dispatch_from_params actor, verb, params
53
+ group = actor.playbooks.reverse.map { |p| p.dispatch_from_params(actor, verb, params) }
54
+ dispatcher = Dispatcher.new(actor)
55
+ group.each { |d| dispatcher.merge d }
56
+ dispatcher
57
+ end
58
+
59
+ protected
60
+
61
+ # @return [Actor]
62
+ attr_reader :actor
63
+
64
+ # @return [Array<Command>]
65
+ attr_reader :commands
66
+
67
+ # @return [Array<Action>]
68
+ attr_reader :actions
69
+
70
+ private
71
+
72
+ def validate_playbook playbook, action
73
+ playbook.validators.all? { |v| v.call(actor, action.verb, action.parameters) != false }
74
+ end
75
+ end
76
+ end
@@ -7,13 +7,16 @@ module Gamefic
7
7
  #
8
8
  class Element
9
9
  include Gamefic::Describable
10
- # include Gamefic::Index
11
10
  include Gamefic::Serialize
12
11
 
13
- # @todo It would be nice if this initialization wasn't necessary.
14
12
  def initialize(args = {})
15
- # super self.class.default_attributes.merge(args)
16
- self.class.default_attributes.merge(args).each_pair do |k, v|
13
+ klass = self.class
14
+ defaults = {}
15
+ while klass <= Element
16
+ defaults = klass.default_attributes.merge(defaults)
17
+ klass = klass.superclass
18
+ end
19
+ defaults.merge(args).each_pair do |k, v|
17
20
  public_send "#{k}=", v
18
21
  end
19
22
  post_initialize
@@ -38,10 +41,6 @@ module Gamefic
38
41
  def default_attributes
39
42
  @default_attributes ||= {}
40
43
  end
41
-
42
- def inherited subclass
43
- subclass.set_default default_attributes
44
- end
45
44
  end
46
45
  end
47
46
  end
@@ -13,10 +13,10 @@ module Gamefic
13
13
 
14
14
  # Set the Entity's parent.
15
15
  #
16
- # @param node [Gamefic::Entity] The new parent.
16
+ # @param node [Gamefic::Entity, nil] The new parent.
17
17
  def parent=(node)
18
- if node != nil and node.kind_of?(Entity) == false
19
- raise "Entity's parent must be an Entity"
18
+ if node && node.is_a?(Entity) == false
19
+ raise ArgumentError, "Entity's parent must be an Entity"
20
20
  end
21
21
  super
22
22
  end
data/lib/gamefic/plot.rb CHANGED
@@ -74,7 +74,6 @@ module Gamefic
74
74
  entities.each { |e| e.flush }
75
75
  call_before_player_update
76
76
  players.each do |p|
77
- p.performed nil
78
77
  next unless p.scene
79
78
  p.last_input = p.queue.last
80
79
  p.last_prompt = p.scene.prompt
@@ -10,9 +10,7 @@ module Gamefic
10
10
 
11
11
  def finish
12
12
  super
13
- o = nil
14
- o = actor.perform input.strip unless input.to_s.strip.empty?
15
- actor.performed o
13
+ actor.perform input.strip unless input.to_s.strip.empty?
16
14
  end
17
15
 
18
16
  class << self
@@ -32,13 +32,6 @@ module Gamefic
32
32
  end
33
33
  end
34
34
 
35
- def self.instances
36
- GC.start
37
- result = []
38
- ObjectSpace.each_object(Gamefic::Serialize) { |obj| result.push obj }
39
- result
40
- end
41
-
42
35
  # @param string [String]
43
36
  # @return [Object]
44
37
  def self.string_to_constant string
@@ -71,7 +64,7 @@ class Object
71
64
  end
72
65
 
73
66
  def from_serial(index = [])
74
- if self.is_a?(Hash) && (self['class'] || self['instance'])
67
+ if self.is_a?(Hash)
75
68
  if self['instance']
76
69
  elematch = self['instance'].match(/^#<ELE_([\d]+)>$/)
77
70
  object = index[elematch[1].to_i]
@@ -110,25 +103,8 @@ class Object
110
103
  return index.index(match[1].to_i) if match
111
104
  match = self.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
112
105
  return match[1].to_sym if match
113
- # return nil if self == '#<UNKNOWN>'
106
+ return nil if self == '#<UNKNOWN>'
114
107
  self
115
- elsif self.is_a?(Hash)
116
- result = {}
117
- unknown = false
118
- self.each_pair do |k, v|
119
- k2 = k.from_serial(index)
120
- v2 = v.from_serial(index)
121
- if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
122
- unknown = true
123
- break
124
- end
125
- result[k2] = v2
126
- end
127
- result = "#<UNKNOWN>" if unknown
128
- result
129
- elsif self && self != true
130
- STDERR.puts "Unable to unserialize #{self.class}"
131
- nil
132
108
  else
133
109
  # true, false, or nil
134
110
  self
@@ -1,5 +1,3 @@
1
- require 'gamefic/command'
2
-
3
1
  module Gamefic
4
2
  class Syntax
5
3
  attr_reader :token_count, :first_word, :verb, :template, :command
@@ -57,17 +55,18 @@ module Gamefic
57
55
  m = text.match(@regexp)
58
56
  return nil if m.nil?
59
57
  arguments = []
60
- # HACK: Skip the first word if the verb is not nil.
61
- # This is ugly.
62
58
  b = @verb.nil? ? 0 : 1
59
+ xverb = @verb
63
60
  @replace.to_s.split_words[b..-1].each { |r|
64
61
  if r.match(/^\{\$[0-9]+\}$/)
65
62
  arguments.push m[r[2..-2].to_i]
63
+ elsif arguments.empty? && xverb.nil?
64
+ xverb = r.to_sym
66
65
  else
67
66
  arguments.push r
68
67
  end
69
68
  }
70
- Command.new @verb, arguments
69
+ Command.new xverb, arguments
71
70
  end
72
71
 
73
72
  # Determine if the specified text matches the syntax's expected pattern.
@@ -90,25 +89,32 @@ module Gamefic
90
89
  signature == other.signature
91
90
  end
92
91
 
93
- # Tokenize an Array of Commands from the specified text.
92
+ def eql?(other)
93
+ self == other
94
+ end
95
+
96
+ def hash
97
+ signature.hash
98
+ end
99
+
100
+ # Tokenize an Array of Commands from the specified text. The resulting
101
+ # array is in descending order of specificity, i.e., most to least matched
102
+ # tokens.
94
103
  #
95
104
  # @param text [String] The text to tokenize.
96
105
  # @param syntaxes [Array<Gamefic::Syntax>] The Syntaxes to use.
97
106
  # @return [Array<Gamefic::Command>] The tokenized commands.
98
107
  def self.tokenize text, syntaxes
99
- matches = []
100
- syntaxes.each { |syntax|
101
- result = syntax.tokenize text
102
- matches.push(result) if !result.nil?
103
- }
104
- matches.sort! { |a,b|
105
- if a.arguments.length == b.arguments.length
106
- b.verb.to_s <=> a.verb.to_s
107
- else
108
- b.arguments.length <=> a.arguments.length
108
+ syntaxes
109
+ .map { |syn| syn.tokenize(text) }
110
+ .reject(&:nil?)
111
+ .sort do |a, b|
112
+ if a.arguments.length == b.arguments.length
113
+ b.verb.to_s <=> a.verb.to_s
114
+ else
115
+ b.arguments.length <=> a.arguments.length
116
+ end
109
117
  end
110
- }
111
- matches
112
118
  end
113
119
  end
114
120
  end
@@ -1,3 +1,3 @@
1
1
  module Gamefic
2
- VERSION = '2.1.1'
2
+ VERSION = '2.2.0'
3
3
  end
@@ -95,25 +95,6 @@ module Gamefic
95
95
  playbook.meta command, *queries, &proc
96
96
  end
97
97
 
98
- # Declare a dismabiguation response for actions.
99
- # The disambiguator is executed when an action expects an argument to
100
- # reference a specific entity but its query matched more than one. For
101
- # example, "red" might refer to either a red key or a red book.
102
- #
103
- # If a disambiguator is not defined, the playbook will use its default
104
- # implementation.
105
- #
106
- # @example Tell the player the list of ambiguous entities.
107
- # disambiguate do |actor, entities|
108
- # actor.tell "I don't know which you mean: #{entities.join_or}."
109
- # end
110
- #
111
- # @yieldparam [Gamefic::Actor]
112
- # @yieldparam [Array<Gamefic::Entity>]
113
- def disambiguate &block
114
- playbook.disambiguate &block
115
- end
116
-
117
98
  # Validate an order before a character can execute its command.
118
99
  #
119
100
  # @yieldparam [Gamefic::Director::Order]
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module Gamefic
2
4
  module World
3
5
  # A collection of rules for performing commands.
@@ -13,11 +15,14 @@ module Gamefic
13
15
  # @return [Array<Proc>]
14
16
  attr_reader :validators
15
17
 
16
- def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
18
+ # @param commands [Hash]
19
+ # @param syntaxes [Array<Syntax>, Set<Syntax>]
20
+ # @param validators [Array]
21
+ def initialize commands: {}, syntaxes: [], validators: []
17
22
  @commands = commands
18
- @syntaxes = syntaxes
23
+ @syntax_set = syntaxes.to_set
24
+ sort_syntaxes
19
25
  @validators = validators
20
- @disambiguator = disambiguator
21
26
  end
22
27
 
23
28
  # An array of available actions.
@@ -34,25 +39,6 @@ module Gamefic
34
39
  @commands.keys
35
40
  end
36
41
 
37
- # Get the action for handling ambiguous entity references.
38
- #
39
- def disambiguator
40
- @disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities|
41
- definites = []
42
- entities.each do |entity|
43
- definites.push entity.definitely
44
- end
45
- actor.tell "I don't know which you mean: #{definites.join_or}."
46
- end
47
- end
48
-
49
- # Set the action for handling ambiguous entity references.
50
- #
51
- def disambiguate &block
52
- @disambiguator = Action.subclass(nil, Query::Base.new, meta: true, &block)
53
- @disambiguator
54
- end
55
-
56
42
  # Add a block that determines whether an action can be executed.
57
43
  #
58
44
  def validate &block
@@ -138,35 +124,16 @@ module Gamefic
138
124
  syn
139
125
  end
140
126
 
141
- # Get an array of actions, derived from the specified command, that the
142
- # actor can potentially execute.
143
- # The command can either be a single string (e.g., "examine book") or a
144
- # list of tokens (e.g., :examine, @book).
145
- #
146
- # @return [Array<Gamefic::Action>]
147
- def dispatch(actor, *command)
148
- result = []
149
- result.concat dispatch_from_params(actor, command[0], command[1..-1]) if command.length > 1
150
- result.concat dispatch_from_string(actor, command.join(' ')) if result.empty?
151
- result
152
- end
153
-
154
- # Get an array of actions, derived from the specified command, that the
155
- # actor can potentially execute.
156
- # The command should be a plain-text string, e.g., "examine the book."
127
+ # Get a Dispatcher to select actions that can potentially be executed
128
+ # from the specified command string.
157
129
  #
158
- # @return [Array<Gamefic::Action>]
159
- def dispatch_from_string actor, text
160
- result = []
130
+ # @param actor [Actor]
131
+ # @param text [String]
132
+ # @return [Dispatcher]
133
+ def dispatch(actor, text)
161
134
  commands = Syntax.tokenize(text, actor.syntaxes)
162
- commands.each do |c|
163
- actions_for(c.verb).each do |a|
164
- next if a.hidden?
165
- o = a.attempt(actor, c.arguments)
166
- result.unshift o unless o.nil?
167
- end
168
- end
169
- sort_and_reduce_actions result
135
+ actions = commands.flat_map { |cmd| actions_for(cmd.verb).reject(&:hidden?) }
136
+ Dispatcher.new(actor, commands, sort_and_reduce_actions(actions))
170
137
  end
171
138
 
172
139
  # Get an array of actions, derived from the specified verb and params,
@@ -174,12 +141,8 @@ module Gamefic
174
141
  #
175
142
  # @return [Array<Gamefic::Action>]
176
143
  def dispatch_from_params actor, verb, params
177
- result = []
178
144
  available = actions_for(verb)
179
- available.each do |a|
180
- result.unshift a.new(actor, params) if a.valid?(actor, params)
181
- end
182
- sort_and_reduce_actions result
145
+ Dispatcher.new(actor, [Command.new(verb, params)], sort_and_reduce_actions(available))
183
146
  end
184
147
 
185
148
  # Duplicate the playbook.
@@ -224,22 +187,23 @@ module Gamefic
224
187
 
225
188
  def add_syntax syntax
226
189
  raise "No actions exist for \"#{syntax.verb}\"" if @commands[syntax.verb].nil?
190
+ sort_syntaxes if @syntax_set.add?(syntax)
191
+ end
227
192
 
228
- @syntaxes.unshift syntax
229
- @syntaxes.uniq!(&:signature)
230
- @syntaxes.sort! do |a, b|
193
+ def sort_and_reduce_actions arr
194
+ arr.sort_by.with_index { |a, i| [a.rank, i] }.reverse.uniq
195
+ end
196
+
197
+ def sort_syntaxes
198
+ @syntaxes = @syntax_set.sort do |a, b|
231
199
  if a.token_count == b.token_count
232
- # For syntaxes of the same length, length of action takes precedence
200
+ # For syntaxes of the same length, sort first word
233
201
  b.first_word <=> a.first_word
234
202
  else
235
203
  b.token_count <=> a.token_count
236
204
  end
237
205
  end
238
206
  end
239
-
240
- def sort_and_reduce_actions arr
241
- arr.sort_by.with_index { |a, i| [a.rank, -i]}.reverse.uniq(&:class)
242
- end
243
207
  end
244
208
  end
245
209
  end
data/lib/gamefic.rb CHANGED
@@ -14,6 +14,8 @@ require "gamefic/scene"
14
14
  require "gamefic/query"
15
15
  require "gamefic/action"
16
16
  require "gamefic/syntax"
17
+ require "gamefic/command"
18
+ require "gamefic/dispatcher"
17
19
  require 'gamefic/world'
18
20
  require 'gamefic/scriptable'
19
21
  require 'gamefic/plot'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gamefic
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-03 00:00:00.000000000 Z
11
+ date: 2021-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -88,6 +88,7 @@ files:
88
88
  - lib/gamefic/core_ext/array.rb
89
89
  - lib/gamefic/core_ext/string.rb
90
90
  - lib/gamefic/describable.rb
91
+ - lib/gamefic/dispatcher.rb
91
92
  - lib/gamefic/element.rb
92
93
  - lib/gamefic/entity.rb
93
94
  - lib/gamefic/keywords.rb