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