gamefic 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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