gamefic 0.2.0 → 0.6.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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gamefic/action.rb +87 -56
  3. data/lib/gamefic/ansi.rb +55 -0
  4. data/lib/gamefic/character.rb +130 -76
  5. data/lib/gamefic/command.rb +19 -0
  6. data/lib/gamefic/core_ext/array.rb +51 -40
  7. data/lib/gamefic/core_ext/string.rb +4 -0
  8. data/lib/gamefic/describable.rb +108 -46
  9. data/lib/gamefic/direction.rb +46 -0
  10. data/lib/gamefic/director/delegate.rb +91 -0
  11. data/lib/gamefic/director/order.rb +10 -0
  12. data/lib/gamefic/director/parser.rb +119 -0
  13. data/lib/gamefic/director.rb +16 -197
  14. data/lib/gamefic/engine/cgi.rb +221 -0
  15. data/lib/gamefic/engine/tty.rb +237 -0
  16. data/lib/gamefic/engine.rb +88 -67
  17. data/lib/gamefic/entity.rb +96 -69
  18. data/lib/gamefic/grammar/conjugator.rb +20 -0
  19. data/lib/gamefic/grammar/gender.rb +11 -0
  20. data/lib/gamefic/grammar/person.rb +10 -0
  21. data/lib/gamefic/grammar/plural.rb +13 -0
  22. data/lib/gamefic/grammar/pronouns.rb +60 -0
  23. data/lib/gamefic/grammar/tense.rb +6 -0
  24. data/lib/gamefic/grammar/verb_set.rb +43 -0
  25. data/lib/gamefic/grammar/verbs.rb +25 -0
  26. data/lib/gamefic/grammar/word_adapter.rb +36 -0
  27. data/lib/gamefic/grammar.rb +13 -0
  28. data/lib/gamefic/html.rb +53 -0
  29. data/lib/gamefic/keywords.rb +51 -33
  30. data/lib/gamefic/node.rb +65 -58
  31. data/lib/gamefic/plot/article_mount.rb +22 -0
  32. data/lib/gamefic/plot/command_mount.rb +88 -0
  33. data/lib/gamefic/plot/entity_mount.rb +45 -0
  34. data/lib/gamefic/plot/query_mount.rb +9 -0
  35. data/lib/gamefic/plot/scene_mount.rb +181 -0
  36. data/lib/gamefic/plot/you_mount.rb +22 -0
  37. data/lib/gamefic/plot.rb +296 -247
  38. data/lib/gamefic/query/ambiguous_children.rb +5 -0
  39. data/lib/gamefic/query/base.rb +265 -0
  40. data/lib/gamefic/query/children.rb +10 -0
  41. data/lib/gamefic/query/expression.rb +47 -0
  42. data/lib/gamefic/query/family.rb +10 -0
  43. data/lib/gamefic/query/many_children.rb +7 -0
  44. data/lib/gamefic/query/matches.rb +11 -0
  45. data/lib/gamefic/query/parent.rb +10 -0
  46. data/lib/gamefic/query/plural_children.rb +14 -0
  47. data/lib/gamefic/query/self.rb +10 -0
  48. data/lib/gamefic/query/siblings.rb +10 -0
  49. data/lib/gamefic/query/text.rb +43 -0
  50. data/lib/gamefic/query.rb +19 -203
  51. data/lib/gamefic/rule.rb +18 -0
  52. data/lib/gamefic/scene/active.rb +25 -0
  53. data/lib/gamefic/scene/concluded.rb +22 -0
  54. data/lib/gamefic/scene/multiplechoice.rb +74 -0
  55. data/lib/gamefic/scene/paused.rb +26 -0
  56. data/lib/gamefic/scene/yesorno.rb +43 -0
  57. data/lib/gamefic/scene.rb +125 -0
  58. data/lib/gamefic/script/base.rb +33 -0
  59. data/lib/gamefic/script/file.rb +14 -0
  60. data/lib/gamefic/script/text.rb +14 -0
  61. data/lib/gamefic/script.rb +9 -0
  62. data/lib/gamefic/serialized.rb +24 -0
  63. data/lib/gamefic/shell.rb +9 -247
  64. data/lib/gamefic/snapshots.rb +134 -0
  65. data/lib/gamefic/source/base.rb +12 -0
  66. data/lib/gamefic/source/file.rb +23 -0
  67. data/lib/gamefic/source/text.rb +16 -0
  68. data/lib/gamefic/source.rb +9 -0
  69. data/lib/gamefic/stage.rb +75 -0
  70. data/lib/gamefic/syntax.rb +106 -124
  71. data/lib/gamefic/tester.rb +20 -0
  72. data/lib/gamefic/version.rb +3 -0
  73. data/lib/gamefic.rb +18 -12
  74. metadata +102 -70
  75. data/lib/gamefic/base.rb +0 -10
  76. data/lib/gamefic/before.rb +0 -12
  77. data/lib/gamefic/import/basics/actions/close.rb +0 -16
  78. data/lib/gamefic/import/basics/actions/commands.rb +0 -3
  79. data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
  80. data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
  81. data/lib/gamefic/import/basics/actions/drop.rb +0 -30
  82. data/lib/gamefic/import/basics/actions/enter.rb +0 -16
  83. data/lib/gamefic/import/basics/actions/go.rb +0 -35
  84. data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
  85. data/lib/gamefic/import/basics/actions/leave.rb +0 -29
  86. data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
  87. data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
  88. data/lib/gamefic/import/basics/actions/look.rb +0 -71
  89. data/lib/gamefic/import/basics/actions/nil.rb +0 -25
  90. data/lib/gamefic/import/basics/actions/open.rb +0 -23
  91. data/lib/gamefic/import/basics/actions/quit.rb +0 -3
  92. data/lib/gamefic/import/basics/actions/take.rb +0 -107
  93. data/lib/gamefic/import/basics/entities/container.rb +0 -8
  94. data/lib/gamefic/import/basics/entities/entity.rb +0 -11
  95. data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
  96. data/lib/gamefic/import/basics/entities/item.rb +0 -5
  97. data/lib/gamefic/import/basics/entities/portal.rb +0 -40
  98. data/lib/gamefic/import/basics/entities/room.rb +0 -30
  99. data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
  100. data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
  101. data/lib/gamefic/import/basics/entities/thing.rb +0 -16
  102. data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
  103. data/lib/gamefic/import/basics/queries/room.rb +0 -8
  104. data/lib/gamefic/import/basics/queries/visible.rb +0 -32
  105. data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
  106. data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
  107. data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
  108. data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
  109. data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
  110. data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
  111. data/lib/gamefic/import/basics.old/actions.rb +0 -1
  112. data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
  113. data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
  114. data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
  115. data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
  116. data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
  117. data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
  118. data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
  119. data/lib/gamefic/import/basics.old/entities.rb +0 -1
  120. data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
  121. data/lib/gamefic/import/basics.rb +0 -6
  122. data/lib/gamefic/import/room_modes.rb +0 -48
  123. data/lib/gamefic/import/standard.rb +0 -1
  124. data/lib/gamefic/meta.rb +0 -12
  125. data/lib/gamefic/optionset.rb +0 -114
  126. data/lib/gamefic/requirement.rb +0 -14
  127. data/lib/gamefic/story.rb +0 -14
  128. data/lib/gamefic/thing.rb +0 -7
