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