rachinations 0.0.3 → 0.0.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 +4 -4
- data/Gemfile +1 -16
- data/Gemfile.lock +2 -0
- data/lib/rachinations/domain/diagrams/diagram.rb +46 -14
- data/lib/rachinations/domain/edges/edge.rb +34 -20
- data/lib/rachinations/domain/modules/common/hash_init.rb +2 -1
- data/lib/rachinations/domain/modules/common/invariant.rb +2 -2
- data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +22 -0
- data/lib/rachinations/domain/nodes/converter.rb +5 -5
- data/lib/rachinations/domain/nodes/gate.rb +77 -0
- data/lib/rachinations/domain/nodes/node.rb +69 -36
- data/lib/rachinations/domain/nodes/pool.rb +121 -74
- data/lib/rachinations/domain/nodes/resourceful_node.rb +0 -1
- data/lib/rachinations/domain/nodes/sink.rb +3 -0
- data/lib/rachinations/domain/nodes/source.rb +3 -2
- data/lib/rachinations/domain/resource_bag.rb +3 -4
- data/lib/rachinations/dsl/bad_dsl.rb +2 -0
- data/lib/rachinations/dsl/bootstrap.rb +59 -0
- data/lib/rachinations/dsl/diagram_shorthand_methods.rb +107 -0
- data/lib/rachinations/dsl/helpers/parser.rb +170 -0
- data/lib/rachinations/extras/constant_hash.rb +25 -0
- data/lib/rachinations/extras/fifo.rb +25 -19
- data/lib/rachinations/helpers/edge_helper.rb +40 -0
- data/lib/rachinations/utils/string_helper.rb +7 -0
- data/lib/rachinations/version.rb +1 -1
- data/lib/rachinations.rb +13 -5
- data/rachinations.gemspec +3 -2
- data/testing/simulations/modelo1.rb +1 -1
- data/testing/simulations/sequencial.rb +1 -1
- data/testing/simulations/sobonito.rb +1 -1
- data/testing/simulations/sobonitowhile.rb +1 -1
- data/testing/simulations/whatIwish1.rb +2 -2
- data/testing/spec/canon/conditions_spec.rb +3 -4
- data/testing/spec/converter_spec.rb +3 -4
- data/testing/spec/diagram_spec.rb +293 -238
- data/testing/spec/edge_spec.rb +28 -14
- data/testing/spec/gate_spec.rb +34 -0
- data/testing/spec/pool_spec.rb +8 -10
- data/testing/spec/release1/dsl_spec.rb +204 -0
- data/testing/spec/spec_helper.rb +1 -0
- data/testing/spec/xexeo_spec.rb +39 -40
- metadata +30 -8
- data/lib/rachinations/domain/edges/random_edge.rb +0 -4
- data/lib/rachinations/dsl/dsl.rb +0 -63
- data/testing/spec/canon/trigger_spec.rb +0 -128
@@ -0,0 +1,170 @@
|
|
1
|
+
require_relative '../../domain/modules/common/refiners/proc_convenience_methods'
|
2
|
+
require_relative '../../extras/constant_hash'
|
3
|
+
|
4
|
+
module DSL
|
5
|
+
|
6
|
+
# This module helps with parsing arguments used in the DiagramShorthandMethods
|
7
|
+
module Parser
|
8
|
+
using ProcConvenienceMethods
|
9
|
+
|
10
|
+
ConstantHash = ::Extras::ConstantHash
|
11
|
+
|
12
|
+
# these patterns (*_EXPR) define what each argument should look like
|
13
|
+
|
14
|
+
IDENTIFIER_EXPR = proc { |arg| arg.is_a?(String) && valid_name?(arg) }
|
15
|
+
|
16
|
+
INITIAL_VALUE_EXPR = proc { |arg| arg.is_a? Fixnum }
|
17
|
+
|
18
|
+
MODE_EXPR = proc { |arg| [:pull_any, :pull_all, :push_any, :push_all].include? arg }
|
19
|
+
|
20
|
+
ACTIVATION_EXPR = proc { |arg| [:automatic, :passive, :start].include? arg }
|
21
|
+
|
22
|
+
PROC_EXPR = proc { |arg| arg.is_a? Proc }
|
23
|
+
|
24
|
+
# returns true for integers or floats
|
25
|
+
LABEL_EXPR = proc { |arg| arg.is_a? Numeric }
|
26
|
+
|
27
|
+
# Parse an arbitrary list of arguments and returns a well-formed
|
28
|
+
# Hash which can then be used as argument to method add_node!
|
29
|
+
# @param [Array] arguments an array of arguments.
|
30
|
+
# @return [Hash] a well-formed hash
|
31
|
+
def self.parse_arguments(arguments)
|
32
|
+
arguments.inject(ConstantHash.new) do |accumulator, arg|
|
33
|
+
|
34
|
+
# named parameters are expressed are hashes
|
35
|
+
# and all arguments can (also) be passed as named parameters
|
36
|
+
if arg.is_a? Hash
|
37
|
+
if arg.has_key? :condition
|
38
|
+
if PROC_EXPR.match? arg[:condition]
|
39
|
+
accumulator[:condition] = arg[:condition]
|
40
|
+
else
|
41
|
+
raise BadDSL.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if arg.has_key? :triggered_by
|
46
|
+
if IDENTIFIER_EXPR.match? arg[:triggered_by]
|
47
|
+
accumulator[:triggered_by] = arg[:triggered_by]
|
48
|
+
else
|
49
|
+
raise BadDSL.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if arg.has_key? :activation
|
54
|
+
if ACTIVATION_EXPR.match? arg[:activation]
|
55
|
+
accumulator[:activation] = arg[:activation]
|
56
|
+
else
|
57
|
+
raise BadDSL.new
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
if arg.has_key? :initial_value
|
62
|
+
if INITIAL_VALUE_EXPR.match? arg[:initial_value]
|
63
|
+
accumulator[:initial_value] = arg[:initial_value]
|
64
|
+
else
|
65
|
+
raise BadDSL.new
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if arg.has_key? :mode
|
70
|
+
if MODE_EXPR.match? arg[:mode]
|
71
|
+
accumulator[:mode] = arg[:mode]
|
72
|
+
else
|
73
|
+
raise BadDSL.new
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
else
|
78
|
+
if IDENTIFIER_EXPR.match?(arg) # a node's name, if present, is always the first argument
|
79
|
+
accumulator[:name] = arg
|
80
|
+
elsif INITIAL_VALUE_EXPR.match?(arg)
|
81
|
+
accumulator[:initial_value] = arg
|
82
|
+
elsif MODE_EXPR.match?(arg)
|
83
|
+
accumulator[:mode] = arg
|
84
|
+
elsif ACTIVATION_EXPR.match?(arg)
|
85
|
+
accumulator[:activation] = arg
|
86
|
+
else
|
87
|
+
raise BadDSL, "Argument #{arg} doesn't fit any known signature"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
# passing the accumulator onto the next iteration
|
91
|
+
accumulator
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.parse_gate_arguments(arguments)
|
96
|
+
|
97
|
+
# mode is always pull_any so user can't choose it
|
98
|
+
# initial_value makes no sense for gates either
|
99
|
+
arguments.inject(ConstantHash.new) do |accumulator, arg|
|
100
|
+
if arg.is_a? Hash
|
101
|
+
if arg.has_key? :condition
|
102
|
+
accumulator[:condition] = arg[:condition] if PROC_EXPR.match?(arg[:condition])
|
103
|
+
elsif arg.has_key? :triggered_by
|
104
|
+
accumulator[:triggered_by] = arg[:triggered_by] if IDENTIFIER_EXPR.match?(arg[:triggered_by])
|
105
|
+
else
|
106
|
+
ra
|
107
|
+
end
|
108
|
+
else
|
109
|
+
if IDENTIFIER_EXPR.match?(arg)
|
110
|
+
accumulator[:name] = arg
|
111
|
+
elsif ACTIVATION_EXPR.match?(arg)
|
112
|
+
accumulator[:activation] = arg
|
113
|
+
else
|
114
|
+
raise BadDSL, "Argument #{arg} doesn't fit any known signature"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
accumulator
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.parse_edge_arguments(arguments)
|
123
|
+
|
124
|
+
arguments.inject(ConstantHash.new) do |accumulator, arg|
|
125
|
+
if IDENTIFIER_EXPR.match?(arg)
|
126
|
+
accumulator[:name] = arg
|
127
|
+
elsif LABEL_EXPR.match?(arg)
|
128
|
+
accumulator[:label]=arg
|
129
|
+
elsif arg.is_a? Hash
|
130
|
+
|
131
|
+
if arg.has_key? :from
|
132
|
+
accumulator[:from] = arg[:from]
|
133
|
+
end
|
134
|
+
if arg.has_key? :to
|
135
|
+
accumulator[:to] = arg[:to]
|
136
|
+
end
|
137
|
+
if arg.has_key? :label
|
138
|
+
accumulator[:label] = arg[:label]
|
139
|
+
end
|
140
|
+
else
|
141
|
+
raise BadDSL, "Argument #{arg} doesn't fit any known signature."
|
142
|
+
end
|
143
|
+
accumulator
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
# Used to validate that a string is a valid name for diagram components
|
149
|
+
#
|
150
|
+
# @param [String] text the name we want to validate
|
151
|
+
# @raise [BadDSL] if given text is not a valid name
|
152
|
+
# @return [String] the text itself, but only if it's valid. Otherwise
|
153
|
+
# an exception will be raised.
|
154
|
+
def self.validate_name!(text)
|
155
|
+
if StringHelper.valid_ruby_variable_name?(text)
|
156
|
+
text
|
157
|
+
else
|
158
|
+
raise BadDSL, "Invalid name: '#{text}'"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# @param [String] text the name we want to validate
|
163
|
+
# @return [Boolean] whether or not given text is a valid name for diagram elements
|
164
|
+
def self.valid_name?(text)
|
165
|
+
StringHelper.valid_ruby_variable_name?(text)
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Extras
|
2
|
+
|
3
|
+
# A normal hash except that it raises RuntimeErrors
|
4
|
+
# if a key gets assigned twice
|
5
|
+
class ConstantHash < Hash
|
6
|
+
|
7
|
+
def []=(key, value)
|
8
|
+
|
9
|
+
if has_key?(key)
|
10
|
+
raise RuntimeError, "key :#{key} has already been set"
|
11
|
+
end
|
12
|
+
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def store(key, value)
|
17
|
+
if has_key?(key)
|
18
|
+
raise RuntimeError, "key :#{key} has already been set"
|
19
|
+
end
|
20
|
+
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -1,27 +1,33 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
|
-
|
4
|
-
extend Forwardable
|
3
|
+
module Extras
|
5
4
|
|
6
|
-
|
5
|
+
# A class that provides a queue-like (first-in, first-out)
|
6
|
+
# structure to clients
|
7
|
+
class Fifo
|
8
|
+
extend Forwardable
|
7
9
|
|
8
|
-
|
9
|
-
@store = Array.new
|
10
|
-
end
|
10
|
+
def_delegators :@store, :length
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
# @return [Fifo] The queue itself.
|
16
|
-
def put!(obj)
|
17
|
-
@store.push(obj)
|
18
|
-
self
|
19
|
-
end
|
12
|
+
def initialize
|
13
|
+
@store = Array.new
|
14
|
+
end
|
20
15
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
# Puts an object into the queue
|
17
|
+
#
|
18
|
+
# @param [Object] obj
|
19
|
+
# @return [Fifo] The queue itself.
|
20
|
+
def put!(obj)
|
21
|
+
@store.push(obj)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Takes the least recently added element.
|
26
|
+
#
|
27
|
+
# @return [Object] whatever was stored in this queue.
|
28
|
+
def take!
|
29
|
+
@store.shift
|
30
|
+
end
|
26
31
|
end
|
32
|
+
|
27
33
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Helpers
|
2
|
+
module EdgeHelper
|
3
|
+
|
4
|
+
# Returns true if all edges passed as arguments can execute a push
|
5
|
+
# when called in the same round. The difference between calling this
|
6
|
+
# method and calling Edge#test_ping? on each edge is that this method
|
7
|
+
# knows that each edge removes at least one Resource when it performs
|
8
|
+
# a push.
|
9
|
+
# @param [Array<Edge>] edges the edges
|
10
|
+
# @param [Boolean] require_all whether to require that the maximum amount
|
11
|
+
# of resources supported by each edge (i.e. its label) be available in
|
12
|
+
# order to succeed.
|
13
|
+
# @return [Boolean]
|
14
|
+
def self.all_can_push?(edges, require_all: false)
|
15
|
+
initial = {
|
16
|
+
partial_success: true,
|
17
|
+
number_of_resources: 0
|
18
|
+
}
|
19
|
+
|
20
|
+
raise NotImplementedError, 'only require_all is implemented so far' unless require_all
|
21
|
+
|
22
|
+
final = edges.inject(initial) do |accumulator, edge|
|
23
|
+
accumulator[:number_of_resources] += edge.label
|
24
|
+
|
25
|
+
resources_available = edge.from.resource_count(expr: edge.push_expression)
|
26
|
+
|
27
|
+
if resources_available >= accumulator[:number_of_resources]
|
28
|
+
accumulator[:partial_success] &&= true
|
29
|
+
else
|
30
|
+
accumulator[:partial_success] &&= false
|
31
|
+
end
|
32
|
+
accumulator
|
33
|
+
end
|
34
|
+
|
35
|
+
final[:partial_success]
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
data/lib/rachinations/version.rb
CHANGED
data/lib/rachinations.rb
CHANGED
@@ -2,23 +2,26 @@ lib = File.expand_path('../lib', __FILE__)
|
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
require 'rachinations/version'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
5
|
require 'rachinations/domain/modules/common/refiners/proc_convenience_methods'
|
6
|
+
require 'rachinations/domain/modules/common/refiners/number_modifiers'
|
7
|
+
|
8
8
|
require 'rachinations/extras/fifo'
|
9
9
|
|
10
10
|
require 'rachinations/domain/diagrams/diagram'
|
11
11
|
require 'rachinations/domain/diagrams/verbose_diagram'
|
12
|
-
require 'rachinations/dsl/
|
12
|
+
require 'rachinations/dsl/diagram_shorthand_methods'
|
13
|
+
require 'rachinations/dsl/bootstrap'
|
13
14
|
require 'rachinations/domain/strategies/strategy'
|
14
15
|
require 'rachinations/domain/strategies/valid_types'
|
15
|
-
require 'rachinations/domain/edges/random_edge'
|
16
16
|
require 'rachinations/domain/edges/edge'
|
17
|
+
|
17
18
|
require 'rachinations/domain/exceptions/no_elements_of_given_type'
|
18
19
|
require 'rachinations/domain/exceptions/unsupported_type_error'
|
19
20
|
require 'rachinations/domain/exceptions/bad_options'
|
20
21
|
require 'rachinations/domain/exceptions/no_elements_matching_condition_error'
|
21
22
|
require 'rachinations/domain/exceptions/no_elements_found'
|
23
|
+
require 'rachinations/dsl/bad_dsl'
|
24
|
+
|
22
25
|
require 'rachinations/domain/nodes/node'
|
23
26
|
require 'rachinations/domain/nodes/resourceful_node'
|
24
27
|
require 'rachinations/domain/nodes/pool'
|
@@ -33,4 +36,9 @@ require 'rachinations/domain/node_collection'
|
|
33
36
|
require 'rachinations/domain/resource_bag'
|
34
37
|
|
35
38
|
|
36
|
-
|
39
|
+
# users can call .percent on integers
|
40
|
+
#using NumberModifiers
|
41
|
+
|
42
|
+
# users can use the dsl to create diagrams
|
43
|
+
include DSL::Bootstrap
|
44
|
+
using DSL::DiagramShorthandMethods
|
data/rachinations.gemspec
CHANGED
@@ -9,9 +9,9 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Felipe Almeida"]
|
10
10
|
spec.email = ["falmeida1988@gmail.com"]
|
11
11
|
spec.summary = %q{Ruby port for Dr. J. Dormans' Machinations Game Mechanics Diagrams.}
|
12
|
-
spec.description = %q{This project provides a Ruby-based
|
12
|
+
spec.description = %q{This project provides a Ruby-based DiagramShorthandMethods to enable game designers to
|
13
13
|
design and also test tentative game designs and/or prototypes}
|
14
|
-
spec.homepage = ""
|
14
|
+
spec.homepage = "https://github.com/queirozfcom/rachinations"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -30,5 +30,6 @@ design and also test tentative game designs and/or prototypes}
|
|
30
30
|
|
31
31
|
spec.add_dependency "activesupport","3.0.0"
|
32
32
|
spec.add_dependency "i18n","0.6.11"
|
33
|
+
spec.add_dependency "weighted_distribution"
|
33
34
|
|
34
35
|
end
|
@@ -2,7 +2,7 @@ require_relative '../../domain/diagrams/diagram'
|
|
2
2
|
require_relative '../../dsl/dsl'
|
3
3
|
require_relative '../../domain/nodes/pool'
|
4
4
|
require_relative '../../domain/edges/edge'
|
5
|
-
include
|
5
|
+
include DiagramShorthandMethods
|
6
6
|
|
7
7
|
n=diagram 'test_diagram', :verbose do
|
8
8
|
node 'p1', Pool, mode: :push, activation: :automatic, initial_value: 8
|
@@ -3,7 +3,7 @@ require_relative '../../dsl/dsl'
|
|
3
3
|
require_relative '../../domain/nodes/pool'
|
4
4
|
require_relative '../../domain/edges/edge'
|
5
5
|
|
6
|
-
include
|
6
|
+
include DiagramShorthandMethods
|
7
7
|
|
8
8
|
n=diagram 'test_diagram',:verbose do
|
9
9
|
node 'p1', Pool, mode: :push, activation: :automatic, initial_value: 8
|
@@ -2,7 +2,7 @@ require_relative '../../domain/diagrams/diagram'
|
|
2
2
|
require_relative '../../dsl/dsl'
|
3
3
|
require_relative '../../domain/nodes/pool'
|
4
4
|
require_relative '../../domain/edges/edge'
|
5
|
-
include
|
5
|
+
include DiagramShorthandMethods
|
6
6
|
|
7
7
|
n=diagram 'test_diagram', :verbose do
|
8
8
|
node 'p1', Pool, mode: :push, activation: :automatic, initial_value: 8
|
@@ -15,14 +15,13 @@ describe 'Nodes that can be given conditions' do
|
|
15
15
|
:initial_value => 0
|
16
16
|
}
|
17
17
|
|
18
|
-
|
19
18
|
d.add_edge! Edge, {
|
20
19
|
:name => 'connector',
|
21
20
|
:from => 'source',
|
22
21
|
:to => 'deposit'
|
23
22
|
}
|
24
23
|
|
25
|
-
d.get_node('source').attach_condition
|
24
|
+
d.get_node('source').attach_condition{ false }
|
26
25
|
|
27
26
|
d.run!(10)
|
28
27
|
|
@@ -53,7 +52,7 @@ describe 'Nodes that can be given conditions' do
|
|
53
52
|
:to => 'deposit'
|
54
53
|
}
|
55
54
|
|
56
|
-
d.get_node('source').attach_condition
|
55
|
+
d.get_node('source').attach_condition { d.get_node('deposit').resource_count < 3 }
|
57
56
|
|
58
57
|
d.run!(10)
|
59
58
|
|
@@ -82,7 +81,7 @@ describe 'Nodes that can be given conditions' do
|
|
82
81
|
:to => 'deposit'
|
83
82
|
}
|
84
83
|
|
85
|
-
d.get_node('deposit').attach_condition
|
84
|
+
d.get_node('deposit').attach_condition { d.get_node('deposit').resource_count < 3 }
|
86
85
|
|
87
86
|
d.run!(10)
|
88
87
|
|
@@ -96,7 +96,6 @@ describe Converter do
|
|
96
96
|
|
97
97
|
expect(@c).to receive(:in_conditions_met?).and_return(true)
|
98
98
|
|
99
|
-
|
100
99
|
expect(@edge_out).to receive(:test_push?).and_return(true)
|
101
100
|
expect(@edge_out).to receive(:push_expression).and_return(proc{true})
|
102
101
|
expect(@edge_out).to receive(:push!)
|
@@ -119,13 +118,13 @@ describe Converter do
|
|
119
118
|
|
120
119
|
it 'pushes and pulls if all edges (incoming and outgoing) can push and pull, respectively' do
|
121
120
|
|
122
|
-
expect(@edge_in).to receive(:test_pull?).with(true).and_return(true)
|
121
|
+
expect(@edge_in).to receive(:test_pull?).with(require_all:true).and_return(true)
|
123
122
|
expect(@edge_in).to receive(:pull!)
|
124
123
|
|
125
|
-
expect(@edge_in2).to receive(:test_pull?).with(true).and_return(true)
|
124
|
+
expect(@edge_in2).to receive(:test_pull?).with(require_all:true).and_return(true)
|
126
125
|
expect(@edge_in2).to receive(:pull!)
|
127
126
|
|
128
|
-
expect(@edge_out).to receive(:test_push?).with(true).and_return(true)
|
127
|
+
expect(@edge_out).to receive(:test_push?).with(require_all:true).and_return(true)
|
129
128
|
expect(@edge_out).to receive(:push!)
|
130
129
|
|
131
130
|
|