@@ -0,0 +1,265 @@
1
+ module Gamefic::Query
2
+ class Base
3
+ @@ignored_words = ['a', 'an', 'the', 'and', ',']
4
+ @@subquery_prepositions = ['in', 'on', 'of', 'inside', 'from']
5
+ # Include is necessary here due to a strange namespace
6
+ # resolution bug when interpreting gfic files
7
+ include Gamefic
8
+ attr_accessor :arguments
9
+ def initialize *arguments
10
+ test_arguments arguments
11
+ @optional = false
12
+ if arguments.include?(:optional)
13
+ @optional = true
14
+ arguments.delete :optional
15
+ end
16
+ @arguments = arguments
17
+ @match_hash = Hash.new
18
+ end
19
+ def allow_ambiguous?
20
+ false
21
+ end
22
+ def allow_many?
23
+ false
24
+ end
25
+ def last_match_for(subject)
26
+ @match_hash[subject]
27
+ end
28
+ def optional?
29
+ @optional
30
+ end
31
+ def context_from(subject)
32
+ subject
33
+ end
34
+ def validate(subject, object)
35
+ arr = context_from(subject)
36
+ @arguments.each { |arg|
37
+ arr = arr.that_are(arg)
38
+ }
39
+ if (allow_many? or allow_ambiguous?)
40
+ if object.kind_of?(Array)
41
+ return (object & arr) == object
42
+ end
43
+ return false
44
+ elsif !object.kind_of?(Array)
45
+ return arr.include?(object)
46
+ end
47
+ return false
48
+ end
49
+ # @return [Array]
50
+ def execute(subject, description)
51
+ if (allow_many? or allow_ambiguous?) and !Query.allow_plurals?
52
+ return Matches.new([], '', description)
53
+ end
54
+ if !allow_ambiguous?
55
+ if allow_many? and !description.include?(',') and !description.downcase.include?(' and ')
56
+ return Matches.new([], '', description)
57
+ end
58
+ end
59
+ array = context_from(subject)
60
+ matches = self.match(description, array)
61
+ objects = matches.objects
62
+ matches = Matches.new(objects, matches.matching_text, matches.remainder)
63
+ if objects.length == 0 and matches.remainder == "it" and subject.respond_to?(:last_object)
64
+ if !subject.last_object.nil?
65
+ obj = subject.last_object
66
+ if validate(subject, obj)
67
+ matches = Matches.new([obj], "it", "")
68
+ end
69
+ end
70
+ end
71
+ @match_hash[subject] = matches
72
+ matches
73
+ end
74
+ def base_specificity
75
+ 0
76
+ end
77
+ def specificity
78
+ if @specificity == nil
79
+ @specificity = base_specificity
80
+ magnitude = 1
81
+ @arguments.each { |item|
82
+ if item.kind_of?(Entity)
83
+ @specificity += (magnitude * 10)
84
+ item = item.class
85
+ end
86
+ if item.kind_of?(Class)
87
+ s = item
88
+ while s != nil
89
+ @specificity += magnitude
90
+ s = s.superclass
91
+ end
92
+ else
93
+ @specificity += magnitude
94
+ end
95
+ }
96
+ if allow_many?
97
+ # HACK Ridiculously high magic number to force queries that return
98
+ # arrays to take precedence over everything
99
+ @specificity = @specificity * 10
100
+ end
101
+ end
102
+ @specificity
103
+ end
104
+ def signature
105
+ "#{self.class}(#{@arguments.join(',')})"
106
+ end
107
+ def test_arguments arguments
108
+ my_classes = [Gamefic::Entity]
109
+ my_objects = []
110
+ arguments.each { |a|
111
+ if a.kind_of?(Class) or a.kind_of?(Module)
112
+ my_classes.push a
113
+ elsif a.kind_of?(Entity)
114
+ my_objects.push a
115
+ elsif a.kind_of?(Symbol)
116
+ if my_classes.length == 0 and my_objects.length == 0
117
+ raise ArgumentError.new("Query signature requires at least one class, module, or object to accept a method symbol")
118
+ end
119
+ if my_classes.length > 0
120
+ responds = false
121
+ my_classes.each { |c|
122
+ if c.instance_methods.include?(a)
123
+ responds = true
124
+ break
125
+ end
126
+ }
127
+ if !responds
128
+ raise ArgumentError.new("Query signature does not have a target that responds to #{a}")
129
+ end
130
+ end
131
+ my_objects.each { |o|
132
+ if !o.respond_to?(a)
133
+ raise ArgumentError.new("Query signature contains object '#{o}' that does not respond to '#{a}'")
134
+ end
135
+ }
136
+ else
137
+ raise ArgumentError.new("Invalid argument '#{a}' in query signature")
138
+ end
139
+ }
140
+ end
141
+ def match(description, array)
142
+ if description.include?(',')
143
+ tmp = description.split(',', -1)
144
+ keywords = []
145
+ first = tmp.shift
146
+ if first.strip != ''
147
+ keywords.push first.strip
148
+ end
149
+ tmp.each { |t|
150
+ keywords.push ','
151
+ if t.strip != ''
152
+ keywords += t.strip.split_words
153
+ end
154
+ }
155
+ keywords = keywords.join(' ').split_words
156
+ else
157
+ keywords = description.split_words
158
+ end
159
+ array.each { |e|
160
+ if e.uid == keywords[0]
161
+ return Matches.new([e], keywords.shift, keywords.join(' '))
162
+ end
163
+ }
164
+ used = []
165
+ skipped = []
166
+ possibilities = array
167
+ at_least_one_match = false
168
+ while keywords.length > 0
169
+ next_word = keywords.shift
170
+ if @@subquery_prepositions.include?(next_word)
171
+ if !at_least_one_match
172
+ return Matches.new([], '', description)
173
+ end
174
+ so_far = keywords.join(' ')
175
+ in_matched = self.match(so_far, array)
176
+ if in_matched.objects.length > 0 and (in_matched.objects.length == 1 or in_matched.objects[0].kind_of?(Array))
177
+ # Subset matching should only consider the intersection of the
178
+ # original array and the matched object's children. This ensures
179
+ # that it won't erroneously match a child that was excluded from
180
+ # the original query.
181
+ parent = in_matched.objects.shift
182
+ subset = self.match(used.join(' '), (array & (parent.kind_of?(Array) ? parent[0].children : parent.children)))
183
+ if subset.objects.length == 1
184
+ if in_matched.objects.length == 0
185
+ return subset
186
+ else
187
+ return Matches.new([subset.objects] + in_matched.objects, subset.matching_text, subset.remainder)
188
+ end
189
+ end
190
+ end
191
+ end
192
+ used.push next_word
193
+ new_results = []
194
+ most_matches = 0.0
195
+ possibilities.each { |p|
196
+ words = Keywords.new(used.last)
197
+ if words.length > 0
198
+ matches = words.found_in(p.keywords, (allow_many? or allow_ambiguous?))
199
+ if matches > 0
200
+ new_results.push p
201
+ most_matches = matches
202
+ end
203
+ end
204
+ }
205
+ if new_results.length > 0
206
+ at_least_one_match = true
207
+ intersection = possibilities & new_results
208
+ if intersection.length == 0
209
+ skipped.push used.pop
210
+ else
211
+ skipped.clear
212
+ possibilities = intersection
213
+ end
214
+ elsif (next_word.downcase == 'and' or next_word == ',')
215
+ while keywords.first == ',' or keywords.first.downcase == 'and'
216
+ used.push keywords.shift
217
+ end
218
+ if allow_ambiguous?
219
+ # Ambiguous queries filter based on all keywords instead of
220
+ # building an array of specified entities
221
+ next
222
+ end
223
+ so_far = keywords.join(' ')
224
+ recursed = self.match(so_far, array)
225
+ if possibilities.length == 1 and !allow_ambiguous?
226
+ possibilities = [possibilities]
227
+ else
228
+ # Force lists of things to be uniquely identifying
229
+ return Matches.new([], '', description)
230
+ end
231
+ objects = recursed.objects.clone
232
+ while objects.length > 0
233
+ obj = objects.shift
234
+ if obj.kind_of?(Array)
235
+ possibilities.push obj
236
+ else
237
+ combined = [obj] + objects
238
+ possibilities.push combined
239
+ break
240
+ end
241
+ end
242
+ used += recursed.matching_text.split_words
243
+ skipped = recursed.remainder.split_words
244
+ keywords = []
245
+ else
246
+ # The first unignored word must have at least one match
247
+ if at_least_one_match and !@@ignored_words.include?(used.last)
248
+ keywords.unshift used.pop
249
+ return Matches.new(possibilities, used.join(' '), keywords.join(' '))
250
+ else
251
+ if !@@ignored_words.include?(used.last)
252
+ return Matches.new([], '', description)
253
+ end
254
+ end
255
+ end
256
+ end
257
+ if at_least_one_match and (used - @@ignored_words).length > 0
258
+ r = Matches.new(possibilities, used.join(' '), skipped.join(' '))
259
+ return r
260
+ else
261
+ return Matches.new([], '', description)
262
+ end
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,10 @@
1
+ module Gamefic::Query
2
+ class Children < Base
3
+ def base_specificity
4
+ 50
5
+ end
6
+ def context_from(subject)
7
+ subject.children
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ module Gamefic::Query
2
+ class Expression < Base
3
+ def base_specificity
4
+ 10
5
+ end
6
+ def validate(subject, description)
7
+ return false unless description.kind_of?(String)
8
+ valid = false
9
+ words = description.split_words
10
+ words.each { |word|
11
+ if description.include?(word)
12
+ valid = true
13
+ break
14
+ end
15
+ }
16
+ valid
17
+ end
18
+ def execute(subject, description)
19
+ if @arguments.length == 0
20
+ return Matches.new([description], description, '')
21
+ end
22
+ keywords = Keywords.new(description)
23
+ possible = []
24
+ @arguments.each { |regexp|
25
+ remainder = keywords.clone
26
+ used = []
27
+ while remainder.length > 0
28
+ used.push remainder.shift
29
+ if used.join(' ').match(regexp)
30
+ possible.push Matches.new([used.join(' ')], used.join(' '), remainder.join(' '))
31
+ end
32
+ end
33
+ }
34
+ if possible.length > 0
35
+ possible.sort! { |a, b|
36
+ b.matching_text.length <=> a.matching_text.length
37
+ }
38
+ return possible[0]
39
+ else
40
+ return Matches.new([], '', description)
41
+ end
42
+ end
43
+ def test_arguments arguments
44
+ # No test for text
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,10 @@
1
+ module Gamefic::Query
2
+ class Family < Base
3
+ def base_specificity
4
+ 40
5
+ end
6
+ def context_from(subject)
7
+ subject.children + subject.parent.children #+ [subject.parent]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'gamefic/query/children'
2
+
3
+ class Gamefic::Query::ManyChildren < Gamefic::Query::Children
4
+ def allow_many?
5
+ true
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module Gamefic::Query
2
+ class Matches
3
+ attr_reader :objects, :matching_text, :remainder
4
+ def initialize(objects, matching_text, remainder)
5
+ @objects = objects
6
+ @matching_text = matching_text
7
+ @remainder = remainder
8
+ @@last_match = self
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Gamefic::Query
2
+ class Parent < Base
3
+ def base_specificity
4
+ 30
5
+ end
6
+ def context_from(subject)
7
+ [subject.parent]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ class Gamefic::Query::PluralChildren < Gamefic::Query::AmbiguousChildren
2
+ def execute(subject, description)
3
+ if (!description.end_with?("s") and !description.end_with?("i") and !description.end_with?("ae")) or (description.end_with?("ous") or description.end_with?("ess"))
4
+ return Gamefic::Query::Matches.new([], '', description)
5
+ end
6
+ super
7
+ end
8
+ def validate(subject, object)
9
+ # Plural queries always return false on validation. Their only purpose is
10
+ # to provide syntactic sugar for plural nouns, so it should never get triggered
11
+ # by a token call.
12
+ false
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Gamefic::Query
2
+ class Self < Base
3
+ def base_specificity
4
+ 30
5
+ end
6
+ def context_from(subject)
7
+ [subject]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Gamefic::Query
2
+ class Siblings < Base
3
+ def base_specificity
4
+ 40
5
+ end
6
+ def context_from(subject)
7
+ (subject.parent.children - [subject])
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,43 @@
1
+ module Gamefic::Query
2
+ class Text < Base
3
+ def base_specificity
4
+ 10
5
+ end
6
+ def validate(subject, description)
7
+ return false unless description.kind_of?(String)
8
+ valid = false
9
+ words = description.split_words
10
+ words.each { |word|
11
+ if description.include?(word)
12
+ valid = true
13
+ break
14
+ end
15
+ }
16
+ valid
17
+ end
18
+ def execute(subject, description)
19
+ if @arguments.length == 0
20
+ return Matches.new([description], description, '')
21
+ end
22
+ keywords = Keywords.new(description)
23
+ args = Keywords.new(@arguments)
24
+ found = Array.new
25
+ remainder = keywords.clone
26
+ while remainder.length > 0
27
+ if args.include?(remainder.first)
28
+ found.push remainder.shift
29
+ else
30
+ break
31
+ end
32
+ end
33
+ if found.length > 0
34
+ return Matches.new(found, found.join(' '), remainder.join(' '))
35
+ else
36
+ return Matches.new([], '', description)
37
+ end
38
+ end
39
+ def test_arguments arguments
40
+ # No test for text
41
+ end
42
+ end
43
+ end
data/lib/gamefic/query.rb CHANGED
@@ -1,214 +1,30 @@
1
- require "gamefic/keywords"
1
+ require 'gamefic/keywords'
2
2
 
