gamefic 1.7.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +16 -0
  5. data/.solargraph.yml +5 -0
  6. data/CHANGELOG.md +10 -0
  7. data/Gemfile +7 -0
  8. data/LICENSE +20 -0
  9. data/README.md +28 -0
  10. data/Rakefile +10 -0
  11. data/gamefic.gemspec +27 -0
  12. data/lib/gamefic.rb +7 -7
  13. data/lib/gamefic/action.rb +66 -60
  14. data/lib/gamefic/active.rb +331 -280
  15. data/lib/gamefic/actor.rb +8 -5
  16. data/lib/gamefic/command.rb +9 -7
  17. data/lib/gamefic/core_ext/array.rb +27 -49
  18. data/lib/gamefic/core_ext/string.rb +25 -16
  19. data/lib/gamefic/describable.rb +21 -23
  20. data/lib/gamefic/element.rb +47 -31
  21. data/lib/gamefic/entity.rb +6 -12
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -44
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +69 -91
  26. data/lib/gamefic/plot/darkroom.rb +80 -264
  27. data/lib/gamefic/plot/host.rb +42 -48
  28. data/lib/gamefic/plot/snapshot.rb +14 -19
  29. data/lib/gamefic/query.rb +15 -18
  30. data/lib/gamefic/query/base.rb +50 -37
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/descendants.rb +2 -2
  33. data/lib/gamefic/query/external.rb +18 -14
  34. data/lib/gamefic/query/family.rb +3 -7
  35. data/lib/gamefic/query/matches.rb +75 -67
  36. data/lib/gamefic/query/parent.rb +0 -0
  37. data/lib/gamefic/query/siblings.rb +0 -0
  38. data/lib/gamefic/query/text.rb +12 -12
  39. data/lib/gamefic/query/tree.rb +17 -0
  40. data/lib/gamefic/scene.rb +0 -2
  41. data/lib/gamefic/scene/activity.rb +24 -26
  42. data/lib/gamefic/scene/base.rb +71 -10
  43. data/lib/gamefic/scene/conclusion.rb +1 -3
  44. data/lib/gamefic/scene/multiple_choice.rb +19 -14
  45. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  46. data/lib/gamefic/scene/pause.rb +8 -3
  47. data/lib/gamefic/scene/yes_or_no.rb +22 -10
  48. data/lib/gamefic/scriptable.rb +88 -0
  49. data/lib/gamefic/serialize.rb +223 -0
  50. data/lib/gamefic/subplot.rb +38 -35
  51. data/lib/gamefic/syntax.rb +15 -13
  52. data/lib/gamefic/version.rb +3 -3
  53. data/lib/gamefic/world.rb +18 -0
  54. data/lib/gamefic/world/callbacks.rb +135 -0
  55. data/lib/gamefic/world/commands.rb +184 -0
  56. data/lib/gamefic/{plot → world}/entities.rb +33 -35
  57. data/lib/gamefic/{plot → world}/playbook.rb +245 -240
  58. data/lib/gamefic/world/players.rb +37 -0
  59. data/lib/gamefic/world/scenes.rb +226 -0
  60. metadata +37 -88
  61. data/bin/gamefic +0 -9
  62. data/lib/gamefic/engine.rb +0 -7
  63. data/lib/gamefic/engine/base.rb +0 -59
  64. data/lib/gamefic/engine/tty.rb +0 -24
  65. data/lib/gamefic/grammar.rb +0 -13
  66. data/lib/gamefic/grammar/conjugator.rb +0 -20
  67. data/lib/gamefic/grammar/gender.rb +0 -11
  68. data/lib/gamefic/grammar/person.rb +0 -10
  69. data/lib/gamefic/grammar/plural.rb +0 -13
  70. data/lib/gamefic/grammar/pronouns.rb +0 -106
  71. data/lib/gamefic/grammar/tense.rb +0 -6
  72. data/lib/gamefic/grammar/verb_set.rb +0 -43
  73. data/lib/gamefic/grammar/verbs.rb +0 -26
  74. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  75. data/lib/gamefic/plot/articles.rb +0 -22
  76. data/lib/gamefic/plot/callbacks.rb +0 -126
  77. data/lib/gamefic/plot/commands.rb +0 -120
  78. data/lib/gamefic/plot/players.rb +0 -15
  79. data/lib/gamefic/plot/scenes.rb +0 -187
  80. data/lib/gamefic/plot/theater.rb +0 -73
  81. data/lib/gamefic/plot/you_mount.rb +0 -22
  82. data/lib/gamefic/scene/custom.rb +0 -9
  83. data/lib/gamefic/script.rb +0 -13
  84. data/lib/gamefic/script/base.rb +0 -42
  85. data/lib/gamefic/script/file.rb +0 -14
  86. data/lib/gamefic/script/text.rb +0 -14
  87. data/lib/gamefic/shell.rb +0 -76
  88. data/lib/gamefic/source.rb +0 -14
  89. data/lib/gamefic/source/base.rb +0 -12
  90. data/lib/gamefic/source/file.rb +0 -23
  91. data/lib/gamefic/source/text.rb +0 -16
  92. data/lib/gamefic/tester.rb +0 -19
  93. data/lib/gamefic/text.rb +0 -8
  94. data/lib/gamefic/text/ansi.rb +0 -53
  95. data/lib/gamefic/text/html.rb +0 -68
  96. data/lib/gamefic/text/html/conversions.rb +0 -250
  97. data/lib/gamefic/text/html/entities.rb +0 -9
  98. data/lib/gamefic/tty.rb +0 -10
  99. data/lib/gamefic/user.rb +0 -7
  100. data/lib/gamefic/user/base.rb +0 -29
  101. data/lib/gamefic/user/tty.rb +0 -38
