rubyplay_framework 1.6.4
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 +7 -0
- data/lib/interpreter/gameLexer.rb +94 -0
- data/lib/interpreter/gameLexer.rex +24 -0
- data/lib/interpreter/gameParser.racc +40 -0
- data/lib/interpreter/gameParser.rb +172 -0
- data/lib/map/dungeon.rb +93 -0
- data/lib/map/entity.rb +59 -0
- data/lib/map/map.rb +228 -0
- data/lib/map/mapExceptions.rb +20 -0
- data/lib/map/point.rb +69 -0
- data/lib/map.xsd +59 -0
- data/lib/rubyplay_framework.rb +20 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3c77d7b777232da3f802d9d9159e08562e3c105d
|
4
|
+
data.tar.gz: f3cd748d4c311ae2c5e7a16be17ad9bbe6bf33f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9a7e262d96c1601a4cb2683c3bb7cda23ba156a878dcb0fdf46782b39118f6b82978f6a1f691c8e97c0ba9688181f3bf31f5714bbdc166ec16fcb73eafbe6c8f
|
7
|
+
data.tar.gz: 8f267960659b72c214d951bf99b11bf6bfc5f74c90e4346c8fbbca990344de579ddb8fa7be3fa067c8851e5391b97eb37f41179824867baaea5b65bf5ef6c120
|
@@ -0,0 +1,94 @@
|
|
1
|
+
#--
|
2
|
+
# DO NOT MODIFY!!!!
|
3
|
+
# This file is automatically generated by rex 1.0.5
|
4
|
+
# from lexical definition file "lib/interpreter/gameLexer.rex".
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'racc/parser'
|
8
|
+
class GameLanguage < Racc::Parser
|
9
|
+
require 'strscan'
|
10
|
+
|
11
|
+
class ScanError < StandardError ; end
|
12
|
+
|
13
|
+
attr_reader :lineno
|
14
|
+
attr_reader :filename
|
15
|
+
attr_accessor :state
|
16
|
+
|
17
|
+
def scan_setup(str)
|
18
|
+
@ss = StringScanner.new(str)
|
19
|
+
@lineno = 1
|
20
|
+
@state = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def action
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
|
27
|
+
def scan_str(str)
|
28
|
+
scan_setup(str)
|
29
|
+
do_parse
|
30
|
+
end
|
31
|
+
alias :scan :scan_str
|
32
|
+
|
33
|
+
def load_file( filename )
|
34
|
+
@filename = filename
|
35
|
+
open(filename, "r") do |f|
|
36
|
+
scan_setup(f.read)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def scan_file( filename )
|
41
|
+
load_file(filename)
|
42
|
+
do_parse
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def next_token
|
47
|
+
return if @ss.eos?
|
48
|
+
|
49
|
+
# skips empty actions
|
50
|
+
until token = _next_token or @ss.eos?; end
|
51
|
+
token
|
52
|
+
end
|
53
|
+
|
54
|
+
def _next_token
|
55
|
+
text = @ss.peek(1)
|
56
|
+
@lineno += 1 if text == "\n"
|
57
|
+
token = case @state
|
58
|
+
when nil
|
59
|
+
case
|
60
|
+
when (text = @ss.scan(/[ \t]+[a-z]+|[ \t]+[A-Z][a-z]+|[ \t]+[a-z]+\d+|[ \t]+[A-Z][a-z]+\d+/))
|
61
|
+
action { [:WORD, text.gsub!(/[\ \t]+/,'')] }
|
62
|
+
|
63
|
+
when (text = @ss.scan(/[ \t]+/))
|
64
|
+
;
|
65
|
+
|
66
|
+
when (text = @ss.scan(/\d+|-\d+/))
|
67
|
+
action {[:NUMBER, text.to_i]}
|
68
|
+
|
69
|
+
when (text = @ss.scan(/\d+\.\d+|-\d+\.\d+/))
|
70
|
+
action {[:FLOAT, test.to_f]}
|
71
|
+
|
72
|
+
when (text = @ss.scan(/\w+/))
|
73
|
+
action {[:FUNCTION, text]}
|
74
|
+
|
75
|
+
else
|
76
|
+
text = @ss.string[@ss.pos .. -1]
|
77
|
+
raise ScanError, "can not match: '" + text + "'"
|
78
|
+
end # if
|
79
|
+
|
80
|
+
else
|
81
|
+
raise ScanError, "undefined state: '" + state.to_s + "'"
|
82
|
+
end # case state
|
83
|
+
token
|
84
|
+
end # def _next_token
|
85
|
+
|
86
|
+
def tokenize(code)
|
87
|
+
scan_setup(code)
|
88
|
+
tokens = []
|
89
|
+
while token = next_token
|
90
|
+
tokens << token
|
91
|
+
end
|
92
|
+
tokens
|
93
|
+
end
|
94
|
+
end # class
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class GameLanguage
|
2
|
+
macro
|
3
|
+
BLANK [\ \t]+ #only works on macro
|
4
|
+
NUMBER \d+|-\d+
|
5
|
+
FLOAT \d+\.\d+|-\d+\.\d+
|
6
|
+
WORD [\ \t]+[a-z]+|[\ \t]+[A-Z][a-z]+|[\ \t]+[a-z]+\d+|[\ \t]+[A-Z][a-z]+\d+
|
7
|
+
FUNCTION \w+
|
8
|
+
rule
|
9
|
+
{WORD} { [:WORD, text.gsub!(/[\ \t]+/,'')] }
|
10
|
+
{BLANK} #do nothing
|
11
|
+
{NUMBER} {[:NUMBER, text.to_i]}
|
12
|
+
{FLOAT} {[:FLOAT, test.to_f]}
|
13
|
+
{FUNCTION} {[:FUNCTION, text]}
|
14
|
+
|
15
|
+
inner
|
16
|
+
def tokenize(code)
|
17
|
+
scan_setup(code)
|
18
|
+
tokens = []
|
19
|
+
while token = next_token
|
20
|
+
tokens << token
|
21
|
+
end
|
22
|
+
tokens
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class GameLanguage
|
2
|
+
rule
|
3
|
+
function : FUNCTION
|
4
|
+
| FUNCTION WORD { return val }
|
5
|
+
| FUNCTION WORD WORD { return val }
|
6
|
+
| FUNCTION WORD WORD WORD { return val }
|
7
|
+
| FUNCTION WORD WORD WORD WORD { return val }
|
8
|
+
| FUNCTION NUMBER NUMBER { return val }
|
9
|
+
| FUNCTION NUMBER NUMBER NUMBER { return val }
|
10
|
+
end
|
11
|
+
|
12
|
+
---- header
|
13
|
+
require_relative 'gameLexer'
|
14
|
+
|
15
|
+
---- inner
|
16
|
+
@@functions = []
|
17
|
+
|
18
|
+
def parse(object, input)
|
19
|
+
output = scan_str(input)
|
20
|
+
if(output.kind_of?(Array))
|
21
|
+
if(@@functions.find { |f| (f[0] == output[0]) && (f.length == output.length) })
|
22
|
+
object.public_send(output[0].to_sym, *(output.drop(1)))
|
23
|
+
else
|
24
|
+
raise RuntimeError, "No such function #{output[0]}"
|
25
|
+
end
|
26
|
+
else
|
27
|
+
if(@@functions.find { |f| (f[0] == output) })
|
28
|
+
object.public_send(output.to_sym)
|
29
|
+
else
|
30
|
+
raise RuntimeError, "No such function #{output}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def intialize_functions(filename)
|
36
|
+
file = File.open(filename).read
|
37
|
+
file.each_line { |line|
|
38
|
+
@@functions << line.gsub(/\s+/, ' ').strip.split(" ")
|
39
|
+
}
|
40
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
#
|
2
|
+
# DO NOT MODIFY!!!!
|
3
|
+
# This file is automatically generated by Racc 1.4.14
|
4
|
+
# from Racc grammer file "".
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'racc/parser.rb'
|
8
|
+
|
9
|
+
require_relative 'gameLexer'
|
10
|
+
|
11
|
+
class GameLanguage < Racc::Parser
|
12
|
+
|
13
|
+
module_eval(<<'...end gameParser.racc/module_eval...', 'gameParser.racc', 16)
|
14
|
+
@@functions = []
|
15
|
+
|
16
|
+
def parse(object, input)
|
17
|
+
output = scan_str(input)
|
18
|
+
if(output.kind_of?(Array))
|
19
|
+
if(@@functions.find { |f| (f[0] == output[0]) && (f.length == output.length) })
|
20
|
+
object.public_send(output[0].to_sym, *(output.drop(1)))
|
21
|
+
else
|
22
|
+
raise RuntimeError, "No such function #{output[0]}"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
if(@@functions.find { |f| (f[0] == output) })
|
26
|
+
object.public_send(output.to_sym)
|
27
|
+
else
|
28
|
+
raise RuntimeError, "No such function #{output}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def intialize_functions(filename)
|
34
|
+
file = File.open(filename).read
|
35
|
+
file.each_line { |line|
|
36
|
+
@@functions << line.gsub(/\s+/, ' ').strip.split(" ")
|
37
|
+
}
|
38
|
+
end
|
39
|
+
...end gameParser.racc/module_eval...
|
40
|
+
##### State transition tables begin ###
|
41
|
+
|
42
|
+
racc_action_table = [
|
43
|
+
4, 5, 7, 2, 6, 3, 8, 9, 10, 11 ]
|
44
|
+
|
45
|
+
racc_action_check = [
|
46
|
+
2, 2, 4, 0, 3, 1, 5, 7, 8, 9 ]
|
47
|
+
|
48
|
+
racc_action_pointer = [
|
49
|
+
1, 5, -3, 4, -1, 2, nil, 4, 4, 6,
|
50
|
+
nil, nil ]
|
51
|
+
|
52
|
+
racc_action_default = [
|
53
|
+
-8, -8, -1, -8, -2, -8, 12, -3, -6, -4,
|
54
|
+
-7, -5 ]
|
55
|
+
|
56
|
+
racc_goto_table = [
|
57
|
+
1 ]
|
58
|
+
|
59
|
+
racc_goto_check = [
|
60
|
+
1 ]
|
61
|
+
|
62
|
+
racc_goto_pointer = [
|
63
|
+
nil, 0 ]
|
64
|
+
|
65
|
+
racc_goto_default = [
|
66
|
+
nil, nil ]
|
67
|
+
|
68
|
+
racc_reduce_table = [
|
69
|
+
0, 0, :racc_error,
|
70
|
+
1, 6, :_reduce_none,
|
71
|
+
2, 6, :_reduce_2,
|
72
|
+
3, 6, :_reduce_3,
|
73
|
+
4, 6, :_reduce_4,
|
74
|
+
5, 6, :_reduce_5,
|
75
|
+
3, 6, :_reduce_6,
|
76
|
+
4, 6, :_reduce_7 ]
|
77
|
+
|
78
|
+
racc_reduce_n = 8
|
79
|
+
|
80
|
+
racc_shift_n = 12
|
81
|
+
|
82
|
+
racc_token_table = {
|
83
|
+
false => 0,
|
84
|
+
:error => 1,
|
85
|
+
:FUNCTION => 2,
|
86
|
+
:WORD => 3,
|
87
|
+
:NUMBER => 4 }
|
88
|
+
|
89
|
+
racc_nt_base = 5
|
90
|
+
|
91
|
+
racc_use_result_var = true
|
92
|
+
|
93
|
+
Racc_arg = [
|
94
|
+
racc_action_table,
|
95
|
+
racc_action_check,
|
96
|
+
racc_action_default,
|
97
|
+
racc_action_pointer,
|
98
|
+
racc_goto_table,
|
99
|
+
racc_goto_check,
|
100
|
+
racc_goto_default,
|
101
|
+
racc_goto_pointer,
|
102
|
+
racc_nt_base,
|
103
|
+
racc_reduce_table,
|
104
|
+
racc_token_table,
|
105
|
+
racc_shift_n,
|
106
|
+
racc_reduce_n,
|
107
|
+
racc_use_result_var ]
|
108
|
+
|
109
|
+
Racc_token_to_s_table = [
|
110
|
+
"$end",
|
111
|
+
"error",
|
112
|
+
"FUNCTION",
|
113
|
+
"WORD",
|
114
|
+
"NUMBER",
|
115
|
+
"$start",
|
116
|
+
"function" ]
|
117
|
+
|
118
|
+
Racc_debug_parser = false
|
119
|
+
|
120
|
+
##### State transition tables end #####
|
121
|
+
|
122
|
+
# reduce 0 omitted
|
123
|
+
|
124
|
+
# reduce 1 omitted
|
125
|
+
|
126
|
+
module_eval(<<'.,.,', 'gameParser.racc', 3)
|
127
|
+
def _reduce_2(val, _values, result)
|
128
|
+
return val
|
129
|
+
result
|
130
|
+
end
|
131
|
+
.,.,
|
132
|
+
|
133
|
+
module_eval(<<'.,.,', 'gameParser.racc', 4)
|
134
|
+
def _reduce_3(val, _values, result)
|
135
|
+
return val
|
136
|
+
result
|
137
|
+
end
|
138
|
+
.,.,
|
139
|
+
|
140
|
+
module_eval(<<'.,.,', 'gameParser.racc', 5)
|
141
|
+
def _reduce_4(val, _values, result)
|
142
|
+
return val
|
143
|
+
result
|
144
|
+
end
|
145
|
+
.,.,
|
146
|
+
|
147
|
+
module_eval(<<'.,.,', 'gameParser.racc', 6)
|
148
|
+
def _reduce_5(val, _values, result)
|
149
|
+
return val
|
150
|
+
result
|
151
|
+
end
|
152
|
+
.,.,
|
153
|
+
|
154
|
+
module_eval(<<'.,.,', 'gameParser.racc', 7)
|
155
|
+
def _reduce_6(val, _values, result)
|
156
|
+
return val
|
157
|
+
result
|
158
|
+
end
|
159
|
+
.,.,
|
160
|
+
|
161
|
+
module_eval(<<'.,.,', 'gameParser.racc', 8)
|
162
|
+
def _reduce_7(val, _values, result)
|
163
|
+
return val
|
164
|
+
result
|
165
|
+
end
|
166
|
+
.,.,
|
167
|
+
|
168
|
+
def _reduce_none(val, _values, result)
|
169
|
+
val[0]
|
170
|
+
end
|
171
|
+
|
172
|
+
end # class GameLanguage
|
data/lib/map/dungeon.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'map/entity'
|
3
|
+
|
4
|
+
module MapDungeon
|
5
|
+
|
6
|
+
class Dungeon
|
7
|
+
|
8
|
+
def initialize(name = "", description = "")
|
9
|
+
@name, @description = name, description
|
10
|
+
@entities = Hash.new()
|
11
|
+
end
|
12
|
+
|
13
|
+
#Attributes access
|
14
|
+
attr_accessor :name, :description
|
15
|
+
|
16
|
+
#Add entities to the hash
|
17
|
+
def add_entity(entity, type)
|
18
|
+
if(@entities[type] == nil)
|
19
|
+
@entities[type] = [entity]
|
20
|
+
else
|
21
|
+
@entities[type] << entity
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#Remove the entity from the array
|
26
|
+
def remove_entity(entity, type)
|
27
|
+
@entities[type].delete(entity)
|
28
|
+
end
|
29
|
+
|
30
|
+
#Returns true if the entity exists
|
31
|
+
def has_entity?(entity, type)
|
32
|
+
@entities[type].find { |e| entity.eql?(e) }
|
33
|
+
end
|
34
|
+
|
35
|
+
#Entity iterator
|
36
|
+
def each_entity()
|
37
|
+
@entities.each { |key, value|
|
38
|
+
value.each { |entity| yield entity }
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
#Concrete entity iterator
|
43
|
+
def each_type_entity(type)
|
44
|
+
@entities[type].each { |entity| yield entity }
|
45
|
+
end
|
46
|
+
|
47
|
+
#To string
|
48
|
+
def to_s()
|
49
|
+
"#{@name}: #{@description}"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
#Dungeon builder class
|
55
|
+
class DungeonXPathBuilder
|
56
|
+
include MapEntity
|
57
|
+
|
58
|
+
def initialize()
|
59
|
+
@dungeon = Dungeon.new()
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_reader :dungeon
|
63
|
+
|
64
|
+
def build_XML_dungeon(name, description, node = nil, entityBuilder = "")
|
65
|
+
add_name(name)
|
66
|
+
add_description(description)
|
67
|
+
if(node != nil)
|
68
|
+
node.each() { |entity|
|
69
|
+
args = []
|
70
|
+
if(entityBuilder.length > 0)
|
71
|
+
builder = Object::const_get(entityBuilder).new()
|
72
|
+
else
|
73
|
+
builder = EntityXPathBuilder.new()
|
74
|
+
end
|
75
|
+
nodeSet = entity.xpath("*")
|
76
|
+
nodeSet.each { |n| args << n.content }
|
77
|
+
builder.build_XML_entity(*(args))
|
78
|
+
@dungeon.add_entity(builder.entity, builder.entity.type)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_name(name)
|
84
|
+
@dungeon.name = name
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_description(description)
|
88
|
+
@dungeon.description = description
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/map/entity.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module MapEntity
|
4
|
+
|
5
|
+
class Entity
|
6
|
+
|
7
|
+
def initialize(type = "", nametag = "")
|
8
|
+
@type, @nametag = type, nametag
|
9
|
+
end
|
10
|
+
|
11
|
+
#Attributes access
|
12
|
+
attr_accessor :type, :nametag
|
13
|
+
|
14
|
+
#Redefinition of equal operator
|
15
|
+
def ==(other)
|
16
|
+
(self.class ==other.class) && (self.state == other.state)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s()
|
20
|
+
"Type: #{@type}\nName: #{@nametag}"
|
21
|
+
end
|
22
|
+
|
23
|
+
#Use Entity == for eql? method
|
24
|
+
alias_method :eql?, :==
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def state
|
29
|
+
[type, nametag]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
#Entity builder class
|
35
|
+
class EntityXPathBuilder
|
36
|
+
|
37
|
+
def initialize()
|
38
|
+
@entity = Entity.new()
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :entity
|
42
|
+
|
43
|
+
#Builds the entity of a dungeon
|
44
|
+
def build_XML_entity(type, nametag)
|
45
|
+
add_type(type)
|
46
|
+
add_nametag(nametag)
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_type(type)
|
50
|
+
@entity.type = type
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_nametag(nametag)
|
54
|
+
@entity.nametag = nametag
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/lib/map/map.rb
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'map/point'
|
3
|
+
require 'map/dungeon'
|
4
|
+
require 'map/entity'
|
5
|
+
require 'map/mapExceptions'
|
6
|
+
|
7
|
+
class Map
|
8
|
+
|
9
|
+
include MapPoint, MapDungeon, MapEntity
|
10
|
+
|
11
|
+
def initialize()
|
12
|
+
@map_nodes = Hash.new()
|
13
|
+
@adjacencies = Hash.new()
|
14
|
+
end
|
15
|
+
|
16
|
+
#Attribute access
|
17
|
+
attr_reader :map_nodes, :adjacencies
|
18
|
+
|
19
|
+
#Builds the map structure from XML specification
|
20
|
+
def build_map(filePath, pointBuilder = "", dungeonBuilder = "", entityBuilder = "")
|
21
|
+
if(filePath.include?('http'))
|
22
|
+
doc = parse_from_URL(filePath)
|
23
|
+
else
|
24
|
+
doc = parse_from_XML(filePath)
|
25
|
+
end
|
26
|
+
doc.remove_namespaces!
|
27
|
+
doc.xpath("//node").each() { |node|
|
28
|
+
coords = node.xpath("point//*")
|
29
|
+
point = build_point(coords, pointBuilder)
|
30
|
+
dungeonElements = node.xpath("dungeon//*[not(name()='entity') and not(ancestor-or-self::entity)]")
|
31
|
+
dungeon = build_dungeon(node, dungeonElements, dungeonBuilder, entityBuilder)
|
32
|
+
@map_nodes[point] = dungeon
|
33
|
+
build_adjacent(point, node, pointBuilder)
|
34
|
+
}
|
35
|
+
p = @map_nodes.keys().sample() #Get random key
|
36
|
+
if(@map_nodes.length() != check_connectivity(p))
|
37
|
+
raise MapExceptions::MalformedMapException.new()
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#Puts move the entity from one point to an adjacent
|
42
|
+
def movement(fromPoint, toPoint, entity)
|
43
|
+
if(is_adjacent?(fromPoint, toPoint))
|
44
|
+
remove_entity(fromPoint, entity)
|
45
|
+
add_entity(toPoint, entity)
|
46
|
+
else
|
47
|
+
raise MapExceptions::NotAdjacentException.new(fromPoint,toPoint)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#Allows to make a movement without creating a explicit Point object
|
52
|
+
def move(point, entity, movX = 0, movY = 0, movZ = 0)
|
53
|
+
toPoint = Point.new(point.x()+movX, point.y()+movY, point.z()+movZ)
|
54
|
+
movement(point, toPoint, entity)
|
55
|
+
end
|
56
|
+
|
57
|
+
#Check if point2 is in the adjacent list of point1
|
58
|
+
def is_adjacent?(point1, point2)
|
59
|
+
@adjacencies[point1].find { |p| point2 == p }
|
60
|
+
end
|
61
|
+
|
62
|
+
#Returns the node with that coordinates
|
63
|
+
def get_node(point)
|
64
|
+
@map_nodes[point]
|
65
|
+
end
|
66
|
+
|
67
|
+
#Iterates over the adjacency hash
|
68
|
+
def each_adjacency()
|
69
|
+
@adjacencies.each { |key, value|
|
70
|
+
value.each { |adjacent| yield adjacent }
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
#Allows user to add a new node after building the map
|
75
|
+
def add_new_node(point, node, adjacencies = [])
|
76
|
+
@map_nodes[point] = node
|
77
|
+
@adjacencies[point] = adjacencies
|
78
|
+
adjacencies.each do |p|
|
79
|
+
if((@adjacencies[p] != nil) && !(is_adjacent?(p, point)))
|
80
|
+
@adjacencies[p] << point
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
#Allows user to delete a node of the map
|
86
|
+
def delete_node(point)
|
87
|
+
@map_nodes.delete(point)
|
88
|
+
@adjacencies.delete(point)
|
89
|
+
@adjacencies.each { |k, v| @adjacencies[k].delete(point) }
|
90
|
+
end
|
91
|
+
|
92
|
+
#Adds a new adjacent to point only if it belongs to the map
|
93
|
+
def add_new_adjacent(point, newAdjacent)
|
94
|
+
if((@map_nodes[point] != nil) && !(is_adjacent?(point, newAdjacent)) && (@map_nodes[newAdjacent] != nil))
|
95
|
+
@adjacencies[point] << newAdjacent
|
96
|
+
@adjacencies[newAdjacent] << point
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
#Delete a single element from the adjacent list of point
|
101
|
+
def delete_adjacent(point, adjacent)
|
102
|
+
@adjacencies[point].delete(adjacent)
|
103
|
+
@adjacencies[adjacent].delete(point)
|
104
|
+
end
|
105
|
+
|
106
|
+
#Adds a new entity to the node
|
107
|
+
def add_entity(point, entity)
|
108
|
+
@map_nodes[point].add_entity(entity, entity.type())
|
109
|
+
end
|
110
|
+
|
111
|
+
#Removes an entity
|
112
|
+
def remove_entity(point, entity)
|
113
|
+
@map_nodes[point].remove_entity(entity, entity.type())
|
114
|
+
end
|
115
|
+
|
116
|
+
#Checks if there is an entity on the node
|
117
|
+
def has_entity?(point, entity)
|
118
|
+
@map_nodes[point].has_entity?(entity, entity.type())
|
119
|
+
end
|
120
|
+
|
121
|
+
#Checks if the map generated is fully connected
|
122
|
+
def check_connectivity(point, visited = Hash.new())
|
123
|
+
visited[point] = 1
|
124
|
+
@adjacencies[point].each { |nextPoint|
|
125
|
+
if(!visited.has_key?(nextPoint))
|
126
|
+
check_connectivity(nextPoint, visited)
|
127
|
+
end
|
128
|
+
}
|
129
|
+
return visited.length()
|
130
|
+
end
|
131
|
+
|
132
|
+
#Shortest path from initial to destination
|
133
|
+
def shortest_path(initial, destination)
|
134
|
+
distance = Hash.new()
|
135
|
+
visited = Hash.new()
|
136
|
+
@adjacencies.each_key { |point|
|
137
|
+
if(is_adjacent?(initial, point))
|
138
|
+
distance[point] = 1
|
139
|
+
else
|
140
|
+
distance[point] = (2**(0.size * 8 - 2) - 1)
|
141
|
+
end
|
142
|
+
}
|
143
|
+
visited[initial] = true
|
144
|
+
distance.delete(initial)
|
145
|
+
until(visited.length == @adjacencies.length) do
|
146
|
+
if((value = distance.values.min) != nil)
|
147
|
+
if(!(visited.has_key?(distance.key(value))))
|
148
|
+
nextNode = distance.key(value)
|
149
|
+
visited[nextNode] = true
|
150
|
+
if(nextNode == destination)
|
151
|
+
break;
|
152
|
+
end
|
153
|
+
@adjacencies[nextNode].each { |a|
|
154
|
+
alt = distance[nextNode] + 1
|
155
|
+
if(!(visited.has_key?(a)) && alt < distance[a])
|
156
|
+
distance[a] = alt
|
157
|
+
end
|
158
|
+
}
|
159
|
+
distance.delete(nextNode)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
return distance[destination]
|
164
|
+
end
|
165
|
+
|
166
|
+
protected
|
167
|
+
|
168
|
+
#Parse XML document and returns a String
|
169
|
+
def parse_from_XML(file)
|
170
|
+
File.open(file) { |f| Nokogiri::XML(f) }
|
171
|
+
end
|
172
|
+
|
173
|
+
#Parse XML from given url
|
174
|
+
def parse_from_URL(url)
|
175
|
+
Nokogiri::XML(open(url))
|
176
|
+
end
|
177
|
+
|
178
|
+
#Builds the point of a node
|
179
|
+
def build_point(nodeSet, pointBuilder = "")
|
180
|
+
args = []
|
181
|
+
if(pointBuilder.length > 0)
|
182
|
+
builder = Object::const_get(pointBuilder).new()
|
183
|
+
else
|
184
|
+
builder = PointXPathBuilder.new()
|
185
|
+
end
|
186
|
+
nodeSet.each { |node| args << node.content }
|
187
|
+
builder.build_XML_point(*(args))
|
188
|
+
builder.point()
|
189
|
+
end
|
190
|
+
|
191
|
+
#Builds the dungeon object of a node
|
192
|
+
def build_dungeon(node, nodeSet, dungeonBuilder = "", entityBuilder = "")
|
193
|
+
args = []
|
194
|
+
if(dungeonBuilder.length > 0)
|
195
|
+
builder = Object::const_get(dungeonBuilder).new()
|
196
|
+
else
|
197
|
+
builder = DungeonXPathBuilder.new()
|
198
|
+
end
|
199
|
+
nodeSet.each { |n| args << n.content }
|
200
|
+
if(!(node.xpath("dungeon//entity").empty?))
|
201
|
+
args << node.xpath("dungeon//entity")
|
202
|
+
args << entityBuilder
|
203
|
+
builder.build_XML_dungeon(*(args))
|
204
|
+
else
|
205
|
+
builder.build_XML_dungeon(*(args))
|
206
|
+
end
|
207
|
+
builder.dungeon()
|
208
|
+
end
|
209
|
+
|
210
|
+
#Builds the adjacent list of a node
|
211
|
+
def build_adjacent(point, node, pointBuilder = nil)
|
212
|
+
node.xpath("adjacent//point").each() { |pnt|
|
213
|
+
coords = pnt.xpath(".//*")
|
214
|
+
p = build_point(coords, pointBuilder)
|
215
|
+
if(@adjacencies[point] == nil)
|
216
|
+
@adjacencies[point] = [p]
|
217
|
+
elsif(!(is_adjacent?(point, p)))
|
218
|
+
@adjacencies[point] << p
|
219
|
+
end
|
220
|
+
if(@adjacencies[p] == nil)
|
221
|
+
@adjacencies[p] = [point]
|
222
|
+
elsif(!(is_adjacent?(p, point)))
|
223
|
+
@adjacencies[p] << point
|
224
|
+
end
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module MapExceptions
|
2
|
+
|
3
|
+
class MalformedMapException < StandardError
|
4
|
+
|
5
|
+
def initialize(msg="The given map is not fully connected")
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class NotAdjacentException < StandardError
|
12
|
+
|
13
|
+
def initialize(pointIni, pointFin)
|
14
|
+
msg = "Point #{pointFin} is not an adjacent point of #{pointIni}"
|
15
|
+
super(msg)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/map/point.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module MapPoint
|
4
|
+
|
5
|
+
class Point
|
6
|
+
|
7
|
+
def initialize(x = 0, y = 0, z = 0)
|
8
|
+
@x, @y, @z = x, y, z
|
9
|
+
end
|
10
|
+
|
11
|
+
#Attributes access
|
12
|
+
attr_accessor :x, :y, :z
|
13
|
+
|
14
|
+
#Redefinition of equal operator
|
15
|
+
def ==(other)
|
16
|
+
(self.class ==other.class) && (self.state == other.state)
|
17
|
+
end
|
18
|
+
|
19
|
+
#Use Point == for eql? method
|
20
|
+
alias_method :eql?, :==
|
21
|
+
|
22
|
+
#It allows to use Point as a hash key
|
23
|
+
def hash
|
24
|
+
state.hash
|
25
|
+
end
|
26
|
+
|
27
|
+
#To string
|
28
|
+
def to_s
|
29
|
+
"(#{@x},#{@y},#{@z})"
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def state
|
35
|
+
[x, y, z]
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
#Point builder class
|
41
|
+
class PointXPathBuilder
|
42
|
+
|
43
|
+
def initialize()
|
44
|
+
@point = Point.new()
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :point
|
48
|
+
|
49
|
+
#Builds the point of a node
|
50
|
+
def build_XML_point(x, y, z)
|
51
|
+
add_x(x.to_i)
|
52
|
+
add_y(y.to_i)
|
53
|
+
add_z(z.to_i)
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_x(x)
|
57
|
+
@point.x = x
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_y(y)
|
61
|
+
@point.y = y
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_z(z)
|
65
|
+
@point.z = z
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/lib/map.xsd
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xs:schema targetNamespace="http://www.w3schools.com/map.xsd"
|
3
|
+
elementFormDefault="qualified"
|
4
|
+
xmlns="http://www.w3schools.com/map.xsd"
|
5
|
+
xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
6
|
+
|
7
|
+
<xs:complexType name="pointType">
|
8
|
+
<xs:sequence>
|
9
|
+
<xs:element name="x" type="xs:integer"/>
|
10
|
+
<xs:element name="y" type="xs:integer"/>
|
11
|
+
<xs:element name="z" type="xs:integer"/>
|
12
|
+
</xs:sequence>
|
13
|
+
</xs:complexType>
|
14
|
+
|
15
|
+
<xs:complexType name="entityType">
|
16
|
+
<xs:sequence>
|
17
|
+
<xs:element name="type" minOccurs="1" maxOccurs="1">
|
18
|
+
<xs:simpleType>
|
19
|
+
<xs:restriction base="xs:string">
|
20
|
+
<xs:pattern value="[A-Z]([a-z])*"/>
|
21
|
+
</xs:restriction>
|
22
|
+
</xs:simpleType>
|
23
|
+
</xs:element>
|
24
|
+
<xs:element name="nametag" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
25
|
+
<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
|
26
|
+
</xs:sequence>
|
27
|
+
</xs:complexType>
|
28
|
+
|
29
|
+
<xs:complexType name="dungeonType">
|
30
|
+
<xs:sequence>
|
31
|
+
<xs:element name="name" type="xs:string"/>
|
32
|
+
<xs:element name="description" type="xs:string"/>
|
33
|
+
<xs:element name="entity" type="entityType" minOccurs="0" maxOccurs="unbounded"/>
|
34
|
+
</xs:sequence>
|
35
|
+
</xs:complexType>
|
36
|
+
|
37
|
+
<xs:element name="map">
|
38
|
+
<xs:complexType>
|
39
|
+
<xs:sequence>
|
40
|
+
<xs:element name="node" minOccurs="2" maxOccurs="unbounded">
|
41
|
+
<xs:complexType>
|
42
|
+
<xs:sequence>
|
43
|
+
<xs:element name="point" type="pointType" minOccurs="1" maxOccurs="1"/>
|
44
|
+
<xs:element name="dungeon" type="dungeonType" minOccurs="1" maxOccurs="1"/>
|
45
|
+
<xs:element name="adjacent" minOccurs="1" maxOccurs="1">
|
46
|
+
<xs:complexType>
|
47
|
+
<xs:sequence>
|
48
|
+
<xs:element name="point" type="pointType" minOccurs="1" maxOccurs="unbounded"/>
|
49
|
+
</xs:sequence>
|
50
|
+
</xs:complexType>
|
51
|
+
</xs:element>
|
52
|
+
</xs:sequence>
|
53
|
+
</xs:complexType>
|
54
|
+
</xs:element>
|
55
|
+
</xs:sequence>
|
56
|
+
</xs:complexType>
|
57
|
+
</xs:element>
|
58
|
+
|
59
|
+
</xs:schema>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'map/map'
|
2
|
+
require 'map/dungeon'
|
3
|
+
require 'map/entity'
|
4
|
+
require 'map/point'
|
5
|
+
require 'map/mapExceptions'
|
6
|
+
require 'interpreter/gameLexer'
|
7
|
+
require 'interpreter/gameParser'
|
8
|
+
|
9
|
+
module RubyplayFramework
|
10
|
+
include MapPoint, MapDungeon, MapEntity
|
11
|
+
|
12
|
+
def init_Map()
|
13
|
+
Map.new()
|
14
|
+
end
|
15
|
+
|
16
|
+
def init_Interpreter()
|
17
|
+
GameLanguage.new()
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubyplay_framework
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.6.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alvaro Pavon Alvarado
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.6.8
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.6'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.6.8
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rexical
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.0'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.0.5
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.0'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.0.5
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: racc
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '1.4'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.4.14
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.4'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.4.14
|
73
|
+
description: A library to ease development of roleplay games for textual enviroments
|
74
|
+
email: alvaro.pavon.alvarado@gmail.com
|
75
|
+
executables: []
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- lib/interpreter/gameLexer.rb
|
80
|
+
- lib/interpreter/gameLexer.rex
|
81
|
+
- lib/interpreter/gameParser.racc
|
82
|
+
- lib/interpreter/gameParser.rb
|
83
|
+
- lib/map.xsd
|
84
|
+
- lib/map/dungeon.rb
|
85
|
+
- lib/map/entity.rb
|
86
|
+
- lib/map/map.rb
|
87
|
+
- lib/map/mapExceptions.rb
|
88
|
+
- lib/map/point.rb
|
89
|
+
- lib/rubyplay_framework.rb
|
90
|
+
homepage: https://github.com/Alvaro95pa/TFG_Alvaro_Pavon_URJC/tree/master
|
91
|
+
licenses:
|
92
|
+
- Apache-2.0
|
93
|
+
metadata: {}
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 2.6.8
|
111
|
+
signing_key:
|
112
|
+
specification_version: 4
|
113
|
+
summary: Roleplay games development helper library
|
114
|
+
test_files: []
|