3
3
  module Gamefic
4
4
 
5
5
  module Query
6
-
7
- class Base
8
- attr_accessor :arguments
9
- def initialize *arguments
10
- @optional = false
11
- if arguments.include?(:optional)
12
- @optional = true
13
- arguments.delete :optional
14
- end
15
- @arguments = arguments
16
- end
17
- def optional?
18
- @optional
19
- end
20
- def context_from(subject)
21
- subject
22
- end
23
- def execute(subject, description)
24
- array = context_from(subject)
25
- @arguments.each { |arg|
26
- array = array.that_are(arg)
27
- }
28
- return Query.match(description, array)
29
- end
30
- def base_specificity
31
- 0
32
- end
33
- def specificity
34
- if @specificity == nil
35
- @specificity = base_specificity
36
- magnitude = 1
37
- @arguments.each { |item|
38
- if item.kind_of?(Entity)
39
- @specificity += (magnitude * 10)
40
- item = item.class
41
- end
42
- if item.kind_of?(Class)
43
- s = item
44
- while s != nil
45
- @specificity += magnitude
46
- s = s.superclass
47
- end
48
- else
49
- @specificity += magnitude
50
- end
51
- #magnitude = magnitude * 10
52
- }
53
- end
54
- @specificity
55
- end
56
- end
6
+ autoload :Base, 'gamefic/query/base'
7
+ autoload :Text, 'gamefic/query/text'
8
+ autoload :Expression, 'gamefic/query/expression'
9
+ autoload :Self, 'gamefic/query/self'
10
+ autoload :Parent, 'gamefic/query/parent'
11
+ autoload :Children, 'gamefic/query/children'
12
+ autoload :ManyChildren, 'gamefic/query/many_children'
13
+ autoload :AmbiguousChildren, 'gamefic/query/ambiguous_children'
14
+ autoload :PluralChildren, 'gamefic/query/plural_children'
15
+ autoload :Siblings, 'gamefic/query/siblings'
16
+ autoload :Family, 'gamefic/query/family'
17
+ autoload :Matches, 'gamefic/query/matches'
57
18
 