data/lib/gamefic/query.rb CHANGED
@@ -1,18 +1,15 @@
1
- #require 'gamefic/keywords'
2
-
3
- module Gamefic
4
-
5
- module Query
6
- autoload :Base, 'gamefic/query/base'
7
- autoload :Children, 'gamefic/query/children'
8
- autoload :Descendants, 'gamefic/query/descendants'
9
- autoload :External, 'gamefic/query/external'
10
- autoload :Family, 'gamefic/query/family'
11
- autoload :Itself, 'gamefic/query/itself'
12
- autoload :Matches, 'gamefic/query/matches'
13
- autoload :Parent, 'gamefic/query/parent'
14
- autoload :Siblings, 'gamefic/query/siblings'
15
- autoload :Text, 'gamefic/query/text'
16
- end
17
-
18
- end
1
+ module Gamefic
2
+ module Query
3
+ autoload :Base, 'gamefic/query/base'
4
+ autoload :Children, 'gamefic/query/children'
5
+ autoload :Descendants, 'gamefic/query/descendants'
6
+ autoload :External, 'gamefic/query/external'
7
+ autoload :Family, 'gamefic/query/family'
8
+ autoload :Tree, 'gamefic/query/tree'
9
+ autoload :Itself, 'gamefic/query/itself'
10
+ autoload :Matches, 'gamefic/query/matches'
11
+ autoload :Parent, 'gamefic/query/parent'
12
+ autoload :Siblings, 'gamefic/query/siblings'
13
+ autoload :Text, 'gamefic/query/text'
14
+ end
15
+ end
@@ -9,6 +9,13 @@ module Gamefic
9
9
  @arguments = args
10
10
  end
11
11
 
12
+ # Determine whether the query allows ambiguous entity references.
13
+ # If false, actions that use this query will only be valid if the token
14
+ # passed into it resolves to a single entity. If true, actions will
15
+ # accept an array of matching entities instead.
16
+ # Queries are not ambiguous by default (ambiguous? == false).
17
+ #
18
+ # @return [Boolean]
12
19
  def ambiguous?
13
20
  false
14
21
  end
@@ -36,8 +43,8 @@ module Gamefic
36
43
  return Matches.new(drill, token, '') unless drill.length != 1
37
44
  return Matches.new([], '', token)
38
45
  end
39
- result = available.select{ |e| e.match?(token) }
40
- result = available.select{ |e| e.match?(token, fuzzy: true) } if result.empty?
46
+ result = available.select{ |e| e.specified?(token) }
47
+ result = available.select{ |e| e.specified?(token, fuzzy: true) } if result.empty?
41
48
  result.keep_if{ |e| accept? e }
