rachinations 0.0.7 → 0.0.8
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.lock +3 -1
- data/README.md +184 -5
- data/lib/rachinations.rb +5 -4
- data/lib/rachinations/domain/diagrams/diagram.rb +35 -24
- data/lib/rachinations/domain/exceptions/bad_config.rb +1 -1
- data/lib/rachinations/domain/{exceptions → modules/common}/bad_options.rb +0 -0
- data/lib/rachinations/domain/modules/common/hash_init.rb +1 -0
- data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +2 -1
- data/lib/rachinations/domain/modules/diagrams/verbose.rb +1 -1
- data/lib/rachinations/domain/nodes/gate.rb +23 -27
- data/lib/rachinations/domain/nodes/node.rb +4 -20
- data/lib/rachinations/domain/nodes/pool.rb +2 -3
- data/lib/rachinations/domain/nodes/resourceful_node.rb +4 -3
- data/lib/rachinations/domain/nodes/sink.rb +1 -1
- data/lib/rachinations/dsl/bad_dsl.rb +3 -0
- data/lib/rachinations/dsl/bootstrap.rb +10 -8
- data/lib/rachinations/dsl/diagram_shorthand_methods.rb +10 -5
- data/lib/rachinations/dsl/helpers/parser.rb +83 -49
- data/lib/rachinations/helpers/edge_helper.rb +105 -8
- data/lib/rachinations/utils/math_utils.rb +83 -0
- data/lib/rachinations/utils/string_utils.rb +8 -0
- data/lib/rachinations/version.rb +1 -1
- data/rachinations.gemspec +2 -1
- data/testing/spec/diagram_spec.rb +78 -17
- data/testing/spec/gate_spec.rb +223 -2
- data/testing/spec/non_deterministic_diagram_spec.rb +1 -1
- data/testing/spec/release1/dsl_spec.rb +100 -3
- data/testing/spec/release1/individual_cases_spec.rb +39 -0
- data/testing/spec/release1/monografia_spec.rb +69 -0
- metadata +24 -5
- data/lib/rachinations/utils/string_helper.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d453a7d9164b1dd29813252604b256fc8219f3c
|
4
|
+
data.tar.gz: 4ac7305c010e2b43d2488bceaab5aecf30f2db72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79580ad9cd32113ee03fb3ca7dbe6d6a64cd3de6f40eb98823a8a4337931c34634046f7515071b67c38c82fc66cdbd7cbf29d2e1ed4a66f7c5ffef13ec719354
|
7
|
+
data.tar.gz: e565c119969a4c534adf4b7c6843d9cea5e5e5ef5548438e58152c40c6418f1118cb862b128ec6924fa19e39d05ed4da1186a3fdbc9fa0c3aa17a38750e7f97b
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rachinations (0.0.
|
4
|
+
rachinations (0.0.7)
|
5
5
|
activesupport (= 3.0.0)
|
6
|
+
fraction (= 0.3.2)
|
6
7
|
i18n (= 0.6.11)
|
7
8
|
weighted_distribution (= 1.0.0)
|
8
9
|
|
@@ -22,6 +23,7 @@ GEM
|
|
22
23
|
docile (1.1.5)
|
23
24
|
ffi (1.9.3-x64-mingw32)
|
24
25
|
ffi (1.9.3-x86-mingw32)
|
26
|
+
fraction (0.3.2)
|
25
27
|
i18n (0.6.11)
|
26
28
|
mime-types (2.3)
|
27
29
|
minitest (5.4.0)
|
data/README.md
CHANGED
@@ -1,18 +1,197 @@
|
|
1
|
-
|
1
|
+
Rachinations
|
2
2
|
====================
|
3
3
|
[](https://travis-ci.org/queirozfcom/rachinations?branch=master)
|
4
4
|
[](https://codeclimate.com/github/queirozfcom/rachinations)
|
5
5
|
[](https://coveralls.io/r/queirozfcom/rachinations?branch=master)
|
6
6
|
[](http://badge.fury.io/rb/rachinations)
|
7
7
|
|
8
|
-
### Introduction
|
9
|
-
|
10
8
|
This is a port of Dr. J. Dormans' [Machinations framework](http://www.jorisdormans.nl/machinations/) into Ruby.
|
11
9
|
|
12
10
|
It provides a Ruby-based DSL to enable game designers to create and also test tentative game designs and/or prototypes.
|
13
11
|
|
14
|
-
|
12
|
+
## Contents
|
15
13
|
|
16
14
|
- Classes to model the domain
|
17
15
|
- Tests
|
18
|
-
- A simple DSL (Domain-specific language) whose objective is to enable anyone to write Machinations diagrams, run them, obtain metrics, compose subdiagrams and so on.
|
16
|
+
- A simple DSL (Domain-specific language) whose objective is to enable anyone to write Machinations diagrams, run them, obtain metrics, compose subdiagrams and so on.
|
17
|
+
|
18
|
+
## Installation Guide
|
19
|
+
|
20
|
+
Rachinations is written in Ruby so you need to have Ruby installed on your system. You only need 5 minutes to get it to work:
|
21
|
+
|
22
|
+
**Linux**
|
23
|
+
|
24
|
+
The best way to install Ruby on a Linux-based machine is probably RVM. [Instructions on how to install RVM on Linux here](http://queirozf.com/entries/tutorial-and-examples-on-how-to-use-rvm-on-linux)
|
25
|
+
|
26
|
+
Once Ruby is installed, you just need to install the rachinations **gem**. The process is straightforward:
|
27
|
+
|
28
|
+
```
|
29
|
+
$ gem install rachinations
|
30
|
+
```
|
31
|
+
|
32
|
+
**Windows**
|
33
|
+
|
34
|
+
- **Installation**
|
35
|
+
|
36
|
+
On Windows, the best way to get up and running with Ruby is probably using the [RubyInstaller for Windows](http://rubyinstaller.org/)
|
37
|
+
|
38
|
+
Please note that Rachinations requires at least Ruby **version 2.1** to work.
|
39
|
+
|
40
|
+
If you have never used Ruby before, I recommend you tick the following two boxes, as per the following image:
|
41
|
+
|
42
|
+

|
43
|
+
|
44
|
+
- **Veryfing that the installation worked**
|
45
|
+
|
46
|
+
Once Ruby is installed, open a **command prompt** and type `ruby -v` just to see if everything worked.
|
47
|
+
|
48
|
+
You should see something like this (details may vary slightly)
|
49
|
+
|
50
|
+
```
|
51
|
+
> ruby -v
|
52
|
+
ruby 2.1.5p273 (2014-11-13 revision 48405) [x64-mingw32]
|
53
|
+
```
|
54
|
+
|
55
|
+
- **Configuring rubygems and installing the library**
|
56
|
+
|
57
|
+
Once that's done, we'll configure `gem` (Ruby's package manager) to address a well known problem that has to do with certificates on Windows. More info [here](http://stackoverflow.com/questions/9962051/could-not-find-a-valid-gem-in-any-repository-rubygame-and-others) and [here](http://help.rubygems.org/discussions/problems/19761-could-not-find-a-valid-gem).
|
58
|
+
|
59
|
+
On the command prompt, do this:
|
60
|
+
|
61
|
+
```
|
62
|
+
> gem sources -r https://rubygems.org
|
63
|
+
```
|
64
|
+
and
|
65
|
+
|
66
|
+
```
|
67
|
+
> gem sources -a http://rubygems.org
|
68
|
+
https://rubygems.org is recommended for security
|
69
|
+
|
70
|
+
Do you want to add this insecure source? [yn] y
|
71
|
+
http://rubygems.org added to sources
|
72
|
+
```
|
73
|
+
|
74
|
+
After you've done the last step (which adds a new source for gems to be fetched from), then you can install the gem proper:
|
75
|
+
|
76
|
+
```
|
77
|
+
> gem install rachinations
|
78
|
+
```
|
79
|
+
(you might see a few error messages, but don't worry)
|
80
|
+
|
81
|
+
## Usage
|
82
|
+
|
83
|
+
All you need to do is write your diagram in a file whose name ends in `.rb` and run it using the `ruby` command.
|
84
|
+
|
85
|
+
### Examples
|
86
|
+
|
87
|
+
- **Simplest possible example**
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
require 'rachinations'
|
91
|
+
|
92
|
+
# this is a simple diagram with a single pool with
|
93
|
+
# 5 resources
|
94
|
+
d=diagram 'simplest_diagram' do
|
95
|
+
pool initial_value: 5
|
96
|
+
end
|
97
|
+
|
98
|
+
# and execute it for 10 rounds
|
99
|
+
d.run 10
|
100
|
+
```
|
101
|
+
|
102
|
+
Save this code into a file (say, `static_diagram.rb`) and run it like this:
|
103
|
+
|
104
|
+
```
|
105
|
+
$ ruby static_diagram.rb
|
106
|
+
```
|
107
|
+
|
108
|
+
- **Example 1**
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
require 'rachinations'
|
112
|
+
|
113
|
+
diagram 'example_1' do
|
114
|
+
source 's1', :automatic
|
115
|
+
pool 'p1'
|
116
|
+
pool 'p2', :automatic
|
117
|
+
edge from: 's1', to: 'p1'
|
118
|
+
edge from: 'p1', to: 'p2'
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
- **Example 2**
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
require 'rachinations'
|
126
|
+
|
127
|
+
diagram 'example_2' do
|
128
|
+
source 's1'
|
129
|
+
pool 'p1'
|
130
|
+
converter 'c1', :automatic
|
131
|
+
pool 'p2'
|
132
|
+
pool 'p3'
|
133
|
+
edge from: 's1', to: 'p1'
|
134
|
+
edge from: 'p1', to: 'c1'
|
135
|
+
edge from: 'c1', to: 'p2'
|
136
|
+
edge from: 'c1', to: 'p3'
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
- **Example 3**
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
require 'rachinations'
|
144
|
+
|
145
|
+
diagram 'example_3' do
|
146
|
+
source 's1'
|
147
|
+
gate 'g1', :probabilistic
|
148
|
+
pool 'p1'
|
149
|
+
pool 'p2'
|
150
|
+
pool 'p3'
|
151
|
+
sink 's2', :automatic, condition: expr{ p2.resource_count > 30 }
|
152
|
+
edge from: 's1', to: 'g1'
|
153
|
+
edge from: 'g1', to: 'p1'
|
154
|
+
edge 2, from: 'g1', to: 'p2'
|
155
|
+
edge from: 'g1', to: 'p3'
|
156
|
+
edge from: 'p3', to: 's2'
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
- **Example 4**
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
require 'rachinations'
|
164
|
+
|
165
|
+
diagram 'example_4' do
|
166
|
+
source 's1'
|
167
|
+
pool 'p1', triggers: 's2'
|
168
|
+
source 's2', :passive
|
169
|
+
pool 'p2'
|
170
|
+
edge from: 's1', to: 'p1'
|
171
|
+
edge from: 's2', to: 'p2'
|
172
|
+
end
|
173
|
+
```
|
174
|
+
|
175
|
+
- **Example 4, alternative version**
|
176
|
+
|
177
|
+
This amounts to the same diagram as the one defined in Example 4, but uses a different mechanism for defining triggers between nodes.
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
require 'rachinations'
|
181
|
+
|
182
|
+
diagram 'example_4_alternative' do
|
183
|
+
source 's1'
|
184
|
+
pool 'p1'
|
185
|
+
source 's2', :passive, triggered_by: 'p1'
|
186
|
+
pool 'p2'
|
187
|
+
edge from: 's1', to: 'p1'
|
188
|
+
edge from: 's2', to: 'p2'
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
### Full DSL specification
|
193
|
+
**TODO**
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
|
data/lib/rachinations.rb
CHANGED
@@ -2,15 +2,18 @@ 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
|
+
require 'rachinations/utils/math_utils'
|
6
|
+
require 'rachinations/utils/string_utils'
|
7
|
+
|
5
8
|
require 'rachinations/domain/modules/common/refiners/proc_convenience_methods'
|
6
9
|
require 'rachinations/domain/modules/common/refiners/number_modifiers'
|
7
10
|
require 'rachinations/domain/modules/common/schedulable_tasks'
|
8
11
|
|
9
|
-
|
10
12
|
require 'rachinations/extras/fifo'
|
11
13
|
|
12
14
|
require 'rachinations/domain/diagrams/diagram'
|
13
15
|
require 'rachinations/domain/diagrams/verbose_diagram'
|
16
|
+
require 'rachinations/domain/diagrams/non_deterministic_diagram'
|
14
17
|
require 'rachinations/dsl/diagram_shorthand_methods'
|
15
18
|
require 'rachinations/dsl/bootstrap'
|
16
19
|
require 'rachinations/domain/strategies/strategy'
|
@@ -19,10 +22,9 @@ require 'rachinations/domain/edges/edge'
|
|
19
22
|
|
20
23
|
require 'rachinations/domain/exceptions/no_elements_of_given_type'
|
21
24
|
require 'rachinations/domain/exceptions/unsupported_type_error'
|
22
|
-
require 'rachinations/domain/exceptions/bad_options'
|
23
|
-
require 'rachinations/domain/exceptions/bad_config'
|
24
25
|
require 'rachinations/domain/exceptions/no_elements_matching_condition_error'
|
25
26
|
require 'rachinations/domain/exceptions/no_elements_found'
|
27
|
+
require 'rachinations/domain/exceptions/bad_config'
|
26
28
|
require 'rachinations/dsl/bad_dsl'
|
27
29
|
|
28
30
|
require 'rachinations/domain/nodes/node'
|
@@ -38,7 +40,6 @@ require 'rachinations/domain/edge_collection'
|
|
38
40
|
require 'rachinations/domain/node_collection'
|
39
41
|
require 'rachinations/domain/resource_bag'
|
40
42
|
|
41
|
-
|
42
43
|
# users can use the dsl to create diagrams
|
43
44
|
include DSL::Bootstrap
|
44
45
|
|
@@ -15,9 +15,9 @@ class Diagram
|
|
15
15
|
@edges = EdgeCollection.new
|
16
16
|
@name = name
|
17
17
|
@max_iterations = 999
|
18
|
+
@stop_conditions = []
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
21
|
def get_node(name)
|
22
22
|
|
23
23
|
nodes.each do |node|
|
@@ -96,10 +96,23 @@ class Diagram
|
|
96
96
|
self
|
97
97
|
end
|
98
98
|
|
99
|
-
def
|
99
|
+
def add_stop_condition!(message: nil, condition:)
|
100
|
+
raise ArgumentError, 'Expression required for stop condition' unless condition.is_a? Proc
|
101
|
+
raise ArgumentError, 'Message can be omitted only if there is no other condition' if message.nil? && !(@stop_conditions.empty?)
|
102
|
+
|
103
|
+
hsh = {
|
104
|
+
message: message,
|
105
|
+
condition: condition
|
106
|
+
}
|
107
|
+
|
108
|
+
@stop_conditions << hsh
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
def run!(rounds = max_iterations)
|
100
113
|
|
101
114
|
run_while! do |i|
|
102
|
-
i<=rounds
|
115
|
+
i <= rounds && no_stop_conditions_met?
|
103
116
|
end
|
104
117
|
|
105
118
|
end
|
@@ -115,10 +128,9 @@ class Diagram
|
|
115
128
|
|
116
129
|
i=1
|
117
130
|
|
118
|
-
# if given condition block turned false, it's time to stop
|
119
131
|
while yield i do
|
120
132
|
|
121
|
-
break
|
133
|
+
break if maximum_round? i
|
122
134
|
|
123
135
|
before_round i
|
124
136
|
|
@@ -139,7 +151,6 @@ class Diagram
|
|
139
151
|
|
140
152
|
end
|
141
153
|
|
142
|
-
|
143
154
|
def resource_count(klass=nil)
|
144
155
|
total=0
|
145
156
|
@nodes.each do |n|
|
@@ -154,40 +165,40 @@ class Diagram
|
|
154
165
|
nodes.reduce('') { |carry, n| carry+n.to_s }
|
155
166
|
end
|
156
167
|
|
157
|
-
def
|
158
|
-
|
159
|
-
|
160
|
-
false
|
161
|
-
else
|
162
|
-
true
|
163
|
-
end
|
168
|
+
def no_stop_conditions_met?
|
169
|
+
@stop_conditions
|
170
|
+
.all? { |el| ! el[:condition].call }
|
164
171
|
end
|
165
172
|
|
166
|
-
def
|
173
|
+
def maximum_round?(round_no)
|
174
|
+
round_no > @max_iterations
|
175
|
+
end
|
167
176
|
|
168
|
-
|
177
|
+
def run_first_round!
|
178
|
+
enabled_nodes
|
179
|
+
.select { |node| node.automatic? || node.start? }
|
180
|
+
.shuffle
|
181
|
+
.each { |node| node.trigger! }
|
169
182
|
|
170
183
|
commit_nodes!
|
171
|
-
|
172
184
|
end
|
173
185
|
|
174
186
|
def run_round!
|
175
|
-
|
176
|
-
|
187
|
+
enabled_nodes
|
188
|
+
.select { |node| node.automatic? }
|
189
|
+
.shuffle
|
190
|
+
.each { |node| node.trigger! }
|
177
191
|
|
178
192
|
commit_nodes!
|
179
|
-
|
180
193
|
end
|
181
194
|
|
182
195
|
def commit_nodes!
|
183
196
|
#only after all nodes have run do we update the actual resources and changes, to be used in the next round.
|
184
197
|
nodes.shuffle.each { |node| node.commit! }
|
185
|
-
|
186
198
|
end
|
187
199
|
|
188
200
|
def enabled_nodes
|
189
201
|
nodes.select { |node| node.enabled? }
|
190
|
-
|
191
202
|
end
|
192
203
|
|
193
204
|
#template method
|
@@ -199,15 +210,15 @@ class Diagram
|
|
199
210
|
end
|
200
211
|
|
201
212
|
#template method
|
202
|
-
def before_run
|
213
|
+
def before_run
|
203
214
|
end
|
204
215
|
|
205
216
|
#template method
|
206
|
-
def after_run
|
217
|
+
def after_run
|
207
218
|
end
|
208
219
|
|
209
220
|
#template method
|
210
|
-
def sanity_check_message
|
221
|
+
def sanity_check_message
|
211
222
|
end
|
212
223
|
|
213
224
|
end
|
File without changes
|
@@ -2,9 +2,13 @@ require_relative '../../domain/nodes/node'
|
|
2
2
|
require_relative '../../domain/nodes/resourceless_node'
|
3
3
|
|
4
4
|
require 'weighted_distribution'
|
5
|
+
require 'fraction'
|
5
6
|
|
6
7
|
class Gate < ResourcelessNode
|
7
8
|
|
9
|
+
EdgeHelper = Helpers::EdgeHelper
|
10
|
+
|
11
|
+
attr_reader :mode, :activation
|
8
12
|
|
9
13
|
def initialize(hsh={})
|
10
14
|
check_options!(hsh)
|
@@ -13,55 +17,47 @@ class Gate < ResourcelessNode
|
|
13
17
|
@diagram = params[:diagram]
|
14
18
|
@name = params[:name]
|
15
19
|
@activation = params[:activation]
|
20
|
+
# for gates, :mode has different semantics
|
16
21
|
@mode = params[:mode]
|
17
22
|
@types = get_types(given_types: params[:types])
|
18
23
|
|
19
24
|
super(hsh)
|
20
25
|
end
|
21
26
|
|
22
|
-
def put_resource!(res,
|
27
|
+
def put_resource!(res, from_edge)
|
23
28
|
|
24
|
-
|
25
|
-
|
29
|
+
raise BadConfig.new('All outgoing Edges must be of the same kind') unless EdgeHelper.all_labels_of_same_kind?(outgoing_edges)
|
30
|
+
raise BadConfig.new('If probabilities are given, they must add up to 1') unless EdgeHelper.labels_valid?(outgoing_edges)
|
26
31
|
|
27
|
-
maybe_edge = pick_one(outgoing_edges)
|
32
|
+
maybe_edge = EdgeHelper.pick_one(edges: outgoing_edges, mode: mode, index: next_edge_index)
|
28
33
|
|
29
|
-
if(maybe_edge.nil?)
|
30
|
-
#
|
34
|
+
if (maybe_edge.nil?)
|
35
|
+
# no outgoing edges. resource disappears
|
31
36
|
else
|
32
37
|
maybe_edge.push!(res)
|
33
38
|
end
|
34
39
|
|
35
40
|
end
|
36
41
|
|
42
|
+
def take_resource!(res, edge)
|
43
|
+
# no action
|
44
|
+
end
|
45
|
+
|
37
46
|
def to_s
|
38
47
|
"Gate '#{@name}'\n\n"
|
39
48
|
end
|
40
49
|
|
41
50
|
private
|
42
51
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
elsif(edges.all?{|e| e.label.class == Float})
|
49
|
-
#{edge=>weight} is the shape required by WeightedRandomizer
|
50
|
-
weights = edges.reduce(Hash.new){|acc,el| acc[el] = el.label; acc }
|
51
|
-
|
52
|
-
sum = edges.reduce(0){|acc,el|acc + el.label }
|
52
|
+
# used to indicate what edge index
|
53
|
+
# when in :deterministic mode
|
54
|
+
def next_edge_index
|
55
|
+
# starting at zero
|
56
|
+
@next_edge_index ||= 0
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
#resource 'vanishes'
|
57
|
-
weights[nil] = remaining
|
58
|
-
|
59
|
-
edge_distribution = WeightedDistribution.new(weights)
|
60
|
-
edge_distribution.sample
|
61
|
-
else
|
62
|
-
raise RuntimeError.new('Invalid setup')
|
63
|
-
end
|
58
|
+
@next_edge_index += 1
|
64
59
|
|
60
|
+
(@next_edge_index - 1)
|
65
61
|
end
|
66
62
|
|
67
63
|
def options
|
@@ -75,7 +71,7 @@ class Gate < ResourcelessNode
|
|
75
71
|
def defaults
|
76
72
|
{
|
77
73
|
activation: :passive,
|
78
|
-
mode: :
|
74
|
+
mode: :deterministic,
|
79
75
|
types: []
|
80
76
|
}
|
81
77
|
end
|