58
- class Text < Base
59
- def base_specificity
60
- 10
61
- end
62
- def execute(subject, description)
63
- if @arguments.length == 0
64
- return Matches.new([description], description, '')
65
- end
66
- keywords = Keywords.new(description)
67
- args = Keywords.new(@arguments)
68
- found = Array.new
69
- remainder = Array.new
70
- keywords.each { |key|
71
- if args.include?(key)
72
- found.push key
73
- else
74
- remainder.push key
75
- end
76
- }
77
- if found.length > 0
78
- return Matches.new([description], found.join(' '), remainder.join(' '))
79
- else
80
- return Matches.new([], '', description)
81
- end
19
+ def self.allow_plurals?
20
+ if @allow_plurals.nil?
21
+ @allow_plurals = true
82
22
  end
23
+ @allow_plurals
83
24
  end
84
-
85
- class Self < Base
86
- def base_specificity
87
- 30
88
- end
89
- def context_from(subject)
90
- [subject]
91
- end
25
+ def self.allow_plurals= boolean
26
+ @allow_plurals = boolean
92
27
  end
93
-
94
- class Parent < Base
95
- def base_specificity
96
- 30
97
- end
98
- def context_from(subject)
99
- [subject.parent]
100
- end
101
- end
102
-
103
- class Children < Base
104
- def base_specificity
105
- 50
106
- end
107
- def context_from(subject)
108
- subject.children
109
- end
110
- end
111
-
112
- class Siblings < Base
113
- def base_specificity
114
- 40
115
- end
116
- def context_from(subject)
117
- (subject.parent.children - [subject])
118
- end
119
- end
120
-
121
- class Family < Base
122
- def base_specificity
123
- 40
124
- end
125
- def context_from(subject)
126
- subject.children + subject.parent.children #+ [subject.parent]
127
- end
128
- end
129
-
130
- def self.match(description, array)
131
- array.each {|e|
132
- if e.uid == description
133
- return Matches.new([e], description, '')
134
- end
135
- }
136
- keywords = description.split_words
137
- results = array
138
- used = Array.new
139
- if results.length > 0
140
- previous_match = false
141
- while keywords.length > 0
142
- used.push keywords.shift
143
- new_results = Array.new
144
- mostMatches = 0.0
145
- results.each { |r|
146
- words = Keywords.new(used.join(' '))
147
- if words.length > 0
148
- matches = words.found_in r.keywords
149
- if matches >= mostMatches and matches > 0
150
- if matches - mostMatches > 0.5
151
- new_results = Array.new
152
- end
153
- new_results.push r
154
- mostMatches = matches
155
- end
156
- end
157
- }
158
- if new_results.length == 0
159
- if previous_match == true
160
- keywords.unshift used.pop
161
- if used.length == 0
162
- results = new_results
163
- end
164
- break
165
- end
166
- else
167
- previous_match = true
168
- results = new_results
169
- # TODO: Uncommenting this code results in "lazy" word matching
170
- #if results.length == 1
171
- # break
172
- #end
173
- end
174
- end
175
- if previous_match == false
176
- # Scrolled through every word and not a single thing matched
177
- results = Array.new
178
- end
179
- end
180
- return Matches.new(results, used.join(' '), keywords.join(' '))
181
- end
182
-
183
- class Subquery < Base
184
- def base_specificity
185
- 40
186
- end
187
- def context_from(subject)
188
- last = Matches.last_match
189
- return [] if last.nil?
190
- last.children
191
- end
192
- end
193
-
194
- class Matches
195
- @@last_match = nil
196
- attr_reader :objects, :matching_text, :remainder
197
- def initialize(objects, matching_text, remainder)
198
- @objects = objects
199
- @matching_text = matching_text
200
- @remainder = remainder
201
- @@last_match = self
202
- end
203
- def self.last_match
204
- return nil if @@last_match.nil?
205
- if @@last_match.objects.length == 1
206
- return @@last_match.objects[0]
207
- end
208
- return nil
209
- end
210
- end
211
-
212
28
  end
213
29
 
214
30
  end
@@ -0,0 +1,18 @@
1
+ class Rule
2
+ attr_reader :caller
3
+ def initialize name, &block
4
+ @name = name
5
+ @block = block
6
+ end
7
+ def test actor, verb, arguments
8
+ return @block.call actor, verb, arguments
9
+ end
10
+ end
11
+
12
+ class Assert < Rule
13
+
14
+ end
15
+
16
+ #class Finish < Rule
17
+ #
18
+ #end