42
49
  Matches.new(result, (result.empty? ? '' : token), (result.empty? ? token : ''))
43
50
  end
@@ -48,22 +55,18 @@ module Gamefic
48
55
  result.include?(object)
49
56
  end
50
57
 
58
+ # A ranking of how precise the query's arguments are.
59
+ #
60
+ # Query precision is a factor in calculating Action#rank.
61
+ #
62
+ # @return [Integer]
51
63
  def precision
52
64
  if @precision.nil?
53
65
  @precision = 1
54
66
  arguments.each { |a|
55
- #if a.kind_of?(Symbol) or a.kind_of?(Regexp)
56
- # @precision += 1
57
- #elsif a.kind_of?(Class)
58
- # @precision += (count_superclasses(a) * 100)
59
- #elsif a.kind_of?(Module)
60
- # @precision += 10
61
- #elsif a.kind_of?(Object)
62
- # @precision += 1000
63
- #end
64
- if a.kind_of?(Class)
67
+ if a.is_a?(Class)
65
68
  @precision += 100
66
- elsif a.kind_of?(Gamefic::Entity)
69
+ elsif a.is_a?(Gamefic::Entity)
67
70
  @precision += 1000
68
71
  end
69
72
  }
@@ -71,52 +74,63 @@ module Gamefic
71
74
  end
72
75
  @precision
73
76
  end
74
-
75
- def rank
76
- precision
77
- end
77
+ alias rank precision
78
78
 
79
79
  def signature
80
- "#{self.class.to_s.downcase}(#{@arguments.join(',')})"
80
+ "#{self.class.to_s.split('::').last.downcase}(#{simplify_arguments.join(', ')})"
81
81
  end
82
82
 
83
+ # Determine whether the specified entity passes the query's arguments.
84
+ #
85
+ # @return [Boolean]
83
86
  def accept?(entity)
84
87
  result = true
85
- arguments.each { |a|
86
- if a.kind_of?(Symbol)
87
- result = (entity.send(a) != false)
88
- elsif a.kind_of?(Regexp)
89
- result = (!entity.to_s.match(a).nil?)
90
- elsif a.is_a?(Module) or a.is_a?(Class)
91
- result = (entity.is_a?(a))
88
+ arguments.each do |a|
89
+ result = if a.is_a?(Symbol)
90
+ (entity.send(a) != false)
91
+ elsif a.is_a?(Regexp)
92
+ !entity.to_s.match(a).nil?
93
+ elsif a.is_a?(Module) || a.is_a?(Class)
94
+ entity.is_a?(a)
92
95
  else
93
- result = (entity == a)
96
+ (entity == a)
94
97
  end
95
98
  break if result == false
96
- }
99
+ end
97
100
  result
98
101
  end
99
102
 
100
103
  protected
101
-
102
- # Return an array of the entity's children. If the child is neighborly,
104
+
105
+ # Return an array of the entity's children. If the child is accessible,
103
106
  # recursively append its children.
104
107
  # The result will NOT include the original entity itself.
105
108
  #
106
109
  # @return [Array<Object>]
107
110
  def subquery_accessible entity
111
+ return [] if entity.nil?
108
112
  result = []
109
113
  if entity.accessible?
110
- entity.children.each { |c|
114
+ entity.children.each do |c|
111
115
  result.push c
112
116
  result.concat subquery_accessible(c)
113
- }
117
+ end
114
118
  end
115
119
  result
116
120
  end
117
121
 
118
122
  private
119
123
 
124
+ def simplify_arguments
125
+ arguments.map do |a|
126
+ if a.is_a?(Class) || a.is_a?(Object)
127
+ a.to_s.split('::').last.downcase
128
+ else
129
+ a.to_s.downcase
130
+ end
131
+ end
132
+ end
133
+
120
134
  def nested?(token)
121
135
  !token.match(NEST_REGEXP).nil?
122
136
  end
@@ -124,13 +138,12 @@ module Gamefic
124
138
  def denest(objects, token)
125
139
  parts = token.split(NEST_REGEXP)
126
140
  current = parts.pop
