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.
- checksums.yaml +4 -4
- data/lib/gamefic/action.rb +87 -56
- data/lib/gamefic/ansi.rb +55 -0
- data/lib/gamefic/character.rb +130 -76
- data/lib/gamefic/command.rb +19 -0
- data/lib/gamefic/core_ext/array.rb +51 -40
- data/lib/gamefic/core_ext/string.rb +4 -0
- data/lib/gamefic/describable.rb +108 -46
- data/lib/gamefic/direction.rb +46 -0
- data/lib/gamefic/director/delegate.rb +91 -0
- data/lib/gamefic/director/order.rb +10 -0
- data/lib/gamefic/director/parser.rb +119 -0
- data/lib/gamefic/director.rb +16 -197
- data/lib/gamefic/engine/cgi.rb +221 -0
- data/lib/gamefic/engine/tty.rb +237 -0
- data/lib/gamefic/engine.rb +88 -67
- data/lib/gamefic/entity.rb +96 -69
- data/lib/gamefic/grammar/conjugator.rb +20 -0
- data/lib/gamefic/grammar/gender.rb +11 -0
- data/lib/gamefic/grammar/person.rb +10 -0
- data/lib/gamefic/grammar/plural.rb +13 -0
- data/lib/gamefic/grammar/pronouns.rb +60 -0
- data/lib/gamefic/grammar/tense.rb +6 -0
- data/lib/gamefic/grammar/verb_set.rb +43 -0
- data/lib/gamefic/grammar/verbs.rb +25 -0
- data/lib/gamefic/grammar/word_adapter.rb +36 -0
- data/lib/gamefic/grammar.rb +13 -0
- data/lib/gamefic/html.rb +53 -0
- data/lib/gamefic/keywords.rb +51 -33
- data/lib/gamefic/node.rb +65 -58
- data/lib/gamefic/plot/article_mount.rb +22 -0
- data/lib/gamefic/plot/command_mount.rb +88 -0
- data/lib/gamefic/plot/entity_mount.rb +45 -0
- data/lib/gamefic/plot/query_mount.rb +9 -0
- data/lib/gamefic/plot/scene_mount.rb +181 -0
- data/lib/gamefic/plot/you_mount.rb +22 -0
- data/lib/gamefic/plot.rb +296 -247
- data/lib/gamefic/query/ambiguous_children.rb +5 -0
- data/lib/gamefic/query/base.rb +265 -0
- data/lib/gamefic/query/children.rb +10 -0
- data/lib/gamefic/query/expression.rb +47 -0
- data/lib/gamefic/query/family.rb +10 -0
- data/lib/gamefic/query/many_children.rb +7 -0
- data/lib/gamefic/query/matches.rb +11 -0
- data/lib/gamefic/query/parent.rb +10 -0
- data/lib/gamefic/query/plural_children.rb +14 -0
- data/lib/gamefic/query/self.rb +10 -0
- data/lib/gamefic/query/siblings.rb +10 -0
- data/lib/gamefic/query/text.rb +43 -0
- data/lib/gamefic/query.rb +19 -203
- data/lib/gamefic/rule.rb +18 -0
- data/lib/gamefic/scene/active.rb +25 -0
- data/lib/gamefic/scene/concluded.rb +22 -0
- data/lib/gamefic/scene/multiplechoice.rb +74 -0
- data/lib/gamefic/scene/paused.rb +26 -0
- data/lib/gamefic/scene/yesorno.rb +43 -0
- data/lib/gamefic/scene.rb +125 -0
- data/lib/gamefic/script/base.rb +33 -0
- data/lib/gamefic/script/file.rb +14 -0
- data/lib/gamefic/script/text.rb +14 -0
- data/lib/gamefic/script.rb +9 -0
- data/lib/gamefic/serialized.rb +24 -0
- data/lib/gamefic/shell.rb +9 -247
- data/lib/gamefic/snapshots.rb +134 -0
- data/lib/gamefic/source/base.rb +12 -0
- data/lib/gamefic/source/file.rb +23 -0
- data/lib/gamefic/source/text.rb +16 -0
- data/lib/gamefic/source.rb +9 -0
- data/lib/gamefic/stage.rb +75 -0
- data/lib/gamefic/syntax.rb +106 -124
- data/lib/gamefic/tester.rb +20 -0
- data/lib/gamefic/version.rb +3 -0
- data/lib/gamefic.rb +18 -12
- metadata +102 -70
- data/lib/gamefic/base.rb +0 -10
- data/lib/gamefic/before.rb +0 -12
- data/lib/gamefic/import/basics/actions/close.rb +0 -16
- data/lib/gamefic/import/basics/actions/commands.rb +0 -3
- data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
- data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
- data/lib/gamefic/import/basics/actions/drop.rb +0 -30
- data/lib/gamefic/import/basics/actions/enter.rb +0 -16
- data/lib/gamefic/import/basics/actions/go.rb +0 -35
- data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
- data/lib/gamefic/import/basics/actions/leave.rb +0 -29
- data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
- data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
- data/lib/gamefic/import/basics/actions/look.rb +0 -71
- data/lib/gamefic/import/basics/actions/nil.rb +0 -25
- data/lib/gamefic/import/basics/actions/open.rb +0 -23
- data/lib/gamefic/import/basics/actions/quit.rb +0 -3
- data/lib/gamefic/import/basics/actions/take.rb +0 -107
- data/lib/gamefic/import/basics/entities/container.rb +0 -8
- data/lib/gamefic/import/basics/entities/entity.rb +0 -11
- data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
- data/lib/gamefic/import/basics/entities/item.rb +0 -5
- data/lib/gamefic/import/basics/entities/portal.rb +0 -40
- data/lib/gamefic/import/basics/entities/room.rb +0 -30
- data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
- data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
- data/lib/gamefic/import/basics/entities/thing.rb +0 -16
- data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
- data/lib/gamefic/import/basics/queries/room.rb +0 -8
- data/lib/gamefic/import/basics/queries/visible.rb +0 -32
- data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
- data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
- data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
- data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
- data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
- data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
- data/lib/gamefic/import/basics.old/actions.rb +0 -1
- data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
- data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
- data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
- data/lib/gamefic/import/basics.old/entities.rb +0 -1
- data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
- data/lib/gamefic/import/basics.rb +0 -6
- data/lib/gamefic/import/room_modes.rb +0 -48
- data/lib/gamefic/import/standard.rb +0 -1
- data/lib/gamefic/meta.rb +0 -12
- data/lib/gamefic/optionset.rb +0 -114
- data/lib/gamefic/requirement.rb +0 -14
- data/lib/gamefic/story.rb +0 -14
- 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,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,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,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,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
|
|
1
|
+
require 'gamefic/keywords'
|
|
2
2
|
|
|
3
3
|
module Gamefic
|
|
4
4
|
|
|
5
5
|
module Query
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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
|
data/lib/gamefic/rule.rb
ADDED
|
@@ -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
|