127
- last_result = objects.select{ |e| e.match?(current) }
128
- last_result = objects.select{ |e| e.match?(current, fuzzy: true) } if last_result.empty?
129
- result = last_result
130
- while parts.length > 0
141
+ last_result = objects.select { |e| e.specified?(current) }
142
+ last_result = objects.select { |e| e.specified?(current, fuzzy: true) } if last_result.empty?
143
+ until parts.empty?
131
144
  current = "#{parts.last} #{current}"
132
- result = last_result.select{ |e| e.match?(current) }
133
- result = last_result.select{ |e| e.match?(current, fuzzy: true) } if result.empty?
145
+ result = last_result.select { |e| e.specified?(current) }
146
+ result = last_result.select { |e| e.specified?(current, fuzzy: true) } if result.empty?
134
147
  break if result.empty?
135
148
  parts.pop
136
149
  last_result = result
File without changes
@@ -5,9 +5,9 @@ module Gamefic
5
5
  result = []
6
6
  children = super
7
7
  result.concat children
8
- children.each { |c|
8
+ children.each do |c|
9
9
  result.concat subquery_accessible(c)
10
- }
10
+ end
11
11
  result
12
12
  end
13
13
  end
@@ -1,14 +1,18 @@
1
- module Gamefic
2
- module Query
3
- class External < Base
4
- def initialize objects, *args
5
- super(*args)
6
- @objects = objects
7
- end
8
-
9
- def context_from subject
10
- @objects
11
- end
12
- end
13
- end
14
- end
1
+ module Gamefic
2
+ module Query
3
+ class External < Base
4
+ def initialize objects, *args
5
+ super(*args)
6
+ @objects = objects
7
+ end
8
+
9
+ def context_from subject
10
+ @objects
11
+ end
12
+
13
+ def accept?(entity)
14
+ @objects.include?(entity) && super(entity)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,15 +1,11 @@
1
1
  module Gamefic
2
2
  module Query
3
+ # Query to retrieve the subject's siblings and all accessible descendants.
4
+ #
3
5
  class Family < Base
4
6
  def context_from(subject)
5
7
  result = []
6
- top = subject.parent
7
- unless top.nil?
8
- #until top.parent.nil?
9
- # top = top.parent
10
- #end
11
- result.concat subquery_accessible(top)
12
- end
8
+ result.concat subquery_accessible(subject.parent)
13
9
  result.delete subject
14
10
  subject.children.each { |c|
15
11
  result.push c
@@ -1,67 +1,75 @@
1
- module Gamefic
2
- module Query
3
- class Matches
4
- attr_accessor :objects, :matching, :remaining
5
-
6
- def initialize objects, matching, remaining
7
- @objects = objects
8
- @matching = matching
9
- @remaining = remaining
10
- end
11
-
12
- def self.execute objects, description, continued: false
13
- if continued
14
- match_with_remainder objects, description
15
- else
16
- match_without_remainder objects, description
17
- end
18
- end
19
-
20
- class << self
21
- private
22
-
23
- def match_without_remainder objects, description
24
- matches = objects.select{ |e| e.match?(description) }
25
- if matches.empty?
26
- matching = ''
27
- remaining = description
28
- else
29
- matching = description
30
- remaining = ''
31
- end
32
- Matches.new(matches, matching, remaining)
33
- end
34
-
35
- def match_with_remainder objects, description
36
- matching_objects = objects
37
- matching_text = []
38
- words = description.split(Matchable::SPLIT_REGEXP)
39
- i = 0
40
- #cursor = []
41
- words.each { |w|
42
- cursor = inner_match matching_objects, words, matching_text, i, w
43
- break if cursor.empty? or (cursor & matching_objects).empty?
44
- matching_objects = (cursor & matching_objects)
45
- i += 1
46
- }
47
- objects = matching_objects
48
- matching = matching_text.uniq.join(' ')
49
- remaining = words[i..-1].join(' ')
50
- m = Matches.new(objects, matching, remaining)
51
- m
52
- end
53
-
54
- def inner_match matching_objects, words, matching_text, i, w
55
- cursor = []
56
- matching_objects.each { |o|
57
- if o.match?(words[0..i].join(' '), fuzzy: true)
58
- cursor.push o
59
- matching_text.push w
60
- end
61
- }
62
- cursor
63
- end
64
- end
65
- end
66
- end
67
- end
1
+ module Gamefic
2
+ module Query
3
+ class Matches
4
+ # The resolved tokens
5
+ # @return [Array<Object>]
6
+ attr_reader :objects
7
+
8
+ # The matching string
9
+ # @return [String]
10
+ attr_reader :matching
11
+
12
+ # The remaining (unmatched) string
13
+ # @return [String]
14
+ attr_reader :remaining
15
+
16
+ def initialize objects, matching, remaining
17
+ @objects = objects
18
+ @matching = matching
19
+ @remaining = remaining
20
+ end
21
+
22
+ def self.execute objects, description, continued: false
23
+ if continued
24
+ match_with_remainder objects, description
25
+ else
26
+ match_without_remainder objects, description
27
+ end
28
+ end
29
+
30
+ class << self
31
+ private
32
+
33
+ def match_without_remainder objects, description
34
+ matches = objects.select{ |e| e.specified?(description) }
35
+ if matches.empty?
36
+ matching = ''
37
+ remaining = description
38
+ else
39
+ matching = description
40
+ remaining = ''
41
+ end
42
+ Matches.new(matches, matching, remaining)
43
+ end
44
+
45
+ def match_with_remainder objects, description
46
+ matching_objects = objects
47
+ matching_text = []
48
+ words = description.split(Keywords::SPLIT_REGEXP)
49
+ i = 0
50
+ words.each { |w|
51
+ cursor = inner_match matching_objects, words, matching_text, i, w
52
+ break if cursor.empty? or (cursor & matching_objects).empty?
53
+ matching_objects = (cursor & matching_objects)
54
+ i += 1
55
+ }
56
+ objects = matching_objects
57
+ matching = matching_text.uniq.join(' ')
58
+ remaining = words[i..-1].join(' ')
59
+ Matches.new(objects, matching, remaining)
60
+ end
61
+
62
+ def inner_match matching_objects, words, matching_text, i, w
63
+ cursor = []
64
+ matching_objects.each { |o|
65
+ if o.specified?(words[0..i].join(' '), fuzzy: true)
66
+ cursor.push o
67
+ matching_text.push w
68
+ end
69
+ }
70
+ cursor
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
File without changes
File without changes
@@ -2,15 +2,17 @@ module Gamefic
2
2
  module Query
3
3
  class Text < Base
4
4
  def initialize *arguments
5
- arguments.each { |a|
6
- if (a.kind_of?(Symbol) or a.kind_of?(String)) and !a.to_s.end_with?('?')
5
+ arguments.each do |a|
6
+ if (a.kind_of?(Symbol) || a.kind_of?(String)) && !a.to_s.end_with?('?')
7
7
  raise ArgumentError.new("Text query arguments can only be boolean method names (:method?) or regular expressions")
8
8
  end
9
- }
9
+ end
10
10
  super
11
11
  end
12
- def resolve(subject, token, continued: false)
13
- parts = token.split(Matchable::SPLIT_REGEXP)
12
+
13
+ def resolve _subject, token, continued: false
14
+ return Matches.new([], '', token) unless accept?(token)
15
+ parts = token.split(Keywords::SPLIT_REGEXP)
14
16
  cursor = []
15
17
  matches = []
16
18
  i = 0
@@ -21,20 +23,18 @@ module Gamefic
21
23
  }
22
24
  if continued
23
25
  Matches.new([matches.join(' ')], matches.join(' '), parts[i..-1].join(' '))
26
+ elsif matches.length == parts.length
27
+ Matches.new([matches.join(' ')], matches.join(' '), '')
24
28
  else
25
- if matches.length == parts.length
26
- Matches.new([matches.join(' ')], matches.join(' '), '')
27
- else
28
- Matches.new([], '', parts.join(' '))
29
- end
29
+ Matches.new([], '', parts.join(' '))
30
30
  end
31
31
  end
32
32
 
33
- def include?(subject, token)
33
+ def include? _subject, token
34
34
  accept?(token)
35
35
  end
36
36
 
37
- def accept?(entity)
37
+ def accept? entity
38
38
  return false unless entity.kind_of?(String) and !entity.empty?
39
39
  super
40